Oracle
 sql >> Database >  >> RDS >> Oracle

Parola chiave Oracle "Partition By" e "Row_Number".

PARTITION BY insiemi segregati, questo ti consente di essere in grado di lavorare (ROW_NUMBER(),COUNT(),SUM(),ecc) su set correlati in modo indipendente.

Nella tua query, il relativo set composto da righe con cdt.country_code, cdt.account, cdt.currency simili. Quando partizioni su quelle colonne e applichi ROW_NUMBER su di esse. Quelle altre colonne su quella combinazione/insieme riceveranno un numero sequenziale da ROW_NUMBER

Ma quella query è divertente, se la tua partizione con alcuni dati univoci e ci metti un row_number, produrrà semplicemente lo stesso numero. È come fare un ORDER BY su una partizione che è garantita come unica. Ad esempio, pensa al GUID come combinazione univoca di cdt.country_code, cdt.account, cdt.currency

newid() produce GUID, quindi cosa ti aspetti da questa espressione?

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

...Esatto, tutti i numeri_riga delle righe partizionate (nessuno è stato partizionato, ogni riga è partizionata nella propria riga) sono tutti impostati su 1

Fondamentalmente, dovresti partizionare su colonne non univoche. ORDER BY su OVER necessitava di PARTITION BY per avere una combinazione non univoca, altrimenti tutti i numeri_riga diventeranno 1

Un esempio, questi sono i tuoi dati:

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');

Allora questo è analogo alla tua richiesta:

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

Quale sarà il risultato?

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

Vedi la combinazione di HI HO? Le prime tre righe hanno una combinazione univoca, quindi sono impostate su 1, le righe B hanno la stessa W, quindi ROW_NUMBERS diversi, allo stesso modo con le righe HI C.

Ora, perché è ORDER BY necessario lì? Se lo sviluppatore precedente desidera semplicemente inserire un numero_riga su dati simili (ad es. HI B, tutti i dati sono B-N, B-N), può semplicemente fare questo:

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

Ma ahimè, Oracle (e anche Sql Server) non consente la partizione senza ORDER BY; mentre in Postgresql, ORDER BY su PARTITION è facoltativo:http://www.sqlfiddle.com/#!1/27821/1

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

Il tuo ORDER BY sulla tua partizione sembra un po' ridondante, non a causa dell'errore dello sviluppatore precedente, alcuni database semplicemente non consentono PARTITION senza ORDER BY , potrebbe non essere in grado di trovare una buona colonna candidata su cui eseguire l'ordinamento. Se entrambe le colonne PARTITION BY e ORDER BY sono uguali, rimuovi semplicemente ORDER BY, ma poiché alcuni database non lo consentono, puoi semplicemente farlo:

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

Non riesci a trovare una buona colonna da utilizzare per ordinare dati simili? Potresti anche ordinare in modo casuale, i dati partizionati hanno gli stessi valori comunque. Ad esempio, puoi utilizzare GUID (usa newid() per SQL Server). Quindi ha lo stesso output prodotto dallo sviluppatore precedente, è un peccato che alcuni database non consentano PARTITION senza ORDER BY

Anche se in realtà, mi sfugge e non riesco a trovare una buona ragione per mettere un numero sulle stesse combinazioni (B-N, B-N nell'esempio sopra). Dà l'impressione che il database abbia dati ridondanti. In qualche modo mi ha ricordato questo:come ottenere un record univoco dallo stesso elenco di record dalla tabella? Nessun vincolo univoco nella tabella

Sembra davvero arcano vedere una PARTITION BY con la stessa combinazione di colonne con ORDER BY, non può facilmente dedurre l'intento del codice.

Test dal vivo:http://www.sqlfiddle.com/#!3/27821/6

Ma come ha notato anche dbaseman, è inutile partizionare e ordinare sulle stesse colonne.

Hai un insieme di dati come questo:

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');

Quindi PARTITION BY hi,ho; e poi ORDINA PER ciao, ho. Non ha senso numerare dati simili :-) 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;

Uscita:

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

Vedere? Perché è necessario inserire i numeri di riga sulla stessa combinazione? Cosa analizzerai sulla tripla A,X, sulla doppia B,Y, sulla doppia C,Z? :-)

Devi solo usare PARTITION su colonne non univoche, quindi ordini su colonne non univoche univoche -ing colonna. L'esempio lo renderà più chiaro:

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 opera su una colonna non univoca, quindi su ogni colonna partizionata, ordini sulla sua colonna univoca(ho), ORDER BY ho

Uscita:

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

Quel set di dati ha più senso

Test dal vivo:http://www.sqlfiddle.com/#!3/d0b44/1

E questo è simile alla tua query con le stesse colonne sia su PARTITION BY che su ORDER BY:

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

E questo è l'output:

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

Vedere? non ha senso?

Test dal vivo:http://www.sqlfiddle.com/#!3/d0b44/3

Finalmente questa potrebbe essere la domanda giusta:

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