Palavra-chave Oracle "Partition By"

253

Alguém pode explicar o que a partition bypalavra - chave faz e dar um exemplo simples dela em ação, bem como por que alguém iria querer usá-la? Eu tenho uma consulta SQL escrita por outra pessoa e estou tentando descobrir o que ela faz.

Um exemplo de partição por:

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

Os exemplos que eu vi online parecem um pouco detalhados demais.

Alex Beardsley
fonte
Outro link relevante: postgresql.org/docs/9.1/static/tutorial-window.html
Shashank Vivek

Respostas:

259

o PARTITION BY cláusula define o intervalo de registros que serão usados ​​para cada "GRUPO" dentro da OVERcláusula.

No seu exemplo de SQL, DEPT_COUNTretornará o número de funcionários desse departamento para cada registro de funcionário. (É como se você estivesse desmalizando a emptabela; você ainda retorna todos os registros noemp tabela.)

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

Se houvesse outra coluna (por exemplo, state), você poderia contar quantos departamentos nesse estado.

É como obter os resultados de um GROUP BY(SUM , AVG, etc.) sem a agregar o conjunto de resultados (ou seja, remover registros correspondentes).

É útil quando você usa as funções LAST OVERou MIN OVERpara obter, por exemplo, o salário mais alto e mais baixo no departamento e, em seguida, usá-lo em um cálculo com relação ao salário registrado sem uma sub-seleção, o que é muito mais rápido.

Leia o artigo vinculado da AskTom para obter mais detalhes.

Cara
fonte
6
LAST_VALUE - retorna o último salário, MAX retorna o salário mais alto
Maciek Kreft
1
Você quer dizer "sem uma seleção secundária, que é muito mais lenta"? Acho que estou confuso se o sub-select é mais lento ou mais rápido que last overe min over. Eu imagino que uma sub-seleção seria mais lenta, mas a gramática inglesa na resposta não sugere isso.
Jason
Essa abordagem reduz o número de vezes que as linhas são processadas, tornando-a mais eficiente do que uma subseleção. Mais perceptível em conjuntos de dados muito grandes.
Guy
164

O conceito é muito bem explicado pela resposta aceita, mas acho que quanto mais exemplo se vê, melhor ele se encaixa. Aqui está um exemplo incremental:

1) O chefe diz "obtenha-me o número de itens que temos em estoque, agrupados por marca"

Você diz : "não há problema"

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

Resultado:

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

2) O chefe diz "Agora, consiga uma lista de todos os itens, com a marca E o número de itens que a respectiva marca possui"

Você pode tentar:

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

Mas você obtém:

ORA-00979: not a GROUP BY expression 

É aqui que OVER (PARTITION BY BRAND)entra:

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

O que significa:

  • COUNT(ITEM_ID) - obtenha o número de itens
  • OVER - Sobre o conjunto de linhas
  • (PARTITION BY BRAND) - que tenham a mesma marca

E o resultado é:

+--------------+---------------+----------+
|  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
fonte
3
Se eu quiser obter um resultado para cada grupo .. Como vou obtê-lo?
Viuu -a
Você sabe se OVER PARTITION BY pode ser usado em uma cláusula WHERE?
22718 Kevin Burton
Eu sugiro que você faça uma pergunta no SO, dar detalhes e explicar o que você quer alcançar
Andrejs
@ Viuu-a: Então você provavelmente vai querer usar um simples GROUP BY.
jackthehipster 11/03
amo esse exemplo ... fácil de entender
Johnny Wu
27

É a extensão SQL chamada analytics. O "over" na instrução select informa ao oracle que a função é uma função analítica, não um grupo por função. A vantagem de usar a análise é que você pode coletar somas, contagens e muito mais com apenas uma passagem dos dados em vez de fazer um loop pelos dados com sub-seleções ou pior, PL / SQL.

Parece confuso no começo, mas isso será uma segunda natureza rapidamente. Ninguém explica melhor do que Tom Kyte. Portanto, o link acima é ótimo.

Obviamente, ler a documentação é uma obrigação.

user60890
fonte
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

Aqui estamos obtendo a contagem para o respectivo departamento. Quanto ao departamento 10, temos 4 registros na tabela com resultados semelhantes para o departamento 20 e 30 também.


fonte
12
Nenhuma expalnation à pergunta de como a PARTIÇÃO por trabalha. Apenas o exemplo de saída sozinho não responde totalmente à pergunta.
Siraj Samsudeen
2

a palavra-chave over partition é como se estivéssemos particionando os dados criando client_id, um 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 retornará o número de operações realizadas pelo client_id

issam
fonte
0

Penso que este exemplo sugere uma pequena nuance sobre como o particionamento funciona e como agrupar por trabalhos. Meu exemplo é do Oracle 12, se for um erro de compilação.

Eu tentei :

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

No entanto, isso funciona como esperado:

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;

Produzindo o número de elementos em cada estado com base na chave externa "data_key". Portanto, se data_key = 'APPLE' tivesse 3 linhas com o estado 'A', 2 linhas com o estado 'B', uma linha com o estado 'C', a linha correspondente para 'APPLE' seria 'APPLE', 3, 2 , 1, 6.

georgejo
fonte