MySQL:使用左连接的查询结果错误

时间:2021-02-11 09:46:43

标签: mysql left-join

我遇到了这里描述的问题:https://www.media-division.com/using-mysql-generate-daily-sales-reports-filled-gaps/(按日期对 SUM 或 COUNT 的结果进行分组时的差距)。我对解决方案的第一次尝试是这样的,查询 1:

  SELECT
    DATE_FORMAT(st.vd, '%Y-%m-%d') as d,
    SUM(
      CASE
        WHEN st.vd IS NULL THEN 0
        ELSE 1
      END
    ) AS nrvisits
  FROM
    (
      SELECT v.visit_date vd
      FROM `temp_dates_2` t
      LEFT JOIN `visits` v ON DATE(v.visit_date) = DATE(t.t_date)
      ORDER BY t.t_date
    ) as st
GROUP BY d
ORDER BY d

(temp_dates_2 包含唯一的日期列表)。我简化了它,查询 2:

  SELECT
    DATE_FORMAT(td.t_date, '%Y-%m-%d') as d,
      (SELECT COUNT(1)
        FROM visits as v
        WHERE DATE(v.visit_date) = DATE(td.t_date)
      ) as nrvisits
  FROM temp_dates_2 td
  GROUP BY d
  ORDER BY d

两个查询都在运行,没有语法或运行时错误,但结果不同:查询 1 提供的值要大得多。为了澄清,我对单个日期使用了一个非常简单的查询:

SELECT count(1) FROM visits WHERE DATE_FORMAT(visit_date, '%Y-%m-%d') = '2021-01-21'

它提供了与查询 2 相同的结果。 我的问题是:为什么查询 1 提供了错误的结果?我怀疑带有内部连接的子查询的结果会为同一次访问返回多条记录。 更新:表的 sql 转储:https://webentwicklung.ulrichbangert.de/temp_dates_2.sql https://webentwicklung.ulrichbangert.de/visits.sql

1 个答案:

答案 0 :(得分:1)

再填写我的评论。我建议这样做:

SELECT 
  t.t_date, 
  COUNT(v.primarykeycolumn)      
FROM 
  temp_dates_2 t       
  LEFT JOIN visits v ON DATE(v.visit_date) = t.t_date       
GROUP BY t.t_date

基于 temp_dates_2 只包含 DATE 的假设(因此不需要对它们调用 DATE()),并且 v.visit_date 可能也包含一个时间,而 DATE() 正在剥离它

我还建议 q1 有一个错误,即子查询选择访问日期,这可能会导致日期合并/丢失。举个例子:

Visits
2020-12-24 12:34:56 --(visit on christmas eve - shop still open
2020-12-24 23:45:00 --(visit on christmas eve - shop still open
                    --(no visit on christmas day - shop closed)
                    --(no visit on boxing day - shop closed)
2020-12-27 12:34:56 --(visit - shop reopen)

temp_dates_2
2020-12-24 
2020-12-25 
2020-12-26 
2020-12-27 

Q1 中左连接子查询的结果(删除了不相关的 orderby):

  SELECT v.visit_date vd
  FROM `temp_dates_2` t
  LEFT JOIN `visits` v ON DATE(v.visit_date) = DATE(t.t_date)

2020-12-24 --(visit on christmas eve - shop still open
2020-12-24 --(visit on christmas eve - shop still open
NULL       --(no visit on christmas day - shop closed)
NULL       --(no visit on boxing day - shop closed)
2020-12-27 --(visit - shop reopen)

然后 q1 分组、求和和格式等,结果:

Date        Count
2020-12-24  2
NULL        0
2020-12-27  1

实际上,它与基本的 SELECT date(visit_date), count(*) FROM visits GROUP BY date(visit_date) 没有太大区别,只是它有一个无用的 NULL,代表圣诞节和节礼日的合并,以及 0 计数。真的,你似乎想要的是:

2020-12-24  2 --(visits on christmas eve - shop still open
2020-12-25  0 --(no visit on christmas day - shop closed)
2020-12-26  0 --(no visit on boxing day - shop closed)
2020-12-27  1 

由我的第一个查询给出;它使用 COUNT() 不计算空值的事实,并且通过计算参与 LEFT JOIN 的列或通过计算主键列,我们可以确定 NULL 只发生在那种列中,因为“连接失败;在右手边的表中没有找到匹配的行”而不是因为“数据在行中自然包含空值”

例如:

temp_dates left join visits:

tempdate    visitdate   visitpk  vistorcomment
2020-12-24  2021-12-24  1        null
2020-12-25  null        null
2020-12-26  null        null
2020-12-27  2021-12-27  2        "nice place"

我们应该 COUNT() visitpk(主键永远不能为空)或 visitdate(如果连接失败,它将为空),我们不应该 COUNT 评论,因为它有时是空的自然(访客没有留下评论)。如果我们使用访客评论作为计数,它会扭曲数字,因为这意味着“发生了访问并且访客留下了评论”


所有这些都不能回答“为什么我的数字在 X 中比 Y 大”,但如果没有工作示例,就无法回答;创建一个复制它的小提琴,我会告诉你。我目前无法从说明为什么 q1 的数字会更大的数据中看出任何原因-您断言 temp_dates 是唯一的,因此不应发生笛卡尔爆炸,并且您似乎使用日期数据类型,因此应将 dd/mm 和 m/dd 混为一谈不会发生,但我可以看到您处理数据的方式存在错误/结果不是您想要的

这是一个解释如何获得您想要的结果、它为什么起作用以及有一个更简单的查询的答案..