1

可以说我有桌子:

帐户:id、id2、id3、custId、option1、option2、类型

并且同一张表有所有帐户,其中一些是“父”帐户(可以是没有孩子的“独奏”帐户)和一些是“儿童”帐户(我知道设计不好,但这就是我正在使用的)。

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

我想用特定的custIdand查询“父母”或“独奏”帐户的列表type,这很容易通过以下方式完成:

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

where'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)

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

4

1 回答 1

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 与演示

评论后编辑:

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

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

基本上,对于每个非单独行,我们将其与任何子行(即c.option2 <> 0)进行匹配并获取这些行的计数,当现在有匹配项或它是“单独”父行时显式返回 0。

由于我们只匹配非单独的父行,我们用 过滤它们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 与演示

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

于 2013-04-25T21:20:15.937 回答