Mysql

如何對來自相同兩個表但來自兩個不同結果集和/或條件的相同列進行分組

  • December 30, 2018

我在想出一個應該很簡單的查詢時遇到了麻煩。

最初,我有兩個數據表,可以從中生成聯合結果集;一個事件表和一個表(想想社區或組織)。我將使用查詢來獲取事件數據,例如事件名稱、ID、託管事件的組(僅限單個 ID)等。每個事件和組都有自己唯一的 ID 號,由 AUTO_INCREMENT 給出。我會使用以下查詢:

SELECT g.group_id,
      g.name,
      prev_id,
      activity_state+0,
      state,
      first_event_id,
      last_event_id,
      GROUP_CONCAT(e.event_id ORDER BY start_date_time SEPARATOR ','),
      GROUP_CONCAT(e.name ORDER BY start_date_time SEPARATOR '__'),
      GROUP_CONCAT(start_date_time ORDER BY start_date_time SEPARATOR ',') AS first_dates,
      GROUP_CONCAT(end_date_time ORDER BY start_date_time SEPARATOR ',') 
FROM events e 
INNER JOIN groups g ON e.group_id=g.group_id 
WHERE start_date_time BETWEEN ? AND ? 
GROUP BY g.name 
ORDER BY first_dates

兩個問號代表準備好的語句中的兩個綁定日期和時間。這個查詢可以很好地工作。

但是,我最近更新了數據庫的結構以包含所謂的“聯合事件”,其中多個組一起舉辦相同的事件。我添加了一個joint_event表,它只包含一個event_id 列和一個group_id 列。相同的事件 ID 在表中出現多次(至少兩次),用於指示哪些組主持了特定的事件。聯合事件也被添加到事件表中,但其 ID 為0(零)。

我現在需要一個與上述功能相似的查詢。查詢必須包括來自事件表和組表連接條件的初始結果集,以及與聯合事件表中相同條件匹配的任何联合事件的發生。它們必須適當地分組在一起,這樣我就不會得到相同數據的重複(每個組應該出現在自己的行中,並且每組只有一行)。

另外,SQL-89 語法中的連接條件如下:

events.group_id = groups.group_id

(給出大部分結果,至少 98%,加上……)

events.event_id = joint_events.event_id AND joint_events.group_id = groups.group_id

(聯合活動)

我曾嘗試使用 UNION,但它似乎不支持按返回的整個數據集進行分組。我曾嘗試使用子查詢,但在使用別名來描述兩個單獨數據結果集中的相同列時遇到問題,因為它通常會產生“不明確”的列名錯誤。使用 DISTINCT 不起作用,因為它要麼刪除彼此具有完全相同名稱的事件(這些需要保留),要麼在未應用於需要分組的所有四個列時在分組列中創建不一致. 我最接近的是一個查詢,它導致聯合事件數據與其他數據分開,而不是被分組到其他數據中;或數據被包含,但事件數據被複製。

我不能在任何地方使用 NULL,或者使用兩個完全獨立的查詢,否則我將不得不重構我的 PHP 腳本,並且可能會使使用 PHP 對數據進行排序的操作效率低下。

提前致謝。

我解決了。該解決方案側重於開頭的組 ID、事件 ID 和 start_date_time 欄位,這些欄位由相關條件過濾並放入派生表中。然後圍繞它放置一個外部查詢,負責收集所有必需的欄位,將它們分組在一起,並按派生的“first_dates”欄位排序。該查詢以 SQL-89 語法顯示在下面(同樣,問號表示準備好的語句格式的綁定日期):

SELECT g.group_id,
      g.name,
      prev_id, 
      activity_state+0,
      state,
      first_event_id,
      last_event_id,
      GROUP_CONCAT(e.event_id ORDER BY e.start_date_time),
      GROUP_CONCAT(e.name ORDER BY e.start_date_time SEPARATOR '__'),
      GROUP_CONCAT(e.start_date_time ORDER BY e.start_date_time) AS first_dates,
      GROUP_CONCAT(end_date_time ORDER BY e.start_date_time)
FROM events e, groups g, (SELECT DISTINCT g.group_id,
                                         e.event_id,
                                         start_date_time
                         FROM events e, groups g, joint_events j
                         WHERE (e.group_id=g.group_id OR (e.event_id=j.event_id AND j.group_id=g.group_id)) AND start_date_time BETWEEN ? AND ?) t 
WHERE g.group_id=t.group_id AND e.event_id=t.event_id
GROUP BY g.group_id
ORDER BY first_dates

請記住,這樣的查詢是這樣操作的:

  1. JOIN所有表一起,生成一個“大”臨時表,其中包含所有未被過濾掉的組合。
  2. 然後做聚合(SUM,,GROUP_CONCAT等)

一個常見的問題是由於所有的組合SUMsCOUNTs被誇大了。在您的情況下,GROUP_CONCAT可能有重複;這可以通過說來處理GROUP_CONCAT(DISTINCT ...)

一個常見的解決方案是專注於聚合,使用最少的JOINs來獲得正確的聚合,然後 JOIN關注其餘的表。

有時可行的另一種解決方案是將 a 替換為計算“查找”值JOIN的子查詢(在部分中)(例如將 an 映射到 a )。SELECT``id``name

您必須使用舊版本的 MySQL;新版本不喜歡你GROUP BY one-column在列出SELECT.

對不起,這只是“揮手”,但您的問題也缺少細節,例如“我添加時…”的詳細資訊SHOW CREATE TABLE,指定每列屬於哪個表等。

引用自:https://dba.stackexchange.com/questions/225968