web-dev-qa-db-ja.com

DISTINCTを使用してパーティション関数COUNT()OVERが可能

私は次のように書いて、個別のNumUsersの合計を取得しようとしています:

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

管理スタジオはこれについてあまり満足していないようです。 DISTINCTキーワードを削除するとエラーは表示されなくなりますが、明確なカウントにはなりません。

DISTINCTは、パーティション関数内では使用できないようです。個別のカウントを見つけるにはどうすればよいですか?相関サブクエリなど、より多くのtraditionalメソッドを使用しますか?

これをもう少し検討すると、これらのOVER関数は、SQL-Serverで使用して合計を計算できないという点で、Oracleとは異なる動作をする可能性があります。

ここに SQLfiddle にライブ例を追加しました。ここでは、パーティション関数を使用して実行中の合計を計算しようとしています。

67
whytheq

dense_rank() を使用した非常に簡単なソリューションがあります

dense_rank() over (partition by [Mth] order by [UserAccountKey]) 
+ dense_rank() over (partition by [Mth] order by [UserAccountKey] desc) 
- 1

これにより、まさにあなたが求めていたものが得られます。各月内の個別のUserAccountKeysの数。

139
David

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が最適なオプションであると思います

5
GarethD

上記の David に似たソリューションを使用しますが、一部の行をカウントから除外する必要がある場合は、さらにひねりを加えます。これは、[UserAccountKey]がnullにならないことを前提としています。

-- 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

拡張された例を含むSQL Fiddleはここにあります。

4
Lars Rönnbäck

ネクロマンシング:

DENSE_RANKを介してMAXでPARTITION BYを介してCOUNT 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
3
Stefan Steiger