SQL 在分区上计数不同

sar*_*a92 11 sql-server count window-functions

我有一个包含两列的表,我想计算 Col_B 上(由)Col_A 上的不同值。

我的表

Col_A | Col_B 
A     | 1
A     | 1
A     | 2
A     | 2
A     | 2
A     | 3
b     | 4
b     | 4
b     | 5
Run Code Online (Sandbox Code Playgroud)

预期结果

Col_A   | Col_B | Result
A       | 1     | 3
A       | 1     | 3
A       | 2     | 3
A       | 2     | 3
A       | 2     | 3
A       | 3     | 3
b       | 4     | 2
b       | 4     | 2
b       | 5     | 2
Run Code Online (Sandbox Code Playgroud)

我尝试了以下代码

select *, 
count (distinct col_B) over (partition by col_A) as 'Result'
from MyTable
Run Code Online (Sandbox Code Playgroud)

计数(不同的 col_B)不起作用。如何重写计数函数来计算不同的值?

Eri*_*ing 19

这就是我要做的:

SELECT      *
FROM        #MyTable AS mt
CROSS APPLY (   SELECT COUNT(DISTINCT mt2.Col_B) AS dc
                FROM   #MyTable AS mt2
                WHERE  mt2.Col_A = mt.Col_A
                -- GROUP BY mt2.Col_A 
            ) AS ca;
Run Code Online (Sandbox Code Playgroud)

GROUP BY鉴于问题中提供的数据,该子句是多余的,但可能会为您提供更好的执行计划。见后续问答CROSS APPLY 产生外连接

如果您希望将该功能添加到 SQL Server,请考虑投票支持OVER 子句增强请求 -反馈站点上聚合函数的 DISTINCT 子句


And*_*y M 7

在某种程度上,这是Lennart 解决方案的扩展,但它太丑了,我不敢建议将其作为编辑。这里的目标是在没有派生表的情况下获得结果。可能永远不需要这样做,再加上查询的丑陋,整个努力似乎是一种浪费。不过,我仍然想将此作为练习,现在想分享我的结果:

SELECT
  Col_A,
  Col_B,
  DistinctCount = DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
                + DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
                - 1
                - CASE COUNT(Col_B) OVER (PARTITION BY Col_A)
                  WHEN COUNT(  *  ) OVER (PARTITION BY Col_A)
                  THEN 0
                  ELSE 1
                  END
FROM
  dbo.MyTable
;
Run Code Online (Sandbox Code Playgroud)

计算的核心部分是这样的(我首先要注意这个想法不是我的,我在别处学到了这个技巧):

  DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
+ DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
- 1
Run Code Online (Sandbox Code Playgroud)

如果Col_B保证中的值永远不会有空值,则可以不加任何更改地使用此表达式。但是,如果该列可以有空值,则您需要考虑到这一点,而这正是CASE表达式的用途。它将每个分区的行数与每个分区的Col_B数进行比较。如果数字不同,则意味着某些行中有空值Col_B,因此DENSE_RANK() ... + DENSE_RANK() - 1需要将初始计算 ( ) 减 1。

请注意,因为- 1是核心公式的一部分,所以我选择保留它。但是,它实际上可以合并到CASE表达式中,以徒劳的尝试使整个解决方案看起来不那么难看:

SELECT
  Col_A,
  Col_B,
  DistinctCount = DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B ASC )
                + DENSE_RANK() OVER (PARTITION BY Col_A ORDER BY Col_B DESC)
                - CASE COUNT(Col_B) OVER (PARTITION BY Col_A)
                  WHEN COUNT(  *  ) OVER (PARTITION BY Col_A)
                  THEN 1
                  ELSE 2
                  END
FROM
  dbo.MyTable
;
Run Code Online (Sandbox Code Playgroud)

这个现场演示dbfiddle 徽标db<>fiddle.uk 可用于测试解决方案的两种变体。


Len*_*art 6

您可以使用 模拟它dense_rank,然后为每个分区选择最大等级:

select col_a, col_b, max(rnk) over (partition by col_a)
from (
    select col_a, col_b
        , dense_rank() over (partition by col_A order by col_b) as rnk 
    from #mytable
) as t    
Run Code Online (Sandbox Code Playgroud)

您需要从 中排除任何空值col_b以获得与COUNT(DISTINCT).