Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Oracle-Schlüsselwörter „Partition By“ und „Row_Number“.

PARTITION BY Sätze trennen, dies ermöglicht es Ihnen, unabhängig voneinander an verwandten Sätzen zu arbeiten (ROW_NUMBER(),COUNT(),SUM(),etc).

In Ihrer Abfrage besteht der zugehörige Satz aus Zeilen mit ähnlichem cdt.country_code, cdt.account, cdt.currency. Wenn Sie diese Spalten partitionieren und ROW_NUMBER darauf anwenden. Diese anderen Spalten in dieser Kombination/diesem Satz erhalten eine fortlaufende Nummer von ROW_NUMBER

Aber diese Abfrage ist lustig, wenn Sie Ihre Partition mit einigen eindeutigen Daten versehen und eine row_number darauf setzen, wird sie nur dieselbe Nummer erzeugen. Es ist, als würdest du ein ORDER BY auf einer Partition ausführen, die garantiert eindeutig ist. Stellen Sie sich die GUID beispielsweise als eindeutige Kombination aus cdt.country_code, cdt.account, cdt.currency vor

newid() erzeugt GUID, was können Sie also von diesem Ausdruck erwarten?

select
   hi,ho,
   row_number() over(partition by newid() order by hi,ho)
from tbl;

... Richtig, alle partitionierten (keine wurde partitioniert, jede Zeile ist in ihrer eigenen Zeile partitioniert) row_numbers der Zeilen sind alle auf 1 gesetzt

Grundsätzlich sollten Sie auf nicht eindeutige Spalten partitionieren. ORDER BY auf OVER benötigte PARTITION BY, um eine nicht eindeutige Kombination zu haben, sonst werden alle row_numbers zu 1

Ein Beispiel, das sind Ihre Daten:

create table tbl(hi varchar, ho varchar);

insert into tbl values
('A','X'),
('A','Y'),
('A','Z'),
('B','W'),
('B','W'),
('C','L'),
('C','L');

Dann ist dies analog zu Ihrer Anfrage:

select
   hi,ho,
   row_number() over(partition by hi,ho order by hi,ho)
from tbl;

Was wird dabei herauskommen?

HI  HO  COLUMN_2
A   X   1
A   Y   1
A   Z   1
B   W   1
B   W   2
C   L   1
C   L   2

Siehst du die Kombination von HI HO? Die ersten drei Zeilen haben eine eindeutige Kombination, daher werden sie auf 1 gesetzt, die B-Zeilen haben das gleiche W, daher unterschiedliche ROW_NUMBERS, ebenso wie die HI-C-Zeilen.

Nun, warum ist ORDER BY dort benötigt? Wenn der vorherige Entwickler ähnliche Daten lediglich mit einer Zeilennummer versehen möchte (z. B. HI B, alle Daten sind B-W, B-W), kann er dies einfach tun:

select
   hi,ho,
   row_number() over(partition by hi,ho)
from tbl;

Aber leider erlaubt Oracle (und auch SQL Server) keine Partition ohne ORDER BY; wohingegen in Postgresql ORDER BY on PARTITION ist optional:http://www.sqlfiddle.com/#!1/27821/1

select
   hi,ho,
   row_number() over(partition by hi,ho)
from tbl;

Ihr ORDER BY auf Ihrer Partition sieht etwas überflüssig aus, nicht wegen des Fehlers des vorherigen Entwicklers, einige Datenbanken erlauben einfach keine PARTITION ohne ORDER BY , ist er möglicherweise nicht in der Lage, eine gute Kandidatenspalte zum Sortieren zu finden. Wenn sowohl PARTITION BY-Spalten als auch ORDER BY-Spalten identisch sind, entfernen Sie einfach ORDER BY, aber da einige Datenbanken dies nicht zulassen, können Sie einfach Folgendes tun:

SELECT cdt.*,
        ROW_NUMBER ()
        OVER (PARTITION BY cdt.country_code, cdt.account, cdt.currency
              ORDER BY newid())
           seq_no
   FROM CUSTOMER_DETAILS cdt

Sie finden keine gute Spalte zum Sortieren ähnlicher Daten? Sie können auch nach dem Zufallsprinzip sortieren, die partitionierten Daten haben die gleichen Werte ohnehin. Sie können zum Beispiel GUID verwenden (Sie verwenden newid() für SQL-Server). Das hat also die gleiche Ausgabe wie der vorherige Entwickler, es ist bedauerlich, dass einige Datenbanken PARTITION nicht zulassen ohne ORDER BY

Obwohl es mir wirklich entgeht und ich keinen guten Grund finde, denselben Kombinationen eine Zahl zu geben (B-W, B-W im obigen Beispiel). Es erweckt den Eindruck einer Datenbank mit redundanten Daten. Erinnerte mich irgendwie daran:Wie bekomme ich einen eindeutigen Datensatz aus derselben Liste von Datensätzen aus der Tabelle? Keine Unique-Einschränkung in der Tabelle

Es sieht wirklich obskur aus, eine PARTITION BY mit der gleichen Kombination von Spalten wie ORDER BY zu sehen, kann nicht einfach auf die Absicht des Codes schließen.

Live-Test:http://www.sqlfiddle.com/#!3/27821/6

Aber wie dbaseman auch bemerkt hat, ist es sinnlos, nach denselben Spalten zu partitionieren und zu sortieren.

Sie haben einen Datensatz wie diesen:

create table tbl(hi varchar, ho varchar);

insert into tbl values
('A','X'),
('A','X'),
('A','X'),
('B','Y'),
('B','Y'),
('C','Z'),
('C','Z');

Dann PARTITION BY hi,ho; und dann ORDER BY hi,ho. Es macht keinen Sinn, ähnliche Daten zu nummerieren :-) http://www.sqlfiddle.com/#!3/29ab8/3

select
   hi,ho,
   row_number() over(partition by hi,ho order by hi,ho) as nr
from tbl;

Ausgabe:

HI  HO  ROW_QUERY_A
A   X   1
A   X   2
A   X   3
B   Y   1
B   Y   2
C   Z   1
C   Z   2

Sehen? Warum müssen Zeilennummern auf dieselbe Kombination gesetzt werden? Was werden Sie auf Triple A, X, auf Double B, Y, auf Double C, Z analysieren? :-)

Sie müssen nur PARTITION für nicht eindeutige Spalten verwenden, dann sortieren Sie nach unique der nicht eindeutigen Spalte(n). -ing-Spalte. Beispiel wird es klarer machen:

create table tbl(hi varchar, ho varchar);

insert into tbl values
('A','D'),
('A','E'),
('A','F'),
('B','F'),
('B','E'),
('C','E'),
('C','D');

select
   hi,ho,
   row_number() over(partition by hi order by ho) as nr
from tbl;

PARTITION BY hi arbeitet mit nicht eindeutigen Spalten, dann bestellen Sie bei jeder partitionierten Spalte nach ihrer eindeutigen Spalte (ho), ORDER BY ho

Ausgabe:

HI  HO  NR
A   D   1
A   E   2
A   F   3
B   E   1
B   F   2
C   D   1
C   E   2

Dieser Datensatz ist sinnvoller

Live-Test:http://www.sqlfiddle.com/#!3/d0b44/1

Und dies ähnelt Ihrer Abfrage mit denselben Spalten sowohl bei PARTITION BY als auch bei ORDER BY:

select
   hi,ho,
   row_number() over(partition by hi,ho order by hi,ho) as nr
from tbl;

Und das ist die Ausgabe:

HI  HO  NR
A   D   1
A   E   1
A   F   1
B   E   1
B   F   1
C   D   1
C   E   1

Sehen? keinen Sinn?

Live-Test:http://www.sqlfiddle.com/#!3/d0b44/3

Schließlich könnte dies die richtige Abfrage sein:

SELECT cdt.*,
     ROW_NUMBER ()
     OVER (PARTITION BY cdt.country_code, cdt.account -- removed: cdt.currency
           ORDER BY 
               -- removed: cdt.country_code, cdt.account, 
               cdt.currency) -- keep
        seq_no
FROM CUSTOMER_DETAILS cdt