从查询同一个表的子查询中获取计数列

时间:2013-04-25 20:58:26

标签: sql-server tsql

让我说我有桌子:

帐户:id,id2,id3,custId,option1,option2,type

并且同一张表中的所有帐户都有一些" parent"帐户(可以是#34; solo"没有孩子的帐户),有些是"孩子"帐户(我知道设计不好,但那是我正在使用的)。

每个帐户的不同/复合键为id + id2 + id3 + option1 + option2 + custId

我想查询" parent"的列表或者"独奏"具有特定custIdtype的帐户,可以通过以下方式轻松完成:

Select *
From accounts
Where custId = 1 And type = 'foo'
and (option1 = 'solo' Or option2 = 0)

其中'solo'表示它是一个独立帐户且没有子女,0表示它是一系列帐户中的第一个,因此也就是其父级。

然后我想获得"孩子的数量"与通过上述查询的结果集获得的每个父关联。显然"独奏"帐户不会有孩子。

例如,获得"孩子"来自特定的父母"帐户会是这样的(让我们说我正在寻找id = 1,id2 = 1,id3 = 1的帐户子女:

Select Count(*)
From accounts
Where id = 1 And id2 = 1 And id3 = 1 And custId = 1 
And option1 != 'solo' And option2 != 0)

那么如何组合这两个查询以获得第一个结果集的每个行的计数呢?

示例

填充我们可以拥有的表格:

 id    id2    id3     custId     option1     option2     type
 ------------------------------------------------------------
  1     1      1        1         solo        9           foo
  2     2      2        1         solo        9           foo
  3     4      4        1         NULL        0           foo
  3     4      4        1         NULL        1           foo
  3     4      4        1         NULL        2           foo

我想要一个像这样的结果集:

 id    id2    id3     custId     option1     option2     type     children
 -------------------------------------------------------------------------
  1     1      1        1         solo        9           foo      0
  2     2      2        1         solo        9           foo      0 
  3     4      4        1         NULL        0           foo      2  

基本上我想要这样的东西(我知道这是错的)

Select *, 
(Select count(*) from accounts
Where option1 != 'solo' And option2 != 0
And --all 3 ids and custId equal to the current row
) --this is the part i don't know how to do 
From accounts
Where custId = 1 And Type = 'foo' And (option1 = 'solo' Or option2 = 0)

我的大脑正在围绕如何做到这一点。谢谢您的帮助。

1 个答案:

答案 0 :(得分:1)

这适用于您的数据:

select a.*
  , children = isnull(c.children, 0)
from accounts a
  outer apply (select children = count(1)
              from accounts c
               where a.option1 is null
                and a.id = c.id
                and a.id2 = c.id2
                and a.id3 = c.id3
                and a.custId = c.custId
                and c.option2 <> 0) c
where (a.option1 = 'solo' or a.option2 = 0)
  and a.type = 'foo'

SQL Fiddle with demo

评论后修改:

好的,所以查询的主要部分是WHERE子句 - 这决定了返回的三行。

一旦我们有这些行,我们需要计算出有多少孩子 - 这就是OUTER APPLY实现的目标。

基本上,对于每个非独奏行,我们将此匹配到任何子行(即c.option2 <> 0)并获取这些行的计数,当现在匹配时显式返回0或者它是 'solo'父行。

由于我们仅匹配非独奏父行,因此我们使用a.option1 is null过滤这些行,即在检查任何匹配之前检查父行option1值。由于数据的性质,c.option1 is null也可以,因为父和子非独奏行都有null option1值。

count(1)并不意味着什么;它只是每行计算的任意值,在这种情况下是常量。您可以轻松使用count(*)。在当天,这可能会对查询处理产生影响,但对于现代RDBMS优化器,您将看不到任何差异。

这是一个替代查询,它采用略有不同的方式:

select a.*
  , children = isnull(c.children, 0)
from accounts a
  left join (select children = count(*)
               , id
               , id2
               , id3
               , custId
              from accounts
              where option1 is null and option2 <> 0
              group by id
               , id2
               , id3
               , custId) c
    on a.id = c.id
      and a.id2 = c.id2
      and a.id3 = c.id3
      and a.custId = c.custId
where (a.option1 = 'solo' or a.option2 = 0)
  and a.type = 'foo'

SQL Fiddle with demo

因此它分别使用count(*)LEFT JOIN代替count(1)OUTER APPLYOUTER APPLY会应用于查询之外的每一行;您可以将它用于函数,但它也可以帮助代码更简洁,就像在这种情况下一样。只是个人偏好我做出这些选择。