Paging com Oracle


97

Não estou tão familiarizado com o Oracle quanto gostaria. Tenho cerca de 250 mil registros e desejo exibi-los 100 por página. Atualmente, tenho um procedimento armazenado que recupera todos os registros de um quarto de milhão para um conjunto de dados usando um adaptador de dados e um conjunto de dados e o método dataadapter.Fill (conjunto de dados) nos resultados do proc armazenado. Se eu tiver "Número da página" e "Número de registros por página" como valores inteiros, posso passar como parâmetros, qual seria a melhor maneira de voltar apenas aquela seção específica. Digamos que, se eu passar 10 como número de página e 120 como número de páginas, a partir da declaração selecionada, isso me daria de 1880 a 1200, ou algo parecido, minha matemática pode estar errada.

Estou fazendo isso em .NET com C #, mas isso não é importante, se conseguir acertar no lado sql, então devo ficar bem.

Atualização: Consegui usar a sugestão de Brian e está funcionando muito bem. Eu gostaria de trabalhar em alguma otimização, mas as páginas estão chegando em 4 a 5 segundos, em vez de um minuto, e meu controle de paginação foi capaz de se integrar muito bem com meus novos procs armazenados.

Respostas:


144

Algo assim deve funcionar: Do Blog de Frans Bouma

SELECT * FROM
(
    SELECT a.*, rownum r__
    FROM
    (
        SELECT * FROM ORDERS WHERE CustomerID LIKE 'A%'
        ORDER BY OrderDate DESC, ShippingDate DESC
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE r__ >= (((pageNumber-1) * pageSize) + 1)

4
Sim, é uma coluna 'embutida' que o Oracle suporta, sempre começa em 1 e aumenta para cada linha. Portanto, neste trecho de código, se você tiver 1000 linhas, a ordem de classificação é aplicada e, em seguida, cada linha recebe um rownum. As seleções externas usam esses números de linha para localizar a 'página' que você está procurando com base no tamanho da página.
Brian Schmitt de

9
Isso é bom, mas terrivelmente lento em grandes seleções, basta verificar qual será o tempo para selecionar 0 a 1000 e 500.000 a 501.000 ... Eu estava usando esse tipo de estrutura de seleção agora estou procurando uma solução alternativa.
Newhouse de

3
@ n3whous3 você pode tentar isso - inf.unideb.hu/~gabora/pagination/results.html
jasonk

7
Eu me perguntei por que dois WHEREnão podiam ser combinados e AND, em seguida, encontrei: orafaq.com/wiki/ROWNUM
Mengdi Gao

1
A paginação do oráculo estraga meu dia.
Aetherus 01 de

134

Pergunte ao Tom sobre paginação e funções analíticas muito, muito úteis.

Este é um trecho dessa página:

select * from (
    select /*+ first_rows(25) */
     object_id,object_name,
     row_number() over
    (order by object_id) rn
        from all_objects)
    where rn between :n and :m
        order by rn;

7
Na verdade, esta é uma implementação muito melhor, embora seja difícil de encontrar nesse post. Quando você tem muitas páginas grandes, a outra resposta deve passar por todas as linhas das páginas anteriores também. Em consultas complicadas, isso significa que as páginas posteriores têm um desempenho pior do que as páginas anteriores.
tallseth

@tallseth Você está certo. É difícil encontrar nessa página. O trecho é adicionado.
Chobicus

Esta é a resposta correta, caso você queira alterar seu pedido dinamicamente.
chakeda

74

No interesse da completude, para quem procura uma solução mais moderna, no Oracle 12c existem alguns novos recursos, incluindo melhor paginação e tratamento superior.

Paging

A paginação se parece com isto:

SELECT *
FROM user
ORDER BY first_name
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY;

Top N Records

Obter os principais registros se parece com isto:

SELECT *
FROM user
ORDER BY first_name
FETCH FIRST 5 ROWS ONLY

Observe como os dois exemplos de consulta acima têm ORDER BYcláusulas. Os novos comandos os respeitam e são executados nos dados classificados.

Não consegui encontrar uma boa página de referência do Oracle para FETCHou, OFFSETmas esta página tem uma ótima visão geral desses novos recursos.

atuação

Como @wweicker aponta nos comentários abaixo, o desempenho é um problema com a nova sintaxe do 12c. Eu não tinha uma cópia do 18c para testar se a Oracle o melhorou desde então.

Curiosamente, meus resultados reais foram retornados um pouco mais rápido na primeira vez que executei as consultas em minha tabela (mais de 113 milhões de linhas) para o novo método:

  • Novo método: 0,013 segundos.
  • Método antigo: 0,107 segundos.

No entanto, como @wweicker mencionou, o plano de explicação parece muito pior para o novo método:

  • Custo do novo método: 300.110
  • Custo do método antigo: 30

A nova sintaxe causou uma varredura completa do índice na minha coluna, que era o custo total. Provavelmente, as coisas ficam muito piores ao limitar os dados não indexados.

Vamos dar uma olhada ao incluir uma única coluna não indexada no conjunto de dados anterior:

  • Tempo / custo do novo método: 189,55 segundos / 998.908
  • Tempo / custo do método antigo: 1,973 segundos / 256

Resumo: use com cuidado até que o Oracle melhore esse manuseio. Se você tem um índice para trabalhar, talvez consiga usar o novo método.

Espero ter uma cópia do 18c para brincar em breve e poder atualizar


Esta é uma ótima resposta para usuários 12c
Lalji Gajera

1
A sintaxe é mais limpa, mas o desempenho é pior ( dba-presents.com/index.php/databases/oracle/… )
wweicker

É bom saber, obrigado @wweicker. Esperançosamente, o desempenho será corrigido pela Oracle em breve; embora, conhecendo a Oracle, essa possa ser uma esperança distante!
JoelC

A sintaxe é nova e foi transformada em chamadas ROW_NUMBER / RANK regulares. Relacionado Como posso limitar o número de linhas retornadas por uma consulta Oracle após o pedido?
Lukasz Szozda

@JoelC houve alguma mudança na sua opinião?
Ryan

11

Quero apenas resumir as respostas e comentários. Existem várias maneiras de fazer uma paginação.

Antes do oracle 12c, não havia funcionalidade OFFSET / FETCH, então dê uma olhada no white paper como o @jasonk sugeriu. É o artigo mais completo que encontrei sobre diferentes métodos, com explicação detalhada das vantagens e desvantagens. Levaria muito tempo para copiá-los e colá-los aqui, então não vou fazer isso.

Há também um bom artigo dos criadores do jooq explicando algumas advertências comuns com o oracle e a paginação de outros bancos de dados. postagem do blog de jooq

Boas notícias, desde o oracle 12c temos uma nova funcionalidade OFFSET / FETCH. Novos recursos do OracleMagazine 12c . Consulte "N principais consultas e paginação"

Você pode verificar sua versão do oracle emitindo a seguinte declaração

SELECT * FROM V$VERSION

7

Experimente o seguinte:

SELECT *
FROM
  (SELECT FIELDA,
    FIELDB,
    FIELDC,
    ROW_NUMBER() OVER (ORDER BY FIELDC) R
  FROM TABLE_NAME
  WHERE FIELDA = 10
  )
WHERE R >= 10
AND R   <= 15;

via [tecnicume]


0

No meu projeto usei Oracle 12c e java . O código de paginação é parecido com este:

 public public List<Map<String, Object>> getAllProductOfferWithPagination(int pageNo, int pageElementSize, Long productOfferId, String productOfferName) {
    try {

        if(pageNo==1){
            //do nothing
        } else{
            pageNo=(pageNo-1)*pageElementSize+1;
        }
        System.out.println("algo pageNo: " + pageNo +"  pageElementSize: "+ pageElementSize+"  productOfferId: "+ productOfferId+"  productOfferName: "+ productOfferName);

        String sql = "SELECT * FROM ( SELECT * FROM product_offer po WHERE po.deleted=0 AND (po.product_offer_id=? OR po.product_offer_name LIKE ? )" +
             " ORDER BY po.PRODUCT_OFFER_ID asc) foo OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ";

       return jdbcTemplate.queryForList(sql,new Object[] {productOfferId,"%"+productOfferName+"%",pageNo-1, pageElementSize});

    } catch (Exception e) {
        System.out.println(e);
        e.printStackTrace();
        return null;
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.