web-dev-qa-db-fra.com

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

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.

18
Omri

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 la 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 transtypage en NUMERIC est 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 vous préférez (ou simplement ON M.B = T.B Si la colonne B n'est pas nullable).

12
Martin Smith

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

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_value pour 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
7
Mikael Eriksson

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;
5
Rob Farley

Pour l'instant, SQL Server ne permet pas d'utiliser Distinct avec des fonctions fenêtrées.

Mais une fois que vous vous souvenez du fonctionnement des fonctions fenêtrées (c'est-à-dire qu'elles sont appliquées au jeu de résultats de la requête), vous pouvez contourner cela:

select B,
min(count(distinct A)) over (partition by B) / max(count(*)) over() as A_B
from MyTable
group by B
0
AcePL