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.