Mot-clé Oracle «Partitionner par»

254

Quelqu'un peut-il expliquer ce que fait le partition bymot-clé et en donner un exemple simple en action, ainsi que pourquoi on voudrait l'utiliser? J'ai une requête SQL écrite par quelqu'un d'autre et j'essaie de comprendre ce qu'elle fait.

Un exemple de partition par:

SELECT empno, deptno, COUNT(*) 
OVER (PARTITION BY deptno) DEPT_COUNT
FROM emp

Les exemples que j'ai vus en ligne semblent un peu trop approfondis.

Alex Beardsley
la source
Un autre lien pertinent: postgresql.org/docs/9.1/static/tutorial-window.html
Shashank Vivek

Réponses:

259

La PARTITION BYclause définit la plage d'enregistrements qui seront utilisés pour chaque "GROUPE" dans la OVERclause.

Dans votre exemple SQL, DEPT_COUNTrenverra le nombre d'employés de ce service pour chaque enregistrement d'employé. (C'est comme si vous dé-nomalisiez la emptable; vous renvoyez toujours chaque enregistrement dans la emptable.)

emp_no  dept_no  DEPT_COUNT
1       10       3
2       10       3
3       10       3 <- three because there are three "dept_no = 10" records
4       20       2
5       20       2 <- two because there are two "dept_no = 20" records

S'il y avait une autre colonne (par exemple state), alors vous pourriez compter le nombre de départements dans cet État.

Il est comme obtenir les résultats d'un GROUP BY( SUM, AVG, etc.) sans l'agrégation de l'ensemble des résultats (c. -à- suppression des enregistrements correspondants).

Il est utile lorsque vous utilisez les fonctions LAST OVERou MIN OVERpour obtenir, par exemple, le salaire le plus bas et le plus élevé du service, puis que vous l'utilisez dans un calcul par rapport à ces enregistrements de salaire sans sous-sélection, ce qui est beaucoup plus rapide.

Lisez l' article AskTom lié pour plus de détails.

Gars
la source
6
LAST_VALUE - retourne le dernier salaire, MAX retourne le salaire le plus élevé
Maciek Kreft
1
Voulez-vous dire "sans sous-sélection, ce qui est beaucoup plus lent"? Je suppose que je suis confus si la sous-sélection est plus lente ou plus rapide que last overet min over. J'imagine qu'une sous-sélection serait plus lente, mais la grammaire anglaise dans la réponse ne le suggère pas.
Jason
Cette approche réduit le nombre de fois que les lignes sont traitées, ce qui la rend plus efficace qu'une sous-sélection. Plus visible dans les très grands ensembles de données.
Guy
164

Le concept est très bien expliqué par la réponse acceptée, mais je trouve que plus on voit d'exemple, mieux il s'enfonce. Voici un exemple incrémentiel:

1) Le patron dit "obtenez-moi le nombre d'articles que nous avons en stock, groupés par marque"

Vous dites : "pas de problème"

SELECT 
      BRAND
      ,COUNT(ITEM_ID) 
FROM 
      ITEMS
GROUP BY 
      BRAND;

Résultat:

+--------------+---------------+
|  Brand       |   Count       | 
+--------------+---------------+
| H&M          |     50        |
+--------------+---------------+
| Hugo Boss    |     100       |
+--------------+---------------+
| No brand     |     22        |
+--------------+---------------+

2) Le patron dit "Maintenant, obtenez-moi une liste de tous les articles, avec leur marque ET le nombre d'articles que la marque respective possède"

Vous pouvez essayer:

 SELECT 
      ITEM_NR
      ,BRAND
      ,COUNT(ITEM_ID) 
 FROM 
      ITEMS
 GROUP BY 
      BRAND;

Mais vous obtenez:

ORA-00979: not a GROUP BY expression 

C'est là OVER (PARTITION BY BRAND)qu'intervient:

 SELECT 
      ITEM_NR
      ,BRAND
      ,COUNT(ITEM_ID) OVER (PARTITION BY BRAND) 
 FROM 
      ITEMS;

Whic signifie:

  • COUNT(ITEM_ID) - obtenir le nombre d'articles
  • OVER - Sur l'ensemble des rangées
  • (PARTITION BY BRAND) - qui ont la même marque

Et le résultat est:

+--------------+---------------+----------+
|  Items       |  Brand        | Count()  |
+--------------+---------------+----------+
|  Item 1      |  Hugo Boss    |   100    | 
+--------------+---------------+----------+
|  Item 2      |  Hugo Boss    |   100    | 
+--------------+---------------+----------+
|  Item 3      |  No brand     |   22     | 
+--------------+---------------+----------+
|  Item 4      |  No brand     |   22     | 
+--------------+---------------+----------+
|  Item 5      |  H&M          |   50     | 
+--------------+---------------+----------+

etc...

Andrejs
la source
3
Si je veux obtenir un résultat pour chaque groupe .. Comment vais-je l'obtenir?
Viuu -un
Savez-vous si OVER PARTITION BY peut être utilisé dans une clause WHERE?
Kevin Burton
Je vous suggère de poser une question sur SO, de donner des détails et d'expliquer ce que vous voulez réaliser
Andrejs
@ Viuu-a: Alors vous voudrez probablement utiliser un simple GROUP BY.
jackthehipster
j'adore cet exemple ... facile à comprendre
Johnny Wu
27

Il s'agit de l'extension SQL appelée analytics. Le "over" dans l'instruction select indique à oracle que la fonction est une fonction analytique, pas un groupe par fonction. L'avantage de l'utilisation de l'analyse est que vous pouvez collecter des sommes, des comptages et bien plus encore avec un seul passage des données au lieu de parcourir les données avec des sous-sélections ou pire, PL / SQL.

Cela semble déroutant au premier abord, mais ce sera rapidement une seconde nature. Personne ne l'explique mieux que Tom Kyte. Le lien ci-dessus est donc génial.

Bien sûr, la lecture de la documentation est indispensable.

user60890
la source
9
EMPNO     DEPTNO DEPT_COUNT

 7839         10          4
 5555         10          4
 7934         10          4
 7782         10          4 --- 4 records in table for dept 10
 7902         20          4
 7566         20          4
 7876         20          4
 7369         20          4 --- 4 records in table for dept 20
 7900         30          6
 7844         30          6
 7654         30          6
 7521         30          6
 7499         30          6
 7698         30          6 --- 6 records in table for dept 30

Ici, nous obtenons le décompte de chaque département. En ce qui concerne deptno 10, nous avons 4 enregistrements dans le tableau emp des résultats similaires pour deptno 20 et 30 également.


la source
12
Pas d'expansion à la question de savoir comment fonctionne PARTITION by. L'exemple de sortie seul ne répond pas entièrement à la question.
Siraj Samsudeen
2

le mot clé over partition est comme si nous partitionnions les données en créant client_id un sous-ensemble de chaque identifiant client

select client_id, operation_date,
       row_number() count(*) over (partition by client_id order by client_id ) as operationctrbyclient
from client_operations e
order by e.client_id;

cette requête renverra le nombre d'opérations effectuées par le client_id

issam
la source
0

Je pense que cet exemple suggère une petite nuance sur la façon dont le partitionnement fonctionne et comment le groupe par fonctionne. Mon exemple vient d'Oracle 12, si mon exemple se trouve être un bogue de compilation.

J'ai essayé :

SELECT t.data_key
,      SUM ( CASE when t.state = 'A' THEN 1 ELSE 0 END) 
OVER   (PARTITION BY t.data_key) count_a_rows
,      SUM ( CASE when t.state = 'B' THEN 1 ELSE 0 END) 
OVER   (PARTITION BY t.data_key) count_b_rows
,      SUM ( CASE when t.state = 'C' THEN 1 ELSE 0 END) 
OVER   (PARTITION BY t.data_key) count_c_rows
,      COUNT (1) total_rows
from mytable t
group by t.data_key  ---- This does not compile as the compiler feels that t.state isn't in the group by and doesn't recognize the aggregation I'm looking for

Cela fonctionne cependant comme prévu:

SELECT distinct t.data_key
,      SUM ( CASE when t.state = 'A' THEN 1 ELSE 0 END) 
OVER   (PARTITION BY t.data_key) count_a_rows
,      SUM ( CASE when t.state = 'B' THEN 1 ELSE 0 END) 
OVER   (PARTITION BY t.data_key) count_b_rows
,      SUM ( CASE when t.state = 'C' THEN 1 ELSE 0 END) 
OVER   (PARTITION BY t.data_key) count_c_rows
,      COUNT (1) total_rows
from mytable t;

Produire le nombre d'éléments dans chaque état sur la base de la clé externe "clé_données". Donc, si data_key = 'APPLE' avait 3 lignes avec l'état 'A', 2 lignes avec l'état 'B', une ligne avec l'état 'C', la ligne correspondante pour 'APPLE' serait 'APPLE', 3, 2 , 1, 6.

georgejo
la source