Palabra clave "Partición por" de Oracle

253

¿Alguien puede explicar qué hace la partition bypalabra clave y dar un ejemplo simple de ella en acción, así como por qué uno querría usarla? Tengo una consulta SQL escrita por otra persona y estoy tratando de averiguar qué hace.

Un ejemplo de partición por:

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

Los ejemplos que he visto en línea parecen demasiado profundos.

Alex Beardsley
fuente
Otro enlace relevante: postgresql.org/docs/9.1/static/tutorial-window.html
Shashank Vivek

Respuestas:

259

La PARTITION BYcláusula establece el rango de registros que se utilizarán para cada "GRUPO" dentro de la OVERcláusula.

En su ejemplo SQL, DEPT_COUNTdevolverá el número de empleados dentro de ese departamento para cada registro de empleado. (Es como si des-nominalizara la emptabla; todavía devuelve todos los registros de la emptabla).

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

Si hubiera otra columna (p. Ej. state) , Entonces podría contar cuántos departamentos en ese estado.

Es como conseguir los resultados de un GROUP BY( SUM, AVG, etc.) sin la agregación del conjunto de resultados (es decir, la eliminación de registros coincidentes).

Es útil cuando usa las funciones LAST OVERo MIN OVERpara obtener, por ejemplo, el salario más bajo y más alto en el departamento y luego lo usa en un cálculo contra este salario de registros sin una subselección, que es mucho más rápido.

Lea el artículo de AskTom vinculado para obtener más detalles.

Chico
fuente
66
LAST_VALUE - devuelve el último salario, MAX devuelve el salario más alto
Maciek Kreft
1
¿Quieres decir "sin una sub selección, que es mucho más lenta"? Supongo que estoy confundido si la selección secundaria es más lenta o más rápida que last overy min over. Me imagino que una selección secundaria sería más lenta, pero la gramática inglesa en la respuesta no sugiere eso.
Jason
Este enfoque reduce la cantidad de veces que se procesan las filas, lo que lo hace más eficiente que una subselección. Más notable en conjuntos de datos muy grandes.
Guy
164

El concepto está muy bien explicado por la respuesta aceptada, pero creo que cuanto más ejemplo se ve, mejor se asimila. Aquí hay un ejemplo incremental:

1) El jefe dice "dame la cantidad de artículos que tenemos en stock agrupados por marca"

Usted dice : "no hay problema"

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

Resultado:

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

2) El jefe dice "Ahora consígueme una lista de todos los artículos, con su marca Y la cantidad de artículos que tiene la marca respectiva"

Puedes probar:

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

Pero obtienes:

ORA-00979: not a GROUP BY expression 

Aquí es donde OVER (PARTITION BY BRAND)entra el:

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

¿Qué significa:

  • COUNT(ITEM_ID) - obtener el número de artículos
  • OVER - Sobre el conjunto de filas
  • (PARTITION BY BRAND) - que tienen la misma marca

Y el resultado es:

+--------------+---------------+----------+
|  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
fuente
3
Si quiero obtener un resultado para cada grupo ... ¿Cómo lo conseguiré?
Viuu -a
¿Sabes si OVER PARTITION BY puede usarse en una cláusula WHERE?
Kevin Burton
Le sugiero que haga una pregunta sobre SO, brinde detalles y explique lo que quiere lograr
Andrejs
@ Viuu-a: Entonces probablemente querrás usar un simple GROUP BY.
Jackthehipster
Me encanta este ejemplo ... fácil de entender
Johnny Wu
27

Es la extensión SQL llamada análisis. El "over" en la instrucción select le dice a Oracle que la función es una función analítica, no un grupo por función. La ventaja de usar el análisis es que puede recopilar sumas, recuentos y mucho más con solo pasar los datos en lugar de recorrerlos con subselecciones o, peor aún, PL / SQL.

Al principio parece confuso, pero será una segunda naturaleza rápidamente. Nadie lo explica mejor que Tom Kyte. Entonces el enlace de arriba es genial.

Por supuesto, leer la documentación es imprescindible.

usuario60890
fuente
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

Aquí estamos contando para el departamento respectivo. En cuanto al departamento 10 tenemos 4 registros en la tabla con resultados similares para los departamentos 20 y 30 también.


fuente
12
Sin expansión a la pregunta de cómo funciona la PARTICIÓN por. Solo la salida de ejemplo por sí sola no responde completamente la pregunta.
Siraj Samsudeen
2

la palabra clave sobre partición es como si estuviéramos particionando los datos mediante la creación de client_id un subconjunto de cada ID de cliente

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;

esta consulta devolverá el número de operaciones realizadas por client_id

issam
fuente
0

Creo que este ejemplo sugiere un pequeño matiz sobre cómo funciona la partición y cómo funciona agrupar por. Mi ejemplo es de Oracle 12, si resulta que es un error de compilación.

Lo intenté :

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

Sin embargo, esto funciona como se esperaba:

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;

Produciendo el número de elementos en cada estado basado en la clave externa "data_key". Entonces, si data_key = 'APPLE' tuviera 3 filas con el estado 'A', 2 filas con el estado 'B', una fila con el estado 'C', la fila correspondiente para 'APPLE' sería 'APPLE', 3, 2 , dieciséis.

georgejo
fuente