it-swarm.com.de

Warum keine window-Funktionen in where-Klauseln?

Der Titel sagt alles, warum kann ich keine Fensterfunktion in einer where-Klausel in SQL Server verwenden?

Diese Abfrage macht durchaus Sinn:

select id, sales_person_id, product_type, product_id, sale_amount
from Sales_Log
where 1 = row_number() over(partition by sales_person_id, product_type, product_id order by sale_amount desc)

Aber es geht nicht. Gibt es einen besseren Weg als eine CTE/Unterabfrage?

EDIT

Wofür es sich lohnt, ist die Abfrage mit einem CTE:

with Best_Sales as (
    select id, sales_person_id, product_type, product_id, sale_amount, row_number() over (partition by sales_person_id, product_type, product_id order by sales_amount desc) rank
    from Sales_log
)
select id, sales_person_id, product_type, product_id, sale_amount
from Best_Sales
where rank = 1

EDIT

+1 für die Antworten, die mit einer Unterabfrage angezeigt werden, aber ich suche wirklich nach den Gründen dafür, dass in Windows-Klauseln keine Windowing-Funktionen verwendet werden können.

40
Crisfole

warum kann ich eine Fensterfunktion in einer where-Klausel in SQL Server nicht verwenden?

Eine Antwort, wenn auch nicht besonders aufschlussreich, ist, dass die Spezifikation besagt, dass dies nicht möglich ist.

Siehe den Artikel von Itzik Ben Gan - Logische Abfrageverarbeitung: Was es ist und was es für Sie bedeutet und insbesondere das Bild hier . Fensterfunktionen werden zu dem Zeitpunkt ausgewertet, zu dem die Variable SELECT in der Ergebnismenge verbleibt, nachdem alle Klauseln WHERE/JOIN/GROUP BY/HAVING behandelt wurden (Schritt 5.1).

ich bin wirklich auf der Suche nach dem Grund, nicht in der Lage zu sein Windowing-Funktionen in where-Klauseln.

Der Grund, dass sie in der WHERE-Klausel nicht zulässig sind, besteht darin, dass dadurch Mehrdeutigkeit erzeugt wird. Itzik Ben Gans Beispiel aus Hochleistungs-T-SQL mithilfe von Fensterfunktionen stehlen (S.25)

Angenommen, Ihr Tisch war 

CREATE TABLE T1
(
col1 CHAR(1) PRIMARY KEY
)

INSERT INTO T1 VALUES('A'),('B'),('C'),('D'),('E'),('F')

Und deine Frage

SELECT col1
FROM T1
WHERE ROW_NUMBER() OVER (ORDER BY col1) <= 3
AND col1 > 'B'

Was wäre das richtige Ergebnis? Erwarten Sie, dass das Prädikat col1 > 'B' vor oder nach der Zeilennummerierung ausgeführt wurde?

52
Martin Smith

Es besteht keine Notwendigkeit für CTE, verwenden Sie einfach die Fensterfunktion in einer Unterabfrage:

select id, sales_person_id, product_type, product_id, sale_amount
from
(
  select id, sales_person_id, product_type, product_id, sale_amount,
    row_number() over(partition by sales_person_id, product_type, product_id order by sale_amount desc) rn
  from Sales_Log
) sl
where rn = 1

Bearbeiten, meinen Kommentar zur Antwort verschieben. 

Fensterfunktionen werden nicht ausgeführt, bis die Daten tatsächlich ausgewählt sind, die sich nach der WHERE-Klausel befinden. Wenn Sie also versuchen, einen row_number in einer WHERE-Klausel zu verwenden, ist der Wert noch nicht zugewiesen. 

8
Taryn

Zunächst einmal heißt es all-at-once operation

"All-at-Once-Operationen" bedeutet, dass alle Ausdrücke in derselben logischen Abfrageprozessphase gleichzeitig logisch ausgewertet werden.

Und tolles Kapitel Auswirkungen auf Fensterfunktionen :

Angenommen, Sie haben:

CREATE TABLE #Test ( Id INT) ;

INSERT  INTO #Test VALUES  ( 1001 ), ( 1002 ) ;

SELECT Id
FROM #Test
WHERE Id = 1002
  AND ROW_NUMBER() OVER(ORDER BY Id) = 1;

All-at-Once-Vorgänge teilen uns diese beiden Bedingungen mit, die zum gleichen Zeitpunkt logisch ausgewertet wurden. Daher kann SQL Server Bedingungen in der WHERE-Klausel beliebig auswerten Auftrag, basierend auf dem geschätzten Ausführungsplan. Die Hauptfrage hier ist also, welche Bedingung zuerst bewertet wird.

Fall 1:

If ( Id = 1002 ) is first, then if ( ROW_NUMBER() OVER(ORDER BY Id) = 1 )

Ergebnis: 1002

Fall 2:

If ( ROW_NUMBER() OVER(ORDER BY Id) = 1 ), then check if ( Id = 1002 )

Ergebnis: leer

Wir haben also ein Paradoxon.

Dieses Beispiel zeigt, warum wir in der WHERE-Klausel keine Fensterfunktionen verwenden können. Sie können mehr darüber nachdenken und herausfinden, warum Fensterfunktionen nur in [~ # ~] verwendet werden dürfen. Wählen Sie [~ # ~] und ORDER BY Klauseln!


Nachtrag

Terradata unterstützt die QUALIFY -Klausel:

Filtert die Ergebnisse einer zuvor berechneten geordneten Analysefunktion nach benutzerdefinierten Suchbedingungen.

SELECT Id
FROM #Test
WHERE Id = 1002
QUALIFY ROW_NUMBER() OVER(ORDER BY Id) = 1;
7
Lukasz Szozda

Sie müssen nicht unbedingt einen CTE verwenden, Sie können die Ergebnismenge nach der Verwendung von row_number () abfragen

select row, id, sales_person_id, product_type, product_id, sale_amount
from (
    select
        row_number() over(partition by sales_person_id, 
            product_type, product_id order by sale_amount desc) AS row,
        id, sales_person_id, product_type, product_id, sale_amount
    from Sales_Log 
    ) a
where row = 1
3
Khan

Schließlich gibt es noch den altertümlichen Weg vor SQL Server 2005 mit einer zugehörigen Unterabfrage:

select *
from   Sales_Log sl
where  sl.id = (
    Select Top 1 id
    from   Sales_Log sl2
    where  sales_person_id = sl.sales_person_id
       and product_type = sl.product_type
       and product_id = sl.product_id
    order by sale_amount desc
)

Ich gebe Ihnen das nur zur Vollständigkeit. 

1
Ann L.

Grundsätzlich wird die erste "WHERE" -Klauselbedingung von sql gelesen und die gleiche Spalten-/Wert-ID in die Tabelle hineingeschaut, aber in Tabelle ist row_num = 1 noch nicht vorhanden. Daher wird es nicht funktionieren .. Das ist der Grund, warum wir zuerst Klammern verwenden und danach die WHERE-Klausel schreiben werden.

1
Ayush Garg

Es ist ein alter Thread, aber ich werde versuchen, die im Thema formulierte Frage zu beantworten.

Warum keine window-Funktionen in where-Klauseln?

Die Anweisung SELECT hat die folgenden in Reihenfolge der Eingabeangegebenen Hauptklauseln: _:

SELECT DISTINCT TOP list
FROM  JOIN ON / APPLY / PIVOT / UNPIVOT
WHERE
GROUP BY  WITH CUBE / WITH ROLLUP
HAVING
ORDER BY
OFFSET-FETCH

Logical Query-Verarbeitungsreihenfolge oder Bindungsreihenfolge ist konzeptionelle Interpretationsreihenfolge, sie definiert die Korrektheit der Abfrage. Diese Reihenfolge bestimmt, wann die in einem Schritt definierten Objekte den Klauseln in nachfolgenden Schritten zur Verfügung gestellt werden.

----- Relational result
  1. FROM
    1.1. ON JOIN / APPLY / PIVOT / UNPIVOT
  2. WHERE
  3. GROUP BY
    3.1. WITH CUBE / WITH ROLLUP
  4. HAVING
  ---- After the HAVING step the Underlying Query Result is ready
  5. SELECT
    5.1. SELECT list
    5.2. DISTINCT
----- Relational result

----- Non-relational result (a cursor)
  6. ORDER BY
  7. TOP / OFFSET-FETCH
----- Non-relational result (a cursor)

Wenn der Abfrageprozessor beispielsweise auf die in der Klausel FROM definierten Tabellen oder Ansichten zugreifen kann (zugreifen) kann, werden diese Objekte und ihre Spalten allen nachfolgenden Schritten zur Verfügung gestellt.

Umgekehrt können alle Klauseln vor der SELECT-Klausel nicht auf Spaltenaliasnamen oder abgeleitete Spalten verweisen, die in der SELECT-Klausel definiert sind. Auf diese Spalten kann jedoch durch nachfolgende Klauseln wie die ORDER BY-Klausel verwiesen werden.

Die Klausel OVER bestimmt die Partitionierung und Reihenfolge einer Zeilengruppe, bevor die zugehörige Fensterfunktion angewendet wird. Das heißt, die OVER-Klausel definiert ein Fenster oder einen benutzerdefinierten Satz von Zeilen innerhalb eines zugrunde liegendes Abfrageergebnis -Satz, und die Fensterfunktion berechnet das Ergebnis gegen dieses Fenster.

Msg 4108, Level 15, State 1, …
Windowed functions can only appear in the SELECT or ORDER BY clauses.

Der Grund liegt darin, dass Logical Query Processing in T-SQL funktioniert. Da das zugrunde liegende Abfrageergebnis nur festgelegt wird, wenn die logische Abfrageverarbeitung den Schritt SELECT erreicht, Schritt 5.1. (Das heißt, nach der Verarbeitung der Schritte FROM, WHERE, GROUP BY und HAVING) sind Fensterfunktionen nur in den Klauseln SELECT und ORDER BY der Abfrage zulässig. 

Beachten Sie, dass Fensterfunktionen immer noch Teil der relationalen Ebene sind, selbst wenn das relationale Modell nicht mit geordneten Daten arbeitet. Das Ergebnis nach dem Schritt SELECT 5.1. Bei jedem Fenster ist die Funktion noch relational.

Genau genommen ist der Grund, warum Fensterfunktionen in der WHERE-Klausel nicht zulässig sind, nicht deshalb, weil dadurch Mehrdeutigkeiten erzeugt würden, sondern weil die Reihenfolge, in der Logical Query Processingdie Anweisung SELECT in T-SQL verarbeitet.

Links: hier , hier und hier

1
drumsta

Ja, wenn Sie eine Window-Funktion ausführen, wird SQL leider wütend auf Sie, auch wenn Ihr Prädikat dort legitim ist. Sie machen einen Cte- oder Nested-Select mit dem Wert in Ihrer SELECT-Anweisung. Anschließend referenzieren Sie Ihren CTE oder Nested-Select mit diesem Wert später. Ein einfaches Beispiel, das selbsterklärend sein sollte. Wenn Sie wirklich für einige Leistungsprobleme hassen, wenn Sie einen großen Datensatz ausführen, können Sie immer auf temporäre Tabellen- oder Tabellenvariablen zurückgreifen.

declare @Person table ( PersonID int identity, PersonName varchar(8));

insert into @Person values ('Brett'),('John');

declare @Orders table ( OrderID int identity, PersonID int, OrderName varchar(8));

insert into @Orders values (1, 'Hat'),(1,'Shirt'),(1, 'Shoes'),(2,'Shirt'),(2, 'Shoes');

--Select
--  p.PersonName
--, o.OrderName
--, row_number() over(partition by o.PersonID order by o.OrderID)
--from @Person p 
--  join @Orders o on p.PersonID = o.PersonID
--where row_number() over(partition by o.PersonID order by o.orderID) = 2

-- yields:
--Msg 4108, Level 15, State 1, Line 15
--Windowed functions can only appear in the SELECT or ORDER BY clauses.
;

with a as 
    (
    Select
    p.PersonName
,   o.OrderName
,   row_number() over(partition by o.PersonID order by o.OrderID) as rnk
from @Person p 
    join @Orders o on p.PersonID = o.PersonID
    )
select *
from a 
where rnk >= 2 -- only orders after the first one.
1
djangojazz