限制在 MySQL 问题的左连接中返回的行
Posted
技术标签:
【中文标题】限制在 MySQL 问题的左连接中返回的行【英文标题】:Limiting rows returned in a left join in MySQL issue 【发布时间】:2014-05-07 10:17:51 【问题描述】:我有两张桌子trackings
和responses
。我正在运行下面的查询以根据 case/code_2 列连接两个表。
因为response
表中的每条记录有时在trackings
表中会有多条记录,所以我只希望返回一行,而不是像通常情况下那样为响应表中的每一行重复。
我使用下面的查询完成了这项工作,效果很好。
SELECT T0.timestamp AS 'Creation Date', T0.ipaddress, T0.code_1 AS 'Alias', T0.code_2 AS 'Case ID', COUNT(T0.ipaddress) AS each_amount, T0.first, MAX(T1.res_id) AS 'responses'
FROM `trackings` AS T0
LEFT JOIN `responses` AS T1
ON T0.code_2 = T1.case
JOIN (
SELECT T2.case, MAX(T2.timestamp) AS max_date
FROM `responses` AS T2
GROUP BY T2.case
) x_temp_response_table
ON x_temp_response_table.case = T1.case
AND x_temp_response_table.max_date = T1.timestamp
WHERE T0.timestamp >= '2014-04-20 00:00:00'
AND T0.timestamp <= '2014-04-30 23:59:59'
GROUP BY code_2
但是,由于第二个连接将响应行限制为只有一个,因此当响应表中没有相应记录时,它现在不会返回 trackings
行。
基本上在添加第二个连接之前,它会返回trackings
表中的所有行,如果responses
表中没有对应的行,只需在“响应”列中粘贴一个 NULL
所以理想情况下,即使响应表中没有相应的行,我希望上面的查询仍然返回 trackings
表中的所有行。
任何帮助将不胜感激。
【问题讨论】:
感谢您的评论,不幸的是,将 JOIN 替换为 LEFT JOIN 只会带回重复的行。 (哇,忍者删除了你的评论 :-) 可能是left join
和distinct
?
为什么不用LEFT JOIN x_temp_response_table
而不加入 T1?
LEFT JOIN with DISTINCT 不起作用。如果我删除第一个 LEFT JOIN,我该如何加入 x_temp_response_table
以将响应行限制为 1?
根据标准,我们不能在左连接后使用普通连接,否则它不会提供所需的结果,因此您也应该在第二次连接中使用左连接。
【参考方案1】:
你可以用一个糟糕的子查询来做到这一点(性能不高,但是)......
SELECT
T0.timestamp AS 'Creation Date',
T0.ipaddress, T0.code_1 AS 'Alias',
T0.code_2 AS 'Case ID',
COUNT(T0.ipaddress) AS each_amount,
T0.first,
(SELECT r.res_id from responses r
where r.case = T0.code_2
order by r.timestamp desc
LIMIT 1) as responses
FROM `trackings` AS T0
WHERE T0.timestamp >= '2014-04-20 00:00:00'
AND T0.timestamp <= '2014-04-30 23:59:59'
GROUP BY code_2
【讨论】:
您拥有的 LEFT JOIN 正在加入您现在已删除的第一个 LEFT JOIN (T1)... @superphonic 你是完全正确的。这个应该可以解决问题,但您可能会等待更好的答案... 这是一种享受!我也将尝试@dnoeth 的答案。知道这是否比使用派生表更快或更慢? @superphonic 我怀疑这是否真的“快”(但快慢问题也取决于其他因素)。无论如何,您可以尝试查看不同(工作)版本的执行计划以保持最佳...【参考方案2】:这是未经测试的,但将 responses
连接移动到派生表中应该可以:
SELECT T0.timestamp AS 'Creation Date', T0.ipaddress, T0.code_1 AS 'Alias', T0.code_2 AS 'Case ID', COUNT(T0.ipaddress) AS each_amount, T0.first, MAX(T1.res_id) AS 'responses'
FROM `trackings` AS T0
LEFT JOIN
(
SELECT T1.case, T1.res_id
FROM `responses` AS T1
JOIN
(
SELECT T2.CASE, MAX(T2.TIMESTAMP) AS max_date
FROM `responses` AS T2
GROUP BY T2.CASE
) x_temp_response_table
ON x_temp_response_table.CASE = T1.CASE
AND x_temp_response_table.max_date = T1.TIMESTAMP
) AS T1
ON T0.code_2 = T1.CASE
WHERE T0.TIMESTAMP >= '2014-04-20 00:00:00'
AND T0.timestamp <= '2014-04-30 23:59:59'
GROUP BY code_2
【讨论】:
谢谢你,我收到错误Error Code: 1060. Duplicate column name 'case'
是SELECT *
,你必须用列列表替换它。我将编辑我的回复
是的,我刚刚添加了它们,现在也可以享用了。我将进行一些测试,看看哪些答案更快,因为它们都可以正常工作。
效率主要取决于优化器的能力。否则,它们可能主要基于两个表中的行数以及符合这些条件的行数。顺便说一句,我的查询是标准 SQL,而 Raphaël 的查询是 mysql 专有的 :-)
非常感谢您的努力。不幸的是,您的三个答案中最慢的。可能是因为我的表格设计/索引不好,但 Raphaël's 是这里最快的。【参考方案3】:
试试下面的查询,希望它能提供你想要的结果:
SELECT T0.timestamp AS 'Creation Date', T0.ipaddress, T0.code_1 AS 'Alias', T0.code_2 AS 'Case ID', COUNT(T0.ipaddress) AS each_amount, T0.first, MAX(T1.res_id) AS 'responses'
FROM `trackings` AS T0
LEFT JOIN
(
SELECT `case`,res_id FROM
(SELECT `case`,res_id FROM `responses` ORDER BY `timestamp` DESC) T2
GROUP BY `case`
) T1
ON T0.code_2 = T1.case
WHERE T0.timestamp >= '2014-04-20 00:00:00'
AND T0.timestamp <= '2014-04-30 23:59:59'
GROUP BY code_2;
【讨论】:
感谢您的回答。它运行良好,但比 Raphaël 的尝试要慢一些。以上是关于限制在 MySQL 问题的左连接中返回的行的主要内容,如果未能解决你的问题,请参考以下文章