分区函数COUNT()可以使用DISTINCT

时间:2012-06-26 07:48:12

标签: sql sql-server tsql sql-server-2008-r2 sql-server-2014

我正在尝试编写以下内容,以便获得不同的NumUsers,如下所示:

NumUsers = COUNT(DISTINCT [UserAccountKey]) OVER (PARTITION BY [Mth])

管理工作室对此并不感到高兴。我删除DISTINCT关键字时错误消失了,但它不会是一个明显的计数。

分区函数中似乎无法使用

DISTINCT。 我如何寻找独特的计数?我是否使用更多传统方法,例如相关子查询?

进一步研究一下,这些OVER函数可能与Oracle在SQL-Server中无法用于计算运行总计的方式不同。

我在SQLfiddle添加了一个实例,我尝试使用分区函数计算运行总数。

4 个答案:

答案 0 :(得分:130)

使用dense_rank()

有一个非常简单的解决方案
dense_rank() over (partition by [Mth] order by [UserAccountKey]) 
+ dense_rank() over (partition by [Mth] order by [UserAccountKey] desc) 
- 1

这将为您提供您所要求的内容:每个月内不同UserAccountKeys的数量。

答案 1 :(得分:5)

我认为在SQL-Server 2008R2中执行此操作的唯一方法是使用相关子查询或外部应用:

SELECT  datekey,
        COALESCE(RunningTotal, 0) AS RunningTotal,
        COALESCE(RunningCount, 0) AS RunningCount,
        COALESCE(RunningDistinctCount, 0) AS RunningDistinctCount
FROM    document
        OUTER APPLY
        (   SELECT  SUM(Amount) AS RunningTotal,
                    COUNT(1) AS RunningCount,
                    COUNT(DISTINCT d2.dateKey) AS RunningDistinctCount
            FROM    Document d2
            WHERE   d2.DateKey <= document.DateKey
        ) rt;

可以使用您建议的语法在SQL-Server 2012中完成此操作:

SELECT  datekey,
        SUM(Amount) OVER(ORDER BY DateKey) AS RunningTotal
FROM    document

但是,仍然不允许使用DISTINCT,因此如果需要DISTINCT和/或如果升级不是一个选项,那么我认为OUTER APPLY是您的最佳选择

答案 2 :(得分:4)

我使用的解决方案类似于上面David的解决方案,但如果应从计数中排除某些行,则会额外扭曲。这假设[UserAccountKey]永远不为空。

-- subtract an extra 1 if null was ranked within the partition,
-- which only happens if there were rows where [Include] <> 'Y'
dense_rank() over (
  partition by [Mth] 
  order by case when [Include] = 'Y' then [UserAccountKey] else null end asc
) 
+ dense_rank() over (
  partition by [Mth] 
  order by case when [Include] = 'Y' then [UserAccountKey] else null end desc
)
- max(case when [Include] = 'Y' then 0 else 1 end) over (partition by [Mth])
- 1

An SQL Fiddle with an extended example can be found here.

答案 3 :(得分:3)

Necromancing:

使用MAX通过DENSE_RANK模拟COTI DISTINCT而非相对简单:

;WITH baseTable AS
(
    SELECT 'RM1' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM1' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR3' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR
    UNION ALL SELECT 'RM3' AS RM, 'ADR2' AS ADR
)
,CTE AS
(
    SELECT RM, ADR, DENSE_RANK() OVER(PARTITION BY RM ORDER BY ADR) AS dr 
    FROM baseTable
)
SELECT
     RM
    ,ADR

    ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY ADR) AS cnt1 
    ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM) AS cnt2 
    -- Not supported
    --,COUNT(DISTINCT CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY CTE.ADR) AS cntDist
    ,MAX(CTE.dr) OVER (PARTITION BY CTE.RM ORDER BY CTE.RM) AS cntDistEmu 
FROM CTE