Verwenden von DISTINCT in der Fensterfunktion mit OVER

18

Ich versuche, eine Abfrage von Oracle nach SQL Server 2014 zu migrieren.

Hier ist meine Abfrage, die in Oracle funktioniert:

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

Hier ist der Fehler, den ich bekommen habe, nachdem ich versucht habe, diese Abfrage in SQL Server 2014 auszuführen.

Use of DISTINCT is not allowed with the OVER clause

Weiß jemand was das Problem ist? Ist so eine Art Abfrage in SQL Server möglich? Bitte beraten.

Omri
quelle
Benötigen Sie tatsächlich eine Zeile im Ergebnis für jede Zeile in MyTable? Oder sind einzelne Zeilen genug? Und Sie müssen den Fehler der Division durch Null nicht berücksichtigen, wenn keine Zeilen enthalten sind MyTable?
Erwin Brandstetter

Antworten:

12

Weiß jemand was das Problem ist? Ist so eine Art Abfrage in SQL Server möglich?

Nein, es ist derzeit nicht implementiert. Siehe folgende Verbindungsanfrage.

OVER-Klausel-Erweiterungsanforderung - DISTINCT-Klausel für Aggregatfunktionen

Eine andere mögliche Variante wäre

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) 

Die Umwandlung in NUMERICist da, um eine Ganzzahldivision zu vermeiden. Der Grund für die Join-Klausel wird hier erläutert .

Es kann bei Bedarf durch ersetzt ON M.B = T.B OR (M.B IS NULL AND T.B IS NULL)werden (oder einfach, ON M.B = T.Bwenn die BSpalte nicht nullwertfähig ist).

Martin Smith
quelle
14

Dies ergibt die eindeutige Anzahl (*) für A, dividiert durch B:

dense_rank() over (partition by B order by A) 
+ dense_rank() over (partition by B order by A desc) 
- 1
Ben
quelle
3
Interessante Lösung. Ich nehme an, es sollte einen Haftungsausschluss geben, dass es funktioniert, wenn Aes nur nicht nullbar ist (da ich denke, dass es auch nulls zählt).
Ypercubeᵀᴹ
Das sollte abs(dense_rank - dense_rank) + 1ich glauben.
norcalli
7

Sie können den Maximalwert von verwenden dense_rank(), um die eindeutige Anzahl von A abzurufen, die durch B partitioniert ist.

Um sich um den Fall zu kümmern, dass A Nullwerte haben kann, können Sie first_valueherausfinden, ob in der Partition eine Null vorhanden ist oder nicht, und dann 1 abziehen, wenn dies von Martin Smith im Kommentar vorgeschlagen wurde.

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
Mikael Eriksson
quelle
5

Versuchen Sie, eine Unterabfrage zu erstellen, die nach A, B gruppiert und die Anzahl einschließt. In Ihrer äußeren Abfrage wird Ihre Zählung (distinct) zu einer regulären Zählung und Ihre Zählung (*) wird zu einer Summe (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;
Rob Farley
quelle