Oracle selecione os 10 melhores registros
Pergunta
Tenho um grande problema com uma declaração SQL no Oracle. Quero selecionar os 10 principais registros encomendados pelo Storage_DB, que não estão em uma lista de outra instrução SELECT.
Este funciona bem para todos os registros:
SELECT DISTINCT
APP_ID,
NAME,
STORAGE_GB,
HISTORY_CREATED,
TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE
FROM HISTORY WHERE
STORAGE_GB IS NOT NULL AND
APP_ID NOT IN (SELECT APP_ID
FROM HISTORY
WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') = '06.02.2009')
Mas quando estou adicionando
AND ROWNUM <= 10
ORDER BY STORAGE_GB DESC
Estou recebendo algum tipo de registros "aleatórios". Eu acho que porque o limite leva em vigor antes da ordem.
Alguém tem uma boa solução? O outro problema: esta consulta é realmente lenta (10k+ registros)
Solução
Você precisará colocar sua consulta atual em subconsiva como abaixo:
SELECT * FROM (
SELECT DISTINCT
APP_ID,
NAME,
STORAGE_GB,
HISTORY_CREATED,
TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE
FROM HISTORY WHERE
STORAGE_GB IS NOT NULL AND
APP_ID NOT IN (SELECT APP_ID FROM HISTORY WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') ='06.02.2009')
ORDER BY STORAGE_GB DESC )
WHERE ROWNUM <= 10
Oracle se aplica ROWNUM para o resultado depois de devolvido.
Você precisa filtrar o resultado após o retorno, para que seja necessária uma subconsulta. Você também pode usar CLASSIFICAÇÃO() Função para obter resultados de Top-N.
Para desempenho de desempenho usar NOT EXISTS
no lugar de NOT IN
. Ver isto para mais.
Outras dicas
Se você estiver usando o Oracle 12C, use:
Buscar a seguir N Apenas linhas
SELECT DISTINCT
APP_ID,
NAME,
STORAGE_GB,
HISTORY_CREATED,
TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') AS HISTORY_DATE
FROM HISTORY WHERE
STORAGE_GB IS NOT NULL AND
APP_ID NOT IN (SELECT APP_ID FROM HISTORY WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') ='06.02.2009')
ORDER BY STORAGE_GB DESC
FETCH NEXT 10 ROWS ONLY
Mais informações: http://docs.oracle.com/javadb/10.5.3.0/ref/rrefsqljoffsetfetch.html
Com relação ao mau desempenho, há várias coisas que poderia ser, e realmente deve ser uma pergunta separada. No entanto, há uma coisa óbvia que pode ser um problema:
WHERE TO_CHAR(HISTORY_DATE, 'DD.MM.YYYY') = '06.02.2009')
Se history_date realmente for uma coluna de data e se tiver um índice, esta reescrita terá um desempenho melhor:
WHERE HISTORY_DATE = TO_DATE ('06.02.2009', 'DD.MM.YYYY')
Isso ocorre porque uma conversão de dados de dados desativa o uso de um índice B-Tree.
tentar
SELECT * FROM users FETCH NEXT 10 ROWS ONLY;
Você obtém um conjunto aparentemente aleatório porque o ROWNUM é aplicado antes da ordem. Portanto, sua consulta pega as dez primeiras linhas e as classifica.0 Para selecionar os dez principais salários que você deve usar uma função analítica em uma subconsulta e filtre isso:
select * from
(select empno,
ename,
sal,
row_number() over(order by sal desc nulls last) rnm
from emp)
where rnm<=10