Oracle
 sql >> Base de données >  >> RDS >> Oracle

Mot clé Oracle 'Partition By' et 'Row_Number'

PARTITION BY séparer les ensembles, cela vous permet de pouvoir travailler (ROW_NUMBER(),COUNT(),SUM(),etc) sur un ensemble connexe indépendamment.

Dans votre requête, l'ensemble connexe composé de lignes avec cdt.country_code, cdt.account, cdt.currency similaires. Lorsque vous partitionnez sur ces colonnes et que vous leur appliquez ROW_NUMBER. Les autres colonnes de ces combinaisons/ensembles recevront un numéro séquentiel à partir de ROW_NUMBER

Mais cette requête est amusante, si votre partition contient des données uniques et que vous y mettez un row_number, cela produira simplement le même nombre. C'est comme si vous faisiez un ORDER BY sur une partition dont l'unicité est garantie. Par exemple, considérez le GUID comme une combinaison unique de cdt.country_code, cdt.account, cdt.currency

newid() produit GUID, alors qu'attendez-vous de cette expression ?

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

...Bien, toutes les rangées partitionnées (aucune n'a été partitionnée, chaque ligne est partitionnée dans sa propre ligne) row_numbers sont toutes définies sur 1

Fondamentalement, vous devez partitionner sur des colonnes non uniques. ORDER BY sur OVER nécessitait que PARTITION BY ait une combinaison non unique, sinon tous les row_numbers deviendraient 1

Un exemple, voici vos données :

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

Alors ceci est analogue à votre requête :

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

Quel en sera le résultat ?

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

Tu vois la combinaison de HI HO ? Les trois premières lignes ont une combinaison unique, elles sont donc définies sur 1, les lignes B ont le même W, donc des ROW_NUMBERS différents, de même avec les lignes HI C.

Maintenant, pourquoi le ORDER BY besoin là-bas? Si le développeur précédent veut simplement mettre un row_number sur des données similaires (par exemple HI B, toutes les données sont B-W, B-W), il peut simplement faire ceci :

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

Mais hélas, Oracle (et Sql Server aussi) n'autorise pas la partition sans ORDER BY; alors que dans Postgresql, ORDER BY sur PARTITION est facultatif :http://www.sqlfiddle.com/#!1/27821/1

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

Votre ORDER BY sur votre partition semble un peu redondant, pas à cause de la faute du développeur précédent, certaines bases de données n'autorisent tout simplement pas PARTITION sans ORDER BY , il se peut qu'il ne puisse pas trouver une bonne colonne de candidats à trier. Si les colonnes PARTITION BY et ORDER BY sont identiques, supprimez simplement ORDER BY, mais comme certaines bases de données ne le permettent pas, vous pouvez simplement faire ceci :

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

Vous ne trouvez pas une bonne colonne à utiliser pour trier des données similaires ? Autant trier au hasard, les données partitionnées ont les mêmes valeurs en tous cas. Vous pouvez utiliser GUID par exemple (vous utilisez newid() pour SQL Server). Donc, cela a la même sortie faite par le développeur précédent, il est regrettable que certaines bases de données n'autorisent pas PARTITION sans ORDER BY

Bien que vraiment, cela m'échappe et je ne trouve pas de bonne raison de mettre un numéro sur les mêmes combinaisons (B-W, B-W dans l'exemple ci-dessus). Cela donne l'impression que la base de données contient des données redondantes. D'une manière ou d'une autre, cela m'a rappelé ceci :comment obtenir un enregistrement unique à partir de la même liste d'enregistrements de la table ? Aucune contrainte d'unicité dans la table

Cela semble vraiment mystérieux de voir une PARTITION BY avec la même combinaison de colonnes avec ORDER BY, ne peut pas facilement déduire l'intention du code.

Test en direct :http://www.sqlfiddle.com/#!3/27821/6

Mais comme dbaseman l'a également remarqué, il est inutile de partitionner et d'ordonner sur les mêmes colonnes.

Vous disposez d'un ensemble de données comme celui-ci :

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

Ensuite, vous PARTITION PAR salut, ho ; et puis vous COMMANDEZ PAR salut, ho. Il n'y a aucun sens à numéroter des données similaires :-) 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;

Sortie :

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

Voir? Pourquoi avoir besoin de mettre des numéros de ligne sur la même combinaison ? Qu'allez-vous analyser sur triple A,X, sur double B,Y, sur double C,Z ? :-)

Vous avez juste besoin d'utiliser PARTITION sur une colonne non unique, puis vous triez sur unique de(s) colonne(s) non unique(s) -ing colonne. L'exemple le rendra plus clair :

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 opère sur une colonne non unique, puis sur chaque colonne partitionnée, vous commandez sur sa colonne unique (ho), ORDER BY ho

Sortie :

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

Cet ensemble de données a plus de sens

Test en direct :http://www.sqlfiddle.com/#!3/d0b44/1

Et ceci est similaire à votre requête avec les mêmes colonnes sur PARTITION BY et ORDER BY :

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

Et voici la sortie :

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

Voir? aucun sens ?

Test en direct :http://www.sqlfiddle.com/#!3/d0b44/3

Enfin, cela pourrait être la bonne requête :

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