计算多个不同的一对多关系

时间:2018-06-22 13:54:04

标签: sql sql-server

我有以下SQL:

SELECT j.AssocJobKey
, COUNT(DISTINCT o.ID) AS SubjectsOrdered
, COUNT(DISTINCT s.ID) AS SubjectsShot
FROM Jobs j
LEFT JOIN Orders o ON o.AssocJobKey = j.AssocJobKey
LEFT JOIN Subjects s ON j.AssocJobKey = s.AssocJobKey
GROUP BY 
j.AssocJobKey
,j.JobYear

基本结构是Job,是AssocJobKey唯一的父项,并且与Subjects和Orders具有一对多的关系。 该查询提供了我想要的内容,输出如下所示:

|      AssocJobKey      |     SubjectsOrdered    |     SubjectsShot    |
|-----------------------|------------------------|---------------------|
|        BAT-H181       |            107         |          830        |
|---------------------  |------------------------|---------------------|
|        BAT-H131       |            226         |          1287       |

问题在于查询很繁琐,而我的存储量却增加了,无法在大型数据集上运行它。如果我删除了对应计数中的LEFT JOIN之一,查询将立即执行并且没有问题。因此,某种程度上,两个左联接之间的反弹比它们应有的多,但我不明白为什么会这样。

真的希望尽可能避免加入子选择。

1 个答案:

答案 0 :(得分:1)

您的查询正在为每个作业生成笛卡尔积。这是很大的-您的第二行产生了约50万行。 COUNT(DISTINCT)然后必须找出该笛卡尔积中的唯一ID。

解决方案很简单:预先汇总:

SELECT j.AssocJobKey, o.SubjectsOrdered, s.SubjectsShot
FROM Jobs j LEFT JOIN
     (SELECT o.AssocJobKey, COUNT(*) as SubjectsOrdered
      FROM Orders o
      GROUP BY o.AssocJobKey
     ) o
     ON o.AssocJobKey = j.AssocJobKey LEFT JOIN
     (SELECT j.AssocJobKey, COUNT(s.ID) AS SubjectsShot
      FROM Subjects s
      GROUP BY j.AssocJobKey
     ) s
     ON j.AssocJobKey = s.AssocJobKey;

这做出了一些我认为合理的假设:

  • “订单和主题”表中的id是唯一的且非NULL。
  • jobs.AssocJobKey是唯一的。

如果其中任何一个都不正确,则可以轻松地调整查询,但它们似乎是合理的假设。

通常对于不同维度上的这些类型的联接,COUNT(DISTINCT)是一个合理的解决方案(查询当然更简单)。当最多只有几个值时,这是正确的。