限制在 MySQL 问题的左连接中返回的行

Posted

技术标签:

【中文标题】限制在 MySQL 问题的左连接中返回的行【英文标题】:Limiting rows returned in a left join in MySQL issue 【发布时间】:2014-05-07 10:17:51 【问题描述】:

我有两张桌子trackingsresponses。我正在运行下面的查询以根据 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 joindistinct 为什么不用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 问题的左连接中返回的行的主要内容,如果未能解决你的问题,请参考以下文章

linq中的左外连接

SQL中的左连接与右连接,内连接有啥区别

MySQL - 限制连接中的行数?

数据库操作-内连接外连接

MySQL--7种join连接

为啥Mysql的左连接条件不被遵守?