web-dev-qa-db-fra.com

Partition Fonction COUNT () OVER possible avec DISTINCT

J'essaie d'écrire ce qui suit afin d'obtenir un total cumulé d'utilisateurs NumU distincts, comme ceci:

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

Le studio de gestion ne semble pas trop heureux à ce sujet. L'erreur disparaît lorsque je supprime le mot clé DISTINCT, mais il ne s'agira pas d'un compte distinct.

DISTINCT ne semble pas être possible dans les fonctions de partition. Comment puis-je trouver le nombre distinct? Est-ce que j'utilise une méthode plus traditionnelle telle qu'une sous-requête corrélée?

En regardant cela un peu plus loin, peut-être que ces fonctions OVER fonctionnent différemment de Oracle car elles ne peuvent pas être utilisées dans SQL-Server pour calculer les totaux cumulés.

J'ai ajouté un exemple en direct ici sur SQLfiddle où je tente d'utiliser une fonction de partition pour calculer un total cumulé.

67
whytheq

Il existe une solution très simple utilisant dense_rank()

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

Cela vous donnera exactement ce que vous demandiez: le nombre de UserAccountKeys distincts au cours de chaque mois.

139
David

Je pense que la seule façon de faire cela dans SQL-Server 2008R2 est d'utiliser une sous-requête corrélée, ou une application externe:

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;

Cela peut être fait dans SQL-Server 2012 en utilisant la syntaxe que vous avez suggérée:

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

Cependant, l'utilisation de DISTINCT n'est toujours pas autorisée. Par conséquent, si DISTINCT est requis et/ou si la mise à niveau n'est pas une option, alors je pense que OUTER APPLY est votre meilleure option

5
GarethD

J'utilise une solution similaire à celle de David ci-dessus, mais avec une torsion supplémentaire si certaines lignes doivent être exclues du décompte. Cela suppose que [UserAccountKey] n'est jamais 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

n SQL Fiddle avec un exemple étendu peut être trouvé ici.

4
Lars Rönnbäck

Nécromancie:

Il est relativement simple d'émuler un COUNT DISTINCT sur PARTITION BY avec MAX via DENSE_RANK:

;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