2013-10-14 54 views
2

在這個例子中,我有一個用戶(main_data),通過列表(pass_list)和每個通行碼類型(pass_code)的相應優先級列表。我正在構建的查詢正在查找用戶列表以及具有最低優先級的相應密碼類型。下面的查詢的作品,但它似乎有可能是一個更快的方式來構建它我缺少。 SQL小提琴:http://sqlfiddle.com/#!2/2ec8d/2/0或參見下面的表格詳細信息。最快的方式來選擇加入最小行

SELECT md.first_name, md.last_name, pl.* 
FROM main_data md 
JOIN pass_list pl on pl.main_data_id = md.id 
AND 
pl.id = 
    (
    SELECT pl2.id 
    FROM pass_list pl2 
    JOIN pass_code pc2 on pl2.pass_code_type = pc2.type 
    WHERE pl2.main_data_id = md.id 
    ORDER BY pc2.priority 
    LIMIT 1 
) 

結果:

+------------+-----------+----+--------------+----------------+ 
| first_name | last_name | id | main_data_id | pass_code_type | 
+------------+-----------+----+--------------+----------------+ 
| Bob  | Smith  | 1 |   1 | S    | 
| Mary  | Vance  | 8 |   2 | M    | 
| Margret | Cough  | 5 |   3 | H    | 
| Mark  | Johnson | 9 |   4 | H    | 
| Tim  | Allen  | 13 |   5 | M    | 
+------------+-----------+----+--------------+----------------+ 

用戶(main_data)

+----+------------+-----------+ 
| id | first_name | last_name | 
+----+------------+-----------+ 
| 1 | Bob  | Smith  | 
| 2 | Mary  | Vance  | 
| 3 | Margret | Cough  | 
| 4 | Mark  | Johnson | 
| 5 | Tim  | Allen  | 
+----+------------+-----------+ 

通列表(pass_list)

+----+--------------+----------------+ 
| id | main_data_id | pass_code_type | 
+----+--------------+----------------+ 
| 1 |   1 | S    | 
| 3 |   2 | E    | 
| 4 |   2 | H    | 
| 5 |   3 | H    | 
| 7 |   4 | E    | 
| 8 |   2 | M    | 
| 9 |   4 | H    | 
| 10 |   4 | H    | 
| 11 |   5 | S    | 
| 12 |   3 | S    | 
| 13 |   5 | M    | 
| 14 |   1 | E    | 
+----+--------------+----------------+ 

表指定的優先級(pass_code)

+----+------+----------+ 
| id | type | priority | 
+----+------+----------+ 
| 1 | M |  1 | 
| 2 | H |  2 | 
| 3 | S |  3 | 
| 4 | E |  4 | 
+----+------+----------+ 

回答

1

由於MySQL的獨特擴展其GROUP BY,這很簡單:

SELECT * FROM 
(SELECT md.first_name, md.last_name, pl.* 
FROM main_data md 
JOIN pass_list pl on pl.main_data_id = md.id 
ORDER BY pc2.priority) x 
GROUP BY md.id 

這使通過使用內部查詢的行排序僅返回的md.id每個唯一值遇到的第一行,在您應用該組之前,只能獲得所需的行。

+1

雖然它通常能夠正常工作,但它帶回的行的值是官方未定義的。所以將來它可能不是第一排。 – Kickstart

+0

@Kickstart是的......人們總是這麼說,但它的工作時間如此多年,如果它發生變化,那麼我會擔心這一點。在此之前,我個人認爲利用這個令人難以置信的方便「功能」是很好的。 – Bohemian

+1

公平點,但如果它確實發生了變化,我認爲它是一個噩夢,以查找哪些查詢有什麼問題。 – Kickstart

0

我並不熟悉MySQL組的特殊行爲,但我對這些類型的問題的解決方案是簡單地表達爲不存在具有較低優先級的行。這是標準的SQL,因此應該適用於任何數據庫。

select distinct u.id, u.first_name, u.last_name, pl.pass_code_type, pc.id, pc.priority 
from main_data u 
    inner join pass_list pl on pl.main_data_id = u.id 
    inner join pass_code pc on pc.type = pl.pass_code_type 
where not exists (select 1 
        from pass_list pl2 
        inner join pass_code pc2 on pc2.type = pl2.pass_code_type 
        where pl2.main_data_id = u.id and pc2.priority < pc.priority); 

這樣做的好壞取決於具有合適的索引(假設main_data和pass_list有點大)。在這種情況下,主鍵(應自動創建)和外鍵索引應該足夠。可能有更快的其他查詢,我首先將它與您的查詢進行比較。因爲你在pass_list中有重複的行(id 9 & 10),但是如果你確保重複項不能存在(main_data_id上的唯一索引,pass_code_type),那麼你將節省一些時間刪除強制最終排序結果集的區別。結果集越大,節省的成本就越明顯。

+0

此查詢爲用戶Mark Johnson生成重複項。此外,pass_list中必須存在重複行,並且查詢將選擇具有最高優先級的行並顯示該行。 – bobcat

+0

@John你是對的。我已經從選擇列表中刪除了列pl.id,並且應該修復重複的問題,並讓我之前關於「select distinct」與「select」有效的評論。 – sceaj

+0

@John當你說pass_list中必須有重複項時,你的意思是必須有精確的重複項,就像在pl.id 9&10中一樣,或者只是有多於一個pass_list行將給定的pass_code連接到給定的main_data? – sceaj

0

一個版本,將讓細節的要求,並應在不同的口味工作SQL

SELECT md.first_name, md.last_name, MinId, pl.main_data_id, pl.pass_code_type 
FROM main_data md 
INNER JOIN pass_list pl 
ON md.id = pl.main_data_id 
INNER JOIN pass_code pc 
ON pl.pass_code_type = pc.type 
INNER JOIN 
(
    SELECT pl.main_data_id, pl.pass_code_type, Sub0.MinPriority, MIN(pl.id) AS MinId 
    FROM pass_list pl 
    INNER JOIN pass_code pc 
    ON pl.pass_code_type = pc.type 
    INNER JOIN 
    (
     SELECT main_data_id, MIN(priority) AS MinPriority 
     FROM pass_list a 
     INNER JOIN pass_code b 
     ON a.pass_code_type = b.type 
     GROUP BY main_data_id 
    ) Sub0 
    ON pl.main_data_id = Sub0.main_data_id 
    AND pc.priority = Sub0.MinPriority 
    GROUP BY pl.main_data_id, pl.pass_code_type, Sub0.MinPriority 
) Sub1 
ON pl.main_data_id = Sub1.main_data_id 
AND pl.id = Sub1.MinId 
AND pc.priority = Sub1.MinPriority 
ORDER BY pl.main_data_id 

這不依賴於MySQLs GROUP BY的功能靈活性。

+0

這可以工作,但似乎非常複雜,並且仍比我原來的查詢慢。這對我發佈的那個有什麼好處?對於我的原始查詢,這個數據集需要5 - 6秒的時間,而不到一秒。這也使用臨時的;使用filesort 3次並加入緩衝區兩次。 – bobcat

+0

對於大量的數據,您的查詢應該逐漸減慢(實際上它必須爲每行執行一個單獨的查詢),如果您查詢的查詢具有多個具有相同優先級的ID,則它不會被定義一個會被退回。但是,你可能確實需要添加更多的鍵(例如,pass_code表中的一個類型) – Kickstart