Utilisation de DISTINCT dans la fonction de fenêtre avec OVER


18

J'essaie de migrer une requête d'Oracle vers SQL Server 2014.

Voici ma requête qui fonctionne très bien dans Oracle:

select
count(distinct A) over (partition by B) / count(*) over() as A_B
from MyTable 

Voici l'erreur que j'ai reçue après avoir essayé d'exécuter cette requête dans SQL Server 2014.

Use of DISTINCT is not allowed with the OVER clause

Quelqu'un sait quel est le problème? Un tel type de requête est-il possible dans SQL Server? S'il vous plaît donnez votre avis.


Avez-vous réellement besoin d'une ligne dans le résultat pour chaque ligne MyTable? Ou les lignes distinctes sont-elles suffisantes? Et vous n'avez pas besoin de considérer la division par zéro erreur s'il n'y a pas de lignes MyTable?
Erwin Brandstetter

Réponses:


12

Quelqu'un sait quel est le problème? Un tel type de requête est-il possible dans SQL Server?

Non, il n'est pas actuellement implémenté. Voir la demande d'élément de connexion suivante.

Demande d'amélioration de clause OVER - clause DISTINCT pour les fonctions d'agrégation

Une autre variante possible serait

SELECT M.A,
       M.B,
       T.A_B
FROM   MyTable M
       JOIN (SELECT CAST(COUNT(DISTINCT A) AS NUMERIC(18,8)) / SUM(COUNT(*)) OVER() AS A_B,
                    B
             FROM   MyTable
             GROUP  BY B) T
         ON EXISTS (SELECT M.B INTERSECT SELECT T.B) 

le casting vers NUMERICest là pour éviter la division entière. La raison de la clause join est expliquée ici .

Il peut être remplacé par ON M.B = T.B OR (M.B IS NULL AND T.B IS NULL)si préféré (ou simplement ON M.B = T.Bsi la Bcolonne n'est pas nullable).


14

Cela donne le nombre distinct (*) pour A partitionné par B:

dense_rank() over (partition by B order by A) 
+ dense_rank() over (partition by B order by A desc) 
- 1

3
Solution intéressante. Je suppose qu'il devrait avoir une clause de non-responsabilité selon laquelle il fonctionne Auniquement lorsqu'il est non annulable (car je pense qu'il compte également les null).
ypercubeᵀᴹ

Ça devrait être abs(dense_rank - dense_rank) + 1je crois.
norcalli

7

Vous pouvez prendre la valeur maximale de dense_rank()pour obtenir le nombre distinct de A partitionné par B.

Pour prendre en charge le cas où A peut avoir des valeurs nulles, vous pouvez utiliser first_valuepour déterminer si un null est présent dans la partition ou non, puis soustraire 1 s'il est tel que suggéré par Martin Smith dans le commentaire.

select (max(T.DenseRankA) over(partition by T.B) - 
          cast(iif(T.FirstA is null, 1, 0) as numeric(18, 8))) / T.TotalCount as A_B
from (
     select dense_rank() over(partition by T.B order by T.A) DenseRankA,
            first_value(T.A) over(partition by T.B order by T.A) as FirstA,
            count(*) over() as TotalCount,
            T.A,
            T.B
     from MyTable as T
     ) as T

5

Essayez de faire une sous-requête, en regroupant par A, B et en incluant le nombre. Ensuite, dans votre requête externe, votre nombre (distinct) devient un nombre normal et votre nombre (*) devient une somme (cnt).

select
count(A) over (partition by B) * 1.0 / 
    sum(cnt) over() as A_B
from
(select A, B, count(*) as cnt
 from MyTable
 group by A, B) as partial;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.