【问题标题】:Why is my Count on joined tables not correct?为什么我对连接表的计数不正确?
【发布时间】:2018-04-17 12:40:47
【问题描述】:

我有以下表格:

STUDYA (别名 J)

LOGINID   RCD_NUM         DATE         TYPE
 8745       0         04/15/2018      PRELIM
 6548       0         08/19/2017      PRELIM
 7445       0         10/02/2017      PRELIM
 2867       0         03/19/2018      PRELIM

试用版(别名 G)

LOGINID   RCD_NUM         DATE         TYPE
 8745       0          02/15/2017     ROLLUP
 7445       0          07/09/2016     ROLLUP
 2867       0          05/17/2017     ROLLUP
 2867       0          05/28/2017     ROLLUP
 5249       0          06/20/2017     ROLLUP
 1335       0          09/29/2017     ROLLUP
 9238       0          12/03/2017     ROLLUP

SPRINT (别名 H)

 LOGINID   RCD_NUM         DATE        TYPE
  5521       0          01/10/2018     SPRNT
  8745       1          04/04/2018     SPRNT
  3487       0          11/14/2017     SPRNT
  6627       0          05/05/2018     SPRNT

还有另一个名为 LOGIN 的表,我正在匹配 LOGINID。

我在 SQL Server 2014 上运行以下查询:

SELECT 'COUNTS', COUNT(G.LOGINID), COUNT(H.LOGINID), COUNT(J.LOGINID)
 FROM LOGIN F 
LEFT OUTER JOIN  TRIALS G ON  F.LOGINID= G.LOGINID AND G.RCD_NUM = 
 F.RCD_NUM 
LEFT OUTER JOIN  SPRINT H ON  F.LOGINID= H.LOGINID AND H.RCD_NUM = 
 F.RCD_NUM
LEFT OUTER JOIN  STUDYA J ON  F.LOGINID= J.LOGINID AND J.RCD_NUM = 
 F.RCD_NUM  

WHERE ( ( F.EFFDT = 
(SELECT MAX(F_ED.EFFDT) FROM PS_JOB F_ED 
WHERE F.LOGINID = F_ED.LOGINID 
  AND F.RCD_NUM = F_ED.RCD_NUM 
  AND F_ED.EFFDT <= SUBSTRING(CONVERT(CHAR,GETDATE(),121), 1, 10)) 
AND F.EFFSEQ = 
(SELECT MAX(F_ES.EFFSEQ) FROM PS_JOB F_ES 
 WHERE F.LOGINID = F_ES.LOGINID 
  AND F.RCD_NUM = F_ES.RCD_NUM 
  AND F.EFFDT = F_ES.EFFDT) )

这是上述查询的结果:

(No column name)   (No column name)   (No column name)   (No column name) 
  COUNTS                  9                  5                  5

您可以看到最后一列 (COUNT(J.LOGINID)) 显示 5 条记录,而在实际表中(上述查询)实际上只有 4 > 记录。我认为是因为 LOGINID 2867 在 TRIALS 表中有 2 行,在 STUDYA 中有 1 行。

我对此进行了研究,建议似乎是在加入之前进行聚合。不过,我正在努力寻找最好的方法。任何帮助表示赞赏!

【问题讨论】:

  • 我强烈建议您不要使用ghj 作为表别名。您正在混淆您的代码。表名不长,TRIALS.LOGINID 仅比G.LOGINID 长 5 个字符但显着易于调试、维护等。
  • 您的示例数据也与您的示例输出不匹配。 TRIALS 中有 7 行,但您的示例输出显示 9?
  • 我厌倦了您的解决方案,但是我收到一条错误消息:每个 GROUP BY 表达式必须至少包含一个不是外部引用的列。有任何想法吗?谢谢!

标签: sql tsql count left-join sql-server-2014


【解决方案1】:

根据您的数据和用例,您可能会从您提到的方法中获得性能优势:聚合然后加入...

SELECT
 'COUNTS',
 COUNT(G.LOGINID) distinct_g, SUM(G.ROW_COUNT) row_count_g,
 COUNT(H.LOGINID) distinct_h, SUM(H.ROW_COUNT) row_count_h,
 COUNT(J.LOGINID) distinct_j, SUM(J.ROW_COUNT) row_count_j
FROM
 LOGIN F 
LEFT OUTER JOIN
(
 SELECT LOGINID, RCD_NUM, COUNT(*) AS ROW_COUNT FROM TRIALS GROUP BY LOGINID, RCD_NUM
)
 G
  ON  F.LOGINID = G.LOGINID
  AND F.RCD_NUM = G.RCD_NUM 
LEFT OUTER JOIN
(
 SELECT LOGINID, RCD_NUM, COUNT(*) AS ROW_COUNT FROM SPRINT GROUP BY LOGINID, RCD_NUM
)
 H
  ON  F.LOGINID = H.LOGINID
  AND F.RCD_NUM = H.RCD_NUM 
LEFT OUTER JOIN
(
 SELECT LOGINID, RCD_NUM, COUNT(*) AS ROW_COUNT FROM STUDYA GROUP BY LOGINID, RCD_NUM
)
 J
  ON  F.LOGINID = J.LOGINID
  AND F.RCD_NUM = J.RCD_NUM 

WHERE ( ( F.EFFDT = 
(SELECT MAX(F_ED.EFFDT) FROM PS_JOB F_ED 
WHERE F.LOGINID = F_ED.LOGINID 
  AND F.RCD_NUM = F_ED.RCD_NUM 
  AND F_ED.EFFDT <= SUBSTRING(CONVERT(CHAR,GETDATE(),121), 1, 10)) 
AND F.EFFSEQ = 
(SELECT MAX(F_ES.EFFSEQ) FROM PS_JOB F_ES 
 WHERE F.LOGINID = F_ES.LOGINID 
  AND F.RCD_NUM = F_ES.RCD_NUM 
  AND F.EFFDT = F_ES.EFFDT) )

优化器可以使用 predicate-push-down / macro-like-expansion 来确保只对相关行进行聚合并保留索引的使用。

此模式还允许您获取其他统计信息,例如每个登录/rcd 组合的最小/最大日期。

编辑:

另一种模式可能包括...

SELECT
 'COUNTS',
 SUM(G.ROW_COUNT) row_count_g,
 SUM(H.ROW_COUNT) row_count_h,
 SUM(J.ROW_COUNT) row_count_j
FROM
 LOGIN F 
OUTER APPLY
(
 SELECT COUNT(*) AS ROW_COUNT FROM TRIALS WHERE LOGINID = F.LOGINID AND RCD_NUM = F.RCD_NUM
)
  G
OUTER APPLY
(
 SELECT COUNT(*) AS ROW_COUNT FROM SPRINT WHERE LOGINID = F.LOGINID AND RCD_NUM = F.RCD_NUM
)
  H
OUTER APPLY
(
 SELECT COUNT(*) AS ROW_COUNT FROM STUDYA WHERE LOGINID = F.LOGINID AND RCD_NUM = F.RCD_NUM
)
  J

WHERE ( ( F.EFFDT = 
(SELECT MAX(F_ED.EFFDT) FROM PS_JOB F_ED 
WHERE F.LOGINID = F_ED.LOGINID 
  AND F.RCD_NUM = F_ED.RCD_NUM 
  AND F_ED.EFFDT <= SUBSTRING(CONVERT(CHAR,GETDATE(),121), 1, 10)) 
AND F.EFFSEQ = 
(SELECT MAX(F_ES.EFFSEQ) FROM PS_JOB F_ES 
 WHERE F.LOGINID = F_ES.LOGINID 
  AND F.RCD_NUM = F_ES.RCD_NUM 
  AND F.EFFDT = F_ES.EFFDT) )

【讨论】:

  • 这似乎很好用!所以本质上(如果我错了,请纠正我)这样做是在加入其他表之前对每个表中的总行数求和? ROW_COUNT 是 sql 函数吗?非常感谢。
  • AS ROW_COUNT 只是将该列命名为ROW_COUNT。您可以使用AS lego,然后使用SUM(G.lego)。子查询确保每个LOGINID, RCD_NUM 只返回一行,以便在连接结果时不会发生重复。子查询返回的字段之一是为输出行(使用COUNT(*)聚合了多少源行。然后你可以把这些值加起来。
【解决方案2】:

你需要得到不同的计数

 SELECT 'COUNTS', COUNT(distinct G.LOGINID), COUNT(distinct 
 H.LOGINID), COUNT(distinct J.LOGINID) ..

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-26
    • 2018-08-31
    • 2013-12-12
    相关资源
    最近更新 更多