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

why*_*heq 69 sql t-sql sql-server sql-server-2008-r2 sql-server-2014

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

NumUsers = COUNT(DISTINCT [UserAccountKey]) OVER (PARTITION BY [Mth])
Run Code Online (Sandbox Code Playgroud)

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

DISTINCT在分区函数中似乎不可能.我如何寻找独特的计数?我是否使用更传统的方法,例如相关子查询?

进一步研究一下,也许这些OVER函数与Oracle的工作方式不同,它们不能用于SQL-Server计算运行总计.

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

小智 142

有一个非常简单的解决方案 dense_rank()

dense_rank() over (partition by [Mth] order by [UserAccountKey]) 
+ dense_rank() over (partition by [Mth] order by [UserAccountKey] desc) 
- 1
Run Code Online (Sandbox Code Playgroud)

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

  • 使用`dense_rank()`时要注意的一件事是它会计算NULL,而`COUNT(field)OVER`则不计算.由于这个原因,我不能在我的解决方案中使用它,但我仍然认为它非常聪明. (19认同)
  • @ bf2020,如果`UserAccountKey`中可能有'NULL`值,那么你需要添加这个术语:`-MAX(当UserAccountKey为NULL,然后是1 ELSE 0 END)时(例如,按Mth划分)`.想法取自LarsRönnbäck的答案.基本上,如果`UserAccountKey`具有'NULL`值,则需要从结果中减去额外的`1`,因为`DENSE_RANK`计数NULL. (4认同)

Ste*_*ger 7

死灵法术:

通过 DENSE_RANK 模拟 COUNT DISTINCT over PARTITION BY 和 MAX 相对简单:

;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
Run Code Online (Sandbox Code Playgroud)

注意:
这假设有问题的字段是不可为空的字段。
如果字段中有一个或多个 NULL 条目,则需要减 1。


Gar*_*thD 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;
Run Code Online (Sandbox Code Playgroud)

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

SELECT  datekey,
        SUM(Amount) OVER(ORDER BY DateKey) AS RunningTotal
FROM    document
Run Code Online (Sandbox Code Playgroud)

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


Lar*_*äck 5

我使用的解决方案与上述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
Run Code Online (Sandbox Code Playgroud)

可以在此处找到带有扩展示例的 SQL Fiddle。

  • 当“UserAccountKey”可以是“NULL”时,您的想法可用于使原始公式(没有您在答案中谈论的“[Include]”的复杂性)与“dense_rank()”一起工作。将此术语添加到公式中:`-MAX(CASE WHEN UserAccountKey IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY Mth)`。 (2认同)