web-dev-qa-db-fra.com

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

J'ai une requête SQL écrite par quelqu'un d'autre et j'essaie de comprendre ce qu'elle fait. Quelqu'un peut-il s'il vous plaît expliquer ce que le Partition By et Row_Number mots-clés fait ici et donner un exemple simple de celui-ci en action, ainsi que pourquoi on voudrait l'utiliser?

Un exemple de partition par:

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

J'ai vu des exemples en ligne, ils sont un peu trop profonds.

Merci d'avance!

43
HashimR

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

Dans votre requête, le jeu associé est constitué de lignes avec des cdt.country_code, cdt.account et 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 de ROW_NUMBER.

Mais cette requête est amusante, si votre partition contient des données uniques et que vous y mettez un numéro de ligne, elle produira le même nombre. C'est comme si vous faisiez ORDER BY sur une partition qui est garantie d'être unique. Exemple, pensez à GUID comme combinaison unique de cdt.country_code, cdt.account, cdt.currency

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

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

... à droite, tous les partitionnés (aucun n'a été partitionné, chaque ligne est partitionnée dans sa propre ligne)

Fondamentalement, vous devez partitionner sur des colonnes non uniques. ORDER BY on OVER avait besoin de la partition PAR pour avoir une combinaison non unique, sinon tous les row_numbers deviendraient 1

Un exemple, ce sont 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;

Quelle sera la sortie de cela?

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, et donc un ROW_NUMBERS différent, de même que les lignes HI C.

Maintenant, pourquoi le ORDER BY Est-il nécessaire? Si le développeur précédent veut simplement mettre un numéro_ligne sur des données similaires (par exemple, HI B, toutes les données sont B-W, B-W), il peut simplement le faire:

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

Mais hélas, Oracle (et Sql Server également) n'autorise pas les partitions 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 a l'air un peu redondant, pas à cause de la faute du développeur précédent, certaines bases de données n'autorisent pas PARTITION sans ORDER BY, Il pourrait ne 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, procédez comme suit:

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? Vous pourriez aussi bien trier sur aléatoire, les données partitionnées ont les mêmes valeurs . Vous pouvez utiliser GUID par exemple (vous utilisez newid() pour SQL Server). Ainsi, le même résultat est produit par le développeur précédent. Malheureusement, certaines bases de données ne le permettent 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 (N & B, B-W dans l’exemple ci-dessus). Cela donne l'impression que la base de données contient des données redondantes. En quelque sorte, cela me rappelle: Comment obtenir un enregistrement unique à partir de la même liste d’enregistrements de la table? Aucune contrainte unique dans la table

Il semble vraiment mystérieux de voir un 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 remarqué également, il est inutile de partitionner et d’organiser les mêmes colonnes.

Vous avez un ensemble de données comme ceci:

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 ORDER BY salut, ho. Il n'y a aucun sens à numéroter des données similaires :-) http://www.sqlfiddle.com/#!3/29ab8/

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 faut-il mettre les numéros de ligne sur la même combinaison? Qu'est-ce que vous allez analyser en triple A, X, en double B, Y, en double C, Z? :-)


Vous devez juste utiliser PARTITION sur une colonne non unique, puis vous triez sur une colonne non unique = (s) unique - ing. 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 Fonctionne 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 à la fois pour 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 le résultat:

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? pas de sens?

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


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
100
Michael Buen

Cela sélectionne le numéro de ligne par code de pays, compte et devise. Ainsi, les lignes avec le code de pays "US", le compte "XYZ" et la devise "$ USD" se verront attribuer un numéro de ligne attribué de 1 à n; il en va de même pour toutes les autres combinaisons de ces colonnes dans le jeu de résultats.

Cette requête est un peu drôle, car la clause order by ne fait absolument rien. Toutes les lignes de chaque partition ont le même code de pays, le même compte et la même devise. Il est donc inutile de classer ces colonnes. Les numéros de ligne ultimes attribués dans cette requête seront donc imprévisibles.

J'espère que ça t'as aidé...

7
McGarnagle

J'utilise souvent row_number () comme moyen rapide de supprimer les enregistrements en double de mes instructions select. Ajoutez simplement une clause where. Quelque chose comme...

select a,b,rn 
  from (select a, b, row_number() over (partition by a,b order by a,b) as rn           
          from table) 
 where rn=1;
6
chris

Je sais que c'est un vieux fil mais PARTITION est l'équivalent de GROUP BY et ORDER BY. ORDER BY dans cette fonction est. . . COMMANDÉ PAR. C'est juste un moyen de créer une unicité hors de la redondance en ajoutant un numéro de séquence. Vous pouvez également éliminer les autres enregistrements redondants par la clause WHERE lors de la référence à la colonne avec alias pour la fonction. Cependant, DISTINCT dans l'instruction SELECT accomplirait probablement la même chose à cet égard.

2
OldManOfTheSQL