J'essaye d'écrire ce qui suit afin d'obtenir un total courant de NumUsers 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 DISTINCT
mot - clé, mais ce ne sera pas un décompte distinct.
DISTINCT
ne semble pas possible dans les fonctions de partition. Comment puis-je trouver le décompte distinct? Dois-je utiliser une méthode plus traditionnelle telle qu'une sous-requête corrélée?
En examinant cela un peu plus loin, peut-être que ces OVER
fonctions fonctionnent différemment d'Oracle dans la mesure où elles ne peuvent pas être utilisées SQL-Server
pour calculer les totaux cumulés.
J'ai ajouté un exemple en direct ici sur SQLfiddle où j'essaie d'utiliser une fonction de partition pour calculer un total en cours.
la source
COUNT
avecORDER BY
au lieu dePARTITION BY
est mal défini en 2008. Je suis surpris que cela vous laisse du tout. Selon la documentation , vous n'êtes pas autorisé à utiliser uneORDER BY
fonction d'agrégation.Réponses:
Il existe une solution très simple utilisant
dense_rank()
Cela vous donnera exactement ce que vous demandiez: le nombre de UserAccountKeys distinctes dans chaque mois.
la source
dense_rank()
est qu'il comptera les NULL alors que ceCOUNT(field) OVER
n'est pas le cas. Je ne peux pas l'utiliser dans ma solution à cause de cela, mais je pense toujours que c'est assez intelligent.NULL
valeurs dans leUserAccountKey
, alors vous devez ajouter ce terme:-MAX(CASE WHEN UserAccountKey IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY Mth)
. L'idée est tirée de la réponse de LarsRönnbäck ci-dessous. Essentiellement, siUserAccountKey
a desNULL
valeurs, vous devez soustraire un supplément1
du résultat, carDENSE_RANK
compte NULL.dense_rank
solution lorsque la fonction de fenêtre a un cadre. SQL Server n'autorise pas l'dense_rank
utilisation avec un cadre de fenêtre: stackoverflow.com/questions/63527035/…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
Remarque:
cela suppose que les champs en question sont des champs NON Nullables.
S'il y a une ou plusieurs entrées NULL dans les champs, vous devez soustraire 1.
la source
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 nul.
-- 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
Un SQL Fiddle avec un exemple étendu peut être trouvé ici.
la source
[Include]
ce dont vous parlez dans votre réponse) avec ledense_rank()
travail quandUserAccountKey
peut êtreNULL
. Ajouter ce terme à la formule:-MAX(CASE WHEN UserAccountKey IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY Mth)
.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, donc si DISTINCT est requis et / ou si la mise à niveau n'est pas une option, je pense queOUTER APPLY
c'est votre meilleure optionla source