Equivalente a LIMIT para DB2

91

Como você se sai LIMITno DB2 para iSeries?

Tenho uma tabela com mais de 50.000 registros e desejo retornar os registros de 0 a 10.000 e os registros de 10.000 a 20.000.

Eu sei que no SQL você escreve LIMIT 0,10000no final da consulta de 0 a 10.000 e LIMIT 10000,10000no final da consulta de 10.000 a 20.000

Então, como isso é feito no DB2? Qual é o código e a sintaxe? (exemplo de consulta completo é apreciado)

Elcool
fonte
ROW_NUMBER () foi implementado apenas no iSeries DB2 V5R4. Para versões anteriores, tente usar RRN (), que é semelhante.
Paul Morgan,
RRN () é completamente diferente de row_number ().
Brandon Peterson,
não funcionou para mim. Erro Sytanx.
elcool
1
Tente RRN (nome do arquivo) que fornecerá o número de registro físico relativo da linha. O RRN não será sequencial e pode pular números se as linhas forem excluídas. O RRN também não será sequencial por chave, mas será sequencial com base na adição, se nenhuma exclusão tiver ocorrido. Em qualquer caso, o RRN será único para uma linha e pode ser usado para selecionar subconjuntos da tabela.
Paul Morgan,
1
DB2 fornece suporte de palavra-chave de limite do DB2 9.7.2 de acordo com programmingzen.com/2010/06/02/…
lakshman

Respostas:

139

Usando FETCH FIRST [n] ROWS ONLY:

http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.perf/db2z_fetchfirstnrows.htm

SELECT LASTNAME, FIRSTNAME, EMPNO, SALARY
  FROM EMP
  ORDER BY SALARY DESC
  FETCH FIRST 20 ROWS ONLY;

Para obter intervalos, você teria que usar ROW_NUMBER()(desde a v5r4) e usar isso dentro da WHEREcláusula: (roubado daqui: http://www.justskins.com/forums/db2-select-how-to-123209.html )

SELECT code, name, address
FROM ( 
  SELECT row_number() OVER ( ORDER BY code ) AS rid, code, name, address
  FROM contacts
  WHERE name LIKE '%Bob%' 
  ) AS t
WHERE t.rid BETWEEN 20 AND 25;
Joe
fonte
sim, eu achei isso também, hehe. Eu estava editando a pergunta ao mesmo tempo para indicar que também quero as linhas do meio.
elcool,
2
Você tem que fazer algo assim com ROW_NUMBER: justskins.com/forums/db2-select-how-to-123209.html
Joe,
ROW_NUMBERnão é uma palavra-chave válida. Mas obrigado pelo link, me deu uma ideia e funciona.
elcool,
13

Desenvolvido este método:

Você PRECISA de uma tabela que tenha um valor exclusivo que possa ser solicitado.

Se você deseja linhas de 10.000 a 25.000 e sua Tabela tem 40.000 linhas, primeiro você precisa obter o ponto inicial e o total de linhas:

int start = 40000 - 10000;

int total = 25000 - 10000;

Em seguida, passe-os por código para a consulta:

SELECT * FROM 
(SELECT * FROM schema.mytable 
ORDER BY userId DESC fetch first {start} rows only ) AS mini 
ORDER BY mini.userId ASC fetch first {total} rows only
Elcool
fonte
Observe que a linha 10000 é excluída do conjunto de resultados, a primeira linha é a 10001ª.
azulado de
1
Solução interessante. Eu ia usá-lo para compatibilidade com o banco de dados de teste H2 ... Mas, infelizmente, ele funciona cerca de 30 vezes mais lento do que a abordagem SELECT row_number () OVER (código ORDER BY).
manuna
9

O suporte para OFFSET e LIMIT foi adicionado recentemente ao DB2 para i 7.1 e 7.2. Você precisa dos seguintes níveis de grupo DB PTF para obter este suporte:

  • SF99702 nível 9 para IBM i 7.2
  • SF99701 nível 38 para IBM i 7.1

Consulte aqui para obter mais informações: documentação OFFSET e LIMIT , DB2 for i Enhancement Wiki

Kevin Adler
fonte
7

Esta é a solução que encontrei:

select FIELD from TABLE where FIELD > LASTVAL order by FIELD fetch first N rows only;

Inicializando LASTVAL para 0 (ou '' para um campo de texto) e, em seguida, definindo-o como o último valor no conjunto de registros mais recente, isso percorrerá a tabela em blocos de N registros.

Tom Barron
fonte
(Inicialmente pensei que você estava definindo o valor na tabela, o que seria espetacularmente problemático em um sistema simultâneo) Sim, isso deve funcionar nos casos em que você está fazendo uma leitura sequencial da tabela, embora seja necessário algum tipo de coluna desempatador no caso em que Né menor do que o número de valores idênticos na coluna (embora isso seja verdadeiro ao usar ROW_NUMBER()também). Os valores iniciais também devem ser escolhidos com cuidado - 0obviamente será problemático se a coluna contiver um valor negativo . Cuidado seria necessário com nulos. Não funcionará se as páginas forem puladas.
Clockwork-Muse de
Obrigado pelo comentário. Acho que há uma suposição implícita de que o campo que estamos usando para controlar a consulta é único e aumenta monotonicamente. Concordo que, se essas suposições não forem válidas, não funcionará visitar todos os registros da tabela. E, é claro, você está certo ao dizer que precisa começar com um LASTVAL que faça sentido. Em geral, acho que você quer começar com o que for retornado por "selecionar MÍNIMO (CAMPO) da TABELA". Se o campo for indexado, a maioria dos mecanismos de banco de dados fará melhor do que ler toda a tabela sequencialmente.
Tom Barron
2

A solução de @elcool é uma ideia inteligente, mas você precisa saber o número total de linhas (que pode até mudar enquanto você executa a consulta!). Portanto, proponho uma versão modificada, que infelizmente precisa de 3 subconsultas em vez de 2:

select * from (
    select * from (
        select * from MYLIB.MYTABLE
        order by MYID asc 
        fetch first {last} rows only 
        ) I 
    order by MYID desc
    fetch first {length} rows only
    ) II
order by MYID asc

onde {last}deve ser substituído pelo número da linha do último registro que preciso e {length}deve ser substituído pelo número de linhas que preciso, calculado como last row - first row + 1.

Por exemplo, se eu quiser linhas de 10 a 25 (no total 16 linhas), {last}serei 25 e {length}25-10 + 1 = 16.

azulado
fonte
Eu desprezo aqueles que votam negativamente quando outra pessoa leva tempo para responder à sua pergunta.
jp2code
1

Você também deve considerar a cláusula OPTIMIZE FOR n ROWS. Mais detalhes sobre tudo isso na documentação do DB2 LUW no tópico Diretrizes para restringir instruções SELECT :

  • A cláusula OPTIMIZE FOR declara a intenção de recuperar apenas um subconjunto do resultado ou de dar prioridade à recuperação apenas das primeiras linhas. O otimizador pode então escolher planos de acesso que minimizam o tempo de resposta para recuperar as primeiras linhas.
David Sky
fonte
1

Tente isto

SELECT * FROM
    (
        SELECT T.*, ROW_NUMBER() OVER() R FROM TABLE T
    )
    WHERE R BETWEEN 10000 AND 20000
Lucio Menci
fonte
0

Existem 2 soluções para paginar com eficiência em uma tabela do DB2:

1 - a técnica usando a função número_da_linha () e a cláusula OVER que foi apresentada em outro post ("SELECT número_da_linha () OVER (ORDER BY ...)"). Em algumas mesas grandes, percebi às vezes uma degradação do desempenho.

2 - a técnica usando um cursor de rolagem. A implementação depende da linguagem utilizada. Essa técnica parece mais robusta em grandes mesas.

Apresentei as 2 técnicas implementadas em PHP durante um seminário no próximo ano. O slide está disponível neste link: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

Desculpe, mas este documento está apenas em francês.

gregphplab
fonte
0

Existem estas opções disponíveis: -

DB2 has several strategies to cope with this problem.
You can use the "scrollable cursor" in feature.
In this case you can open a cursor and, instead of re-issuing a query you can FETCH forward and backward.
This works great if your application can hold state since it doesn't require DB2 to rerun the query every time.
You can use the ROW_NUMBER() OLAP function to number rows and then return the subset you want.
This is ANSI SQL 
You can use the ROWNUM pseudo columns which does the same as ROW_NUMBER() but is suitable if you have Oracle skills.
You can use LIMIT and OFFSET if you are more leaning to a mySQL or PostgreSQL dialect.  
Hector
fonte