2013-10-14 23 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