【问题标题】:SQL Server: Two COUNTs in one query multiplying with one another in outputSQL Server:一个查询中的两个 COUNT 在输出中相乘
【发布时间】:2018-08-10 22:49:55
【问题描述】:

我有一个查询用于显示队列中的信息,其中部分信息显示了属于父实体(更改)的子实体(包和实验室)的数量。但是,它们不是显示每种类型孩子的个体计数,而是彼此相乘。

在下面的例子中,应该有 3 个实验室和 18 个包,但是彼此相乘,输出是 54 个。

以下是查询的违规部分。

SELECT cef.ChangeId, COUNT(pac.PackageId) AS 'Packages', COUNT(lab.LabRequestId) AS 'Labs'
FROM dbo.ChangeEvaluationForm cef
LEFT JOIN dbo.Lab
ON cef.ChangeId = Lab.ChangeId
LEFT JOIN dbo.Package pac 
ON (cef.ChangeId = pac.ChangeId AND pac.PackageStatus != 6 AND pac.PackageStatus !=7)
WHERE cef.ChangeId = 255
GROUP BY cef.ChangeId

我觉得这很明显,但我没有想到如何解决它,因此这两个计数彼此独立,就像我应该的那样。在我的任何研究中似乎也没有这样的场景。谁能指导我正确的方向?

【问题讨论】:

    标签: sql sql-server tsql aggregate-functions


    【解决方案1】:

    因为您确实将源行乘以每个左连接。所以有时你更有可能在这里cross join

    SELECT cef.ChangeId, p.Packages, l.Labs
    FROM dbo.ChangeEvaluationForm cef
    OUTER APPLY(
      SELECT COUNT(*) as Labs
      FROM dbo.Lab
      WHERE cef.ChangeId = Lab.ChangeId
    ) l
    OUTER APPLY(
      SELECT COUNT(*) AS Packages
      FROM dbo.Package pac 
      WHERE (cef.ChangeId = pac.ChangeId AND pac.PackageStatus != 6 AND pac.PackageStatus !=7)
    ) p
    WHERE cef.ChangeId = 255
    GROUP BY cef.ChangeId
    

    现在可能不需要GROUP BY

    【讨论】:

    • 非常感谢,这行得通。我有一些新概念要在这里学习。
    【解决方案2】:

    您从 dbo.ChangeEvaluationForm 表中的特定 ChangeId 开始(您的示例中的 ChangeId = 255),然后加入 dbo.Lab 表。考虑到有 3 个 Labs 的 ChangeId = 255,此联接使您的结果从 1 行变为 3 行。您的问题是在下一次联接上,您将前一次联接的所有 3 个结果行与 dbo.Package 表联接起来,该表具有ChangeId = 255 为 18 行。pac.PackageId 和 lab.LabRequestId 列的结果计数将为 3 x 18 = 54。

    要获得您想要的,有两种简单的解决方案:

    • 使用 COUNT DISTINCT 而不是 COUNT。这只会计算 pac.PackageId 和 lab.LabRequestId 的不同值,而不是重复的值。
    • 将连接拆分为 2 个子查询并连接它们的结果(通过 ChangeId)

    【讨论】:

      【解决方案3】:

      从您的问题中很难得出您期望从查询中得到什么结果。所以我想你想要以下结果:

      +----------+----------+------+
      | ChangeId | Packages | Labs |
      +----------+----------+------+
      |      255 |       18 |    3 |
      +----------+----------+------+
      

      如果您正在寻找上述结果,请尝试以下查询。

      SELECT cef.ChangeId, ISNULL(pac.PacCount, 0) AS 'Packages', ISNULL(Lab.LabCount, 0) AS 'Labs'
      FROM dbo.ChangeEvaluationForm cef
      LEFT JOIN (SELECT Lab.ChangeId, COUNT(*) LabCount FROM dbo.Lab GROUP BY) Lab
      ON cef.ChangeId = Lab.ChangeId
      LEFT JOIN (SELECT pac.ChangeId, COUNT(*) PacCount FROM dbo.Package pac WHERE pac.PackageStatus != 6 AND pac.PackageStatus !=7 GROUP BY pac.ChangeId) pac
      ON cef.ChangeId = pac.ChangeId
      WHERE cef.ChangeId = 255
      

      查询说明:

      • 在您的查询中,您没有使用 group by,所以它最终给您 54 作为计数,这是笛卡尔积。
      • 在此查询中,我尝试按“ChangeId”进行分组,并在加入表之前找到聚合。因此,加入前将计算 3 个实验室和 18 个包。
      • 您还会注意到我已将PackageStatus 过滤器移到pac 表中的group by 之前。所以不需要的记录不会影响我们的计数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多