2017-03-02 53 views
13

我創建了一個查詢,返回我想要的結果,但我覺得必須有更好的方法來做到這一點。任何指導將不勝感激。可以優化查詢:獲取記錄的最大日期,然後加入最大日期的值

我想要獲取特定會議的所有項目,並加入最大會議日期< X並加入最大日期委員會首字母縮略詞。 X是當前會議日期。

我已經嘗試了幾個不同的查詢,但沒有一個,除了下面的那個之外,都一直返回預期的結果。

您可以通過轉至rextester來查看此查詢。

DROP TABLE IF EXISTS `committees`; 
CREATE TABLE committees 
    (`id` int, `acronym` varchar(4)) 
; 

INSERT INTO committees 
    (`id`, `acronym`) 
VALUES 
    (1, 'Com1'), 
    (2, 'Com2'), 
    (3, 'Com3') 
; 

DROP TABLE IF EXISTS `meetings`; 
CREATE TABLE meetings 
    (`id` int, `date` datetime, `committee_id` int) 
; 

INSERT INTO meetings 
    (`id`, `date`, `committee_id`) 
VALUES 
    (1, '2017-01-01 00:00:00', 1), 
    (2, '2017-02-02 00:00:00', 2), 
    (3, '2017-03-03 00:00:00', 2) 
; 

DROP TABLE IF EXISTS `agenda_items`; 
CREATE TABLE agenda_items 
    (`id` int, `name` varchar(6)) 
; 

INSERT INTO agenda_items 
    (`id`, `name`) 
VALUES 
    (1, 'Item 1'), 
    (2, 'Item 2'), 
    (3, 'Item 3') 
; 

DROP TABLE IF EXISTS `join_agenda_items_meetings`; 
CREATE TABLE join_agenda_items_meetings 
    (`id` int, `agenda_item_id` int, `meeting_id` int) 
; 

INSERT INTO join_agenda_items_meetings 
    (`id`, `agenda_item_id`, `meeting_id`) 
VALUES 
    (1, 1, 1), 
    (2, 1, 2), 
    (3, 2, 1), 
    (4, 3, 2), 
    (5, 2, 1), 
    (6, 1, 3) 
; 




SELECT agenda_items.id, 
     meetings.id, 
     meetings.date, 
     sub_one.max_date, 
     sub_two.acronym 
FROM agenda_items 
     LEFT JOIN (SELECT ai.id    AS ai_id, 
         me.id    AS me_id, 
         Max(me.date) AS max_date 
        FROM agenda_items AS ai 
         JOIN join_agenda_items_meetings AS jaim 
          ON jaim.agenda_item_id = ai.id 
         JOIN meetings AS me 
          ON me.id = jaim.meeting_id 
        WHERE me.date < '2017-02-02' 
        GROUP BY ai_id) sub_one 
       ON sub_one.ai_id = agenda_items.id 
     LEFT JOIN (SELECT agenda_items.id  AS age_id, 
         meetings.date AS meet_date, 
         committees.acronym AS acronym 
        FROM agenda_items 
         JOIN join_agenda_items_meetings 
          ON join_agenda_items_meetings.agenda_item_id = agenda_items.id 
         JOIN meetings 
          ON meetings.id = join_agenda_items_meetings.meeting_id 
         JOIN committees 
          ON committees.id = meetings.committee_id 
        WHERE meetings.date) sub_two 
       ON sub_two.age_id = agenda_items.id 
       AND sub_one.max_date = sub_two.meet_date 
     JOIN join_agenda_items_meetings 
     ON agenda_items.id = join_agenda_items_meetings.agenda_item_id 
     JOIN meetings 
     ON meetings.id = join_agenda_items_meetings.meeting_id 
WHERE meetings.id = 2; 

審查/測試答案(修訂):*

我修改後的基礎上提出的意見進行測試。

既然我在這個問題上給了一個賞金,我覺得我應該展示我如何評估答案並給出一些反饋。總的來說,我非常感謝所有幫助過的人,謝謝。

爲了測試,我回顧了查詢反對:

我使用EXPLAIN的原始查詢

+----+-------------+---------------------------+------+----------------------------------------------+ 
| id | select_type | table      | rows | Extra          | 
+----+-------------+---------------------------+------+----------------------------------------------+ 
| 1 | PRIMARY  | meetings     | 1 |            | 
| 1 | PRIMARY  | join_agenda_item_meetings | 1976 | Using where; Using index      | 
| 1 | PRIMARY  | agenda_items    | 1 | Using index         | 
| 1 | PRIMARY  | <derived2>    | 1087 |            | 
| 1 | PRIMARY  | <derived3>    | 2202 |            | 
| 3 | DERIVED  | join_agenda_item_meetings | 1976 | Using index         | 
| 3 | DERIVED  | meetings     | 1 | Using where         | 
| 3 | DERIVED  | committees    | 1 |            | 
| 3 | DERIVED  | agenda_items    | 1 | Using index         | 
| 2 | DERIVED  | jaim      | 1976 | Using index; Using temporary; Using filesort | 
| 2 | DERIVED  | me      | 1 | Using where         | 
| 2 | DERIVED  | ai      | 1 | Using index         | 
+----+-------------+---------------------------+------+----------------------------------------------+ 
12 rows in set (0.02 sec) 

Paul Spiegel的回答。

最初的答案的作品,似乎是最有效的選項介紹,遠遠超過我的。

Paul Spiegel的第一個查詢拉動了最少的行,比我的更短,更具可讀性。它也不需要引用一個更好的日期。

+----+--------------------+-------+------+--------------------------+ 
| id | select_type  | table | rows | Extra     | 
+----+--------------------+-------+------+--------------------------+ 
| 1 | PRIMARY   | m1 | 1 |       | 
| 1 | PRIMARY   | am1 | 1976 | Using where; Using index | 
| 1 | PRIMARY   | am2 | 1 | Using index    | 
| 1 | PRIMARY   | m2 | 1 |       | 
| 2 | DEPENDENT SUBQUERY | am3 | 1 | Using index    | 
| 2 | DEPENDENT SUBQUERY | m3 | 1 | Using where    | 
| 2 | DEPENDENT SUBQUERY | c3 | 1 | Using where    | 
+----+--------------------+-------+------+--------------------------+ 
7 rows in set (0.00 sec) 

此查詢在將DISTINCT添加到select語句時也返回正確的結果。這個查詢不如第一個那樣好(但它很接近)。

+----+-------------+------------++------+-------------------------+ 
| id | select_type | table  | rows | Extra     | 
+----+-------------+------------++------+-------------------------+ 
| 1 | PRIMARY  | <derived2> | 5 | Using temporary   | 
| 1 | PRIMARY  | am   | 1 | Using index    | 
| 1 | PRIMARY  | m   | 1 |       | 
| 1 | PRIMARY  | c   | 1 | Using where    | 
| 2 | DERIVED  | m1   | 1 |       | 
| 2 | DERIVED  | am1  | 1787 | Using where; Using index | 
| 2 | DERIVED  | am2  | 1 | Using index    | 
| 2 | DERIVED  | m2   | 1 |       | 
+----+-------------+------------+------+--------------------------+ 
8 rows in set (0.00 sec) 

斯特凡諾賈尼尼的回答

這個查詢不會返回使用DISTINCT預期的結果。當使用EXPLAIN和拉動的行數時,與我原來的查詢相比,此查詢效率更高,但Paul Spiegel的效果稍好一些。

+----+-------------+------------+------+---------------------------------+ 
| id | select_type | table  | rows | Extra       | 
+----+-------------+------------+------+---------------------------------+ 
| 1 | PRIMARY  | me   | 1 | Using temporary; Using filesort | 
| 1 | PRIMARY  | rel  | 1787 | Using where; Using index  | 
| 1 | PRIMARY  | <derived2> | 1087 |         | 
| 1 | PRIMARY  | rel2  | 1 | Using index      | 
| 1 | PRIMARY  | me2  | 1 | Using where      | 
| 1 | PRIMARY  | co   | 1 |         | 
| 2 | DERIVED  | t1   | 1787 | Using index      | 
| 2 | DERIVED  | t2   | 1 | Using where      | 
+----+-------------+------------+------+---------------------------------+ 
8 rows in set (0.00 sec) 

EoinS的回答

正如在評論中指出,這個答案工作,如果會議是連續的,但它們可能不是很可惜。

+0

見http://meta.stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-me-to-一個非常簡單的sql查詢 – Strawberry

+0

很快就會添加sqlfiddle。謝謝 – user3366016

+0

@Strawberry,sqlfiddle保持凍結,但我添加了查詢的創建和插入語句以在rextester中運行。 – user3366016

回答

5

這個人是有點瘋了..讓我們做一步一步來:

的第一步是基本的加入

set @meeting_id = 2; 

select am1.meeting_id, 
     am1.agenda_item_id, 
     m1.date as meeting_date 
from meetings m1 
join join_agenda_items_meetings am1 on am1.meeting_id = m1.id 
where m1.id = @meeting_id; 

我們選擇會議(ID = 2)相應的agenda_item_ids。這將已經返回我們需要的前三列的行。

下一步是獲取每個議程項目的最後一次會議日期。我們需要將第一個查詢與連接表和相應的會議(id = 2 - am2.meeting_id <> am1.meeting_id之一除外)進行連接。我們只想在會議開始前約會(m2.date < m1.date)。從所有這些會議中,我們只需要每個議程項目的最新日期。因此,我們通過的議程項目組,並選擇max(m2.date)

select am1.meeting_id, 
     am1.agenda_item_id, 
     m1.date as meeting_date, 
     max(m2.date) as max_date 
from meetings m1 
join join_agenda_items_meetings am1 on am1.meeting_id = m1.id 
left join join_agenda_items_meetings am2 
    on am2.agenda_item_id = am1.agenda_item_id 
    and am2.meeting_id <> am1.meeting_id 
left join meetings m2 
    on m2.id = am2.meeting_id 
    and m2.date < m1.date 
where m1.id = @meeting_id 
group by m1.id, am1.agenda_item_id; 

這樣,我們得到的第四列(max_date)。

最後一步是選擇會議的最後日期(max_date)的acronym。這是瘋狂的部分 - 我們可以在SELECT子句中使用相關的子查詢。我們可以用max(m2.date)對相關:

select c3.acronym 
from meetings m3 
join join_agenda_items_meetings am3 on am3.meeting_id = m3.id 
join committees c3 on c3.id = m3.committee_id 
where am3.agenda_item_id = am2.agenda_item_id 
    and m3.date = max(m2.date) 

最後的查詢將是:

select am1.meeting_id, 
     am1.agenda_item_id, 
     m1.date as meeting_date, 
     max(m2.date) as max_date, 
     ( select c3.acronym 
      from meetings m3 
      join join_agenda_items_meetings am3 on am3.meeting_id = m3.id 
      join committees c3 on c3.id = m3.committee_id 
      where am3.agenda_item_id = am2.agenda_item_id 
      and m3.date = max(m2.date) 
     ) as acronym 
from meetings m1 
join join_agenda_items_meetings am1 on am1.meeting_id = m1.id 
left join join_agenda_items_meetings am2 
    on am2.agenda_item_id = am1.agenda_item_id 
    and am2.meeting_id <> am1.meeting_id 
left join meetings m2 
    on m2.id = am2.meeting_id 
    and m2.date < m1.date 
where m1.id = @meeting_id 
group by m1.id, am1.agenda_item_id; 

http://rextester.com/JKK60222

是真的,我感到驚訝的是,你可以在子查詢中使用max(m2.date)

另一種解決方案 - 在子查詢(派生表)中使用第二個查詢。使用max_date加入會議和加入表的委員會。只保留具有首字母縮寫詞和行的行,而不要使用max_date

select t.*, c.acronym 
from (
    select am1.meeting_id, 
      am1.agenda_item_id, 
      m1.date as meeting_date, 
      max(m2.date) as max_date 
    from meetings m1 
    join join_agenda_items_meetings am1 on am1.meeting_id = m1.id 
    left join join_agenda_items_meetings am2 
     on am2.agenda_item_id = am1.agenda_item_id 
     and am2.meeting_id <> am1.meeting_id 
    left join meetings m2 
     on m2.id = am2.meeting_id 
     and m2.date < m1.date 
    where m1.id = @meeting_id 
    group by m1.id, am1.agenda_item_id 
) t 
left join join_agenda_items_meetings am 
    on am.agenda_item_id = t.agenda_item_id 
    and t.max_date is not null 
left join meetings m 
    on m.id = am.meeting_id 
    and m.date = t.max_date 
left join committees c on c.id = m.committee_id 
where t.max_date is null or c.acronym is not null; 

http://rextester.com/BBMDFL23101

+1

請注意,此查詢不使用硬編碼日期('2017-02-02')。 –

+0

@stefanoz等人我已經用一些測試信息更新了我的答案。 – user3366016

+1

@ user3366016您可以對我的第二個查詢使用'SELECT DISTINCT',並使用Stefanos answer來刪除重複的行。 –

3

使用您的模式我用下面的查詢,假設所有meetings條目順序:

set @mymeeting = 2; 

select j.agenda_item_id, m.id, m.date, mp.date, c.acronym 
from meetings m 
left join join_agenda_items_meetings j on j.meeting_id = m.id 
left join join_agenda_items_meetings jp on jp.meeting_id = m.id -1 and jp.agenda_item_id = j.agenda_item_id 
left join meetings mp on mp.id = jp.meeting_id 
left join committees c on mp.committee_id = c.id 
where m.id = @mymeeting; 

我創建了一個變量只是爲了便於動態更改會議。

Here is a functional example in Rextester

感謝,讓你的模式很容易複製!

+0

謝謝你,我不能夠測試它,但它看起來更乾淨。會議可能會按順序進行,但不會保證。例如,可能會在下週創建一個會議,然後可能會爲明天創建一個會議。兩者都可能與一個議程項目相關。 – user3366016

+0

如果會議始終是連續的('meetings.id - 1'),但不會在會議創建/亂序更新(請參閱上面的某些詳細信息註釋)後才能發揮作用。謝謝。 – user3366016

3

我發現這個問題很有挑戰性,我所取得的結果並不令人瞠目結舌,但我設法擺脫子查詢中的一個,也許幾個連接,這就是結果:

select distinct me.ID, me.DATE, rel.AGENDA_ITEM_ID, sub.MAX_DATE, co.ACRONYM 
from  MEETINGS me 
join  JOIN_AGENDA_ITEMS_MEETINGS rel /* Note 1*/ 
    on  me.ID = rel.MEETING_ID 
left join ( 
       select t1.AGENDA_ITEM_ID, max(t2.DATE) MAX_DATE 
       from JOIN_AGENDA_ITEMS_MEETINGS t1 
       join MEETINGS t2 
       on t2.ID = t1.MEETING_ID 
       where t2.DATE < '2017-02-02' 
       group by t1.AGENDA_ITEM_ID 
     ) sub 
    on  rel.AGENDA_ITEM_ID = sub.AGENDA_ITEM_ID /* Note 2 */ 
left join JOIN_AGENDA_ITEMS_MEETINGS rel2 
    on  rel2.AGENDA_ITEM_ID = rel.AGENDA_ITEM_ID /* Note 3 */ 
left join MEETINGS me2 
    on  rel2.MEETING_ID = me2.ID and 
      sub.MAX_DATE = me2.DATE /* Note 4 */ 
left join COMMITTEES co 
    on  co.ID = me2.COMMITTEE_ID 
where  me.ID = 2 and 
      (sub.MAX_DATE is null or me2.DATE is not null) /* Note 5 */ 
order by rel.AGENDA_ITEM_ID, rel2.MEETING_ID; 

注意

  1. 你不需要用AGENDA_ITEMS的加入,因爲ID已經可以在關係表

  2. 到這一點,我們有次會議,其議程項目和他們的「計算」最大日

  3. 我們得到

  4. 每個議題的所有會議......這樣我們就可以挑誰迄今爲止,我們以前

  5. 需要這個條件,因爲所有的從rel2上都被留下(因爲有些議題可能沒有前一次會議,因此MAX_DATE = null)加入計算最大的日期,但這種方式相匹配的會議me2會給一些議程項目不合適編輯會議。

+0

我將不得不測試更多,但注意到「左連接COMMITTEES」的錯字,但不能在未更改至少6個字符的情況下進行編輯。隨着這種變化,它乍看起來似乎工作。 – user3366016

+0

修復了輸入錯誤:) –

+1

根據@Paul Spiegel的評論,我在我的選擇中添加了一個獨特的選項,結果現在與您在這兩個rextester示例中所期望的一致。 –