LIMIT na sentença SQL - Como fazer?

442 views
Skip to first unread message

pedro.cardoso

unread,
Jun 30, 2009, 10:47:44 AM6/30/09
to GUG Porto Alegre/Brasil
Pessoal, estou enfrentando um problemão em uma pesquisa que filtra em
torno de 900 mil registros.

Qual a situação? Dentro do for each são feitas algumas validações
(essas não podem e não fazem parte do que é filtrado), estando as
validações ok, dou o Exit. Bom, dessa forma, a sentença retornaria
realmente TODOS os 900 mil registros, o que a torna inviável, e
literalmente "senta" o banco de dados.

Copiei a sentença gerada pelo genexus e coloquei um LIMIT de 1000
registros no final, a sentença é executada na hora. Certamente dentro
dos 1000 registros logo os primeiros iriam atender a condição, caso
nenhum deles atendesse, eu faria a aplicação realizar novamente o
SELECT com um offset de 1001, e assim sucessivamente, contando que sem
dúvida já nos primeiros 1000 seria atendida minha necessidade. Isso é,
eu não preciso retornar os 900 mil, mas o meu filtro é em cima deles.

Este é o problema, como eu uso a sentença LIMIT no genexus? o Exit
apenas sai do loop, já após ter retornado todos os dados do SELECT.
Até mesmo colocando somente o Exit dentro do meu For each (como
abaixo), ele retorna os 900 mil! Quer dizer, nada otimizado, se uso o
Exit direto dentro do For Each, pq não gerar um LIMIT de 1 na minha
sentença SQL? Ao invés de retornar tudo e sair do loop no primeiro?

For Each
Where ClienteRelacaoCodigo = &RelacaoCodigo
Where ClienteOperacao = 2
Where ClienteVendedorCodigo = 0
Where ClienteVendas = &FocoVendaStatus
Exit
EndFor

Então fica a pergunta, como fazer LIMIT numa consulta Genexus, e
OFFSET (esse é menos importante, mas seria interessante também)?

Obrigado

Pedro Cardoso

Fernando Gustavo Flôres

unread,
Jun 30, 2009, 10:53:01 AM6/30/09
to gugportoal...@googlegroups.com
Pedro,

No GX X você poderia tentar usar o "blocking" do For Each:

http://wiki.gxtechnical.com/commwiki/servlet/hwiki?Blocking+clause+in+a+%27For+each%27+command,

Saludos,

2009/6/30 pedro.cardoso <pedroca...@gmail.com>



--
Flôres, Fernando Gustavo
Analista Sênior GeneXus
DelSoft Sistemas
Linux User 384457
Rio do Sul/SC
http://www.delsoftsistemas.com.br

http://www.gustavoflores.com.br
{São Judas Tadeu, rogai por nós}

/*
Este é o Brasil de bombacha
É a saga da raça guerreira
Nos fundões sesta pátria se acha
Um gaúcho abrindo fronteira
*/

pedro.cardoso

unread,
Jun 30, 2009, 11:06:19 AM6/30/09
to GUG Porto Alegre/Brasil
Bom dia Gustavo,

Pelo que entendi o blocking apenas executa uma série de operações (o
que tu tiver dentro do for each) em blocos, para melhorar a
performance, do tipo, comitar de 50 em 50, se tu estiver alterando
diversos registros.

No meu caso nao adiantaria, ele iria ler os 900 mil, que é o que senta
o banco, eu preciso de um LIMIT mesmo!!

Abraços e obrigado!

On 30 jun, 11:53, Fernando Gustavo Flôres <fgustavoflo...@gmail.com>
wrote:
> Pedro,
>
> No GX X você poderia tentar usar o "blocking" do For Each:
>
> http://wiki.gxtechnical.com/commwiki/servlet/hwiki?Blocking+clause+in...
> ,
>
> Saludos,
>
> 2009/6/30 pedro.cardoso <pedrocardoso...@gmail.com>
> Rio do Sul/SChttp://www.delsoftsistemas.com.br

Armin Bachmann

unread,
Jun 30, 2009, 11:29:25 AM6/30/09
to gugportoal...@googlegroups.com
Estimados, el limit es algo que no estamos generando en postgresql en estos casos.
En los otros DBMSes existe el TOP que tampoco generamos en este caso pero en los otros dbmses hay hints como "fast first rows" que no lo hacen (tan) necesario.
Estamos evaluando alternativas, todavía no lo tenemos implementado.
Puede ser que implementemos algo para la release de Evolution 1, pero todavía no puedo confirmarlo. Cuando tenga noticias les escribo.

Solo de curiosidad: en el caso de uds, cuánto mejoran los tiempos y el uso de recursos cuando en el fuente generado por ejemplo modifican la SQL agregando el LIMIT, compilan y ejecutan de nuevo?

Saludos, Armin

Daniel Wildt

unread,
Jun 30, 2009, 11:52:43 AM6/30/09
to gugportoal...@googlegroups.com
Armin, 

Isto está impactando diversas aplicações que gerencio, com consumo de banda de servidor e de desempenho do ambiente como um todo.  

Existe alguma previsão? Isto é algo que impacta mesmo. 

As aplicações utilizam banco de dados PostgreSQL.

-- 
Regards,
Daniel Wildt
http://danielwildt.com

2009/6/30 Armin Bachmann <ar...@artech.com.uy>

Armin Bachmann

unread,
Jun 30, 2009, 11:57:01 AM6/30/09
to gugportoal...@googlegroups.com

Daniel, en el momento no tengo más previsión que la esperanza de que podamos incluirlo en la Evolution. Sabemos que impacta mucho pero carecemos de datos concretos en instalaciones concretas.

Tener datos precisos del impacto que generaría en sus instalaciones la implementación de esta funcionalidad ayudaría a dar la prioridad debida a este asunto.

Si sus desarrolladores pudieran modificar la SQL generada y medir el impacto dándonos datos, sería de mucha ayuda. Igualmente estamos estudiando el tema y queremos implementarlo, una cosa no quita la otra.

 

Saludos, Armin

Daniel Wildt

unread,
Jun 30, 2009, 12:19:46 PM6/30/09
to gugportoal...@googlegroups.com
Estamos com um consumo extra de mais de 3Gb de tráfego de rede em virtude de faltar este tipo de funcionalidade. 

Acho que isto dá uma idéia. Nunca vi isto ocorrer em 12 anos desenvolvendo software.

Que tipo de informação exatamente você precisa?

Att,
Daniel Wildt

2009/6/30 Armin Bachmann <ar...@artech.com.uy>

pedro.cardoso

unread,
Jun 30, 2009, 1:06:26 PM6/30/09
to GUG Porto Alegre/Brasil
Boa tarde amigos,

Pelo jeito esse é um problema sem solução, apenas com alternativas de
contorno, como a utilização de views, alteração de código fonte, ou
até mesmo utilização de tabelas temporárias.

Armin, quanto a sua pergunta sobre a melhoria dos tempos: Nessa
pesquisa por exemplo, que tenho que trabalhar com um range de 900 mil
registros, rodando a consulta (SQL gerado pelo genexus) direto no
pgadmin, depois de 10 minutos esperando cancelei a consulta,
utilizando o limit de 1000 registros a consulta foi executada no mesmo
instante, nem 1 segundo pra retornar os dados.

Vou ter que fazer testes aqui alterando os programas gerados, ver se
tem algum problema simplesmente colocar o limit na sentença, ou se
isso vai acabar refletindo no tratamento depois.

O ideal seria o genexus ter a cláusula Limit e Offset para o For Each.
Por exemplo, se eu realizar essa consulta limitando 1000, mas nos
primeiros 1000 eu não encontrar o registro desejado (validações que
seriam feitas dentro do for each, por exemplo), eu faria a próxima
consulta utilizando um offset de 1001 e limit de 1000, e assim
sucessivamente.

Esse é um problema sério, que afeta diretamente aplicações com grandes
bases de dados, acredito que a Artech precisa ver essa questão o
quanto antes.

Abraços.

Pedro C.

Alexandre Muskus Pawlowski

unread,
Jun 30, 2009, 1:58:23 PM6/30/09
to gugportoal...@googlegroups.com
Olá amigos do forum,

Se eu tiver o limit 50 implementado no sql em uma grid com paginação de 50 linhas. o que o genexus fará?
 
Será que o problema de implementar limit no genexus é nestas questões??

Ao invez do limit implementado no postgres prefiro a forma que preciso fazer no oracle, ou seja, o select gerado com uma coluna sendo o contador é adicionado em um select que seleciona o intervalo que será apresentado, com isto a performance em paginação já ganha um performance absurda quando houver muitos registros.

Obrigado pela atenção.
2009/6/30 pedro.cardoso <pedroca...@gmail.com>

--
Alexandre Muskus Pawlowski,

pedro.cardoso

unread,
Jun 30, 2009, 2:30:23 PM6/30/09
to GUG Porto Alegre/Brasil
Bom pessoal, "resolvi" o problema mexendo no fonte gerado, adicionei a
linha com a cláusula limit na string que o genexus monta o select:

scmdbuf = scmdbuf + " ORDER BY T1.ClienteRelacaoCodigo,
T2.ClienteOperacao, T2.ClienteVendedorCodigo, T2.ClienteVendas" ;
scmdbuf = scmdbuf + " LIMIT 500";

No GeneXus, fiz um while por fora do For Each (que continua do ponto
que parou, caso esses 500 não fossem o suficiente pra encontrar o
registro desejado) mais ou menos assim:

&ClienteCodigoWhile = 0

Do While &achouClienteNosCriterios = 0

For Each ClienteRelacaoCodigo ClienteOperacao
ClienteVendedorCodigo
Where ClienteRelacaoCodigo = &RelacaoCodigo
Where ClienteOperacao = 2
Where ClienteVendedorCodigo = 0
Where ClienteCodigo > &ClienteCodigoWhile
// Essa pesquisa retornaria em torno de 900 mil registros
se não tivesse o limit de 500 ...

Do 'ValidacoesGerais'
if &pegaCliente = 1
&achouClienteNosCriterios = 1
Do 'RotinaX'
Exit
endif

&UltimoClienteCodigo = ClienteCodigo
EndFor
&ClienteCodigoWhile = &UltimoClienteCodigo
EndDo

Abraços.

Pedro C.

Cláudio Schirmer Guedes

unread,
Jun 30, 2009, 1:16:43 PM6/30/09
to gugportoal...@googlegroups.com
Ola,

Existe no objeto DataProvider umas Clausulas [Skip / Count] que devereiam justamente servir para situações como essa, mas conversando com colegas de serviço foi verificado que não está funcionando corretamente. Já enviamos um comunicado para Artech.

Att,
Cláudio Schirmer Guedes.



2009/6/30 Daniel Wildt <dwi...@gmail.com>

Armin Bachmann

unread,
Jun 30, 2009, 3:04:17 PM6/30/09
to gugportoal...@googlegroups.com
Pedro, una pregunta: con qué versión de GeneXus estás?



-----Original Message-----
From: gugportoal...@googlegroups.com [mailto:gugportoal...@googlegroups.com] On Behalf Of pedro.cardoso
Sent: Martes, 30 de Junio de 2009 11:48 a.m.
To: GUG Porto Alegre/Brasil

pedro.cardoso

unread,
Jun 30, 2009, 3:15:23 PM6/30/09
to GUG Porto Alegre/Brasil
Armin, estou na 9.0 com U5.

abraços

Armin Bachmann

unread,
Jun 30, 2009, 3:33:19 PM6/30/09
to gugportoal...@googlegroups.com
Pedro y todos, disculpen las confusiones.
Hay 2 temas aquí.
1) lo específicamente reportado por Pedro:
Si se tiene trn1 con A* B y se hace un
For each
Where B = 1
&A = A
exit
Endfor
Esto A partir de GX 9.0 y la implementación de http://wiki.gxtechnical.com/commwiki/servlet/hwiki?For+Each+Exit, debería generar un
SELECT B, A FROM TRN01 WHERE B = 1 ORDER BY A LIMIT 1

Hay un BUG que solamente sucede con .NET y ADO.NET. En este caso está generando
new GeneXus.Data.NTier.ADO.CursorDef("P00012", "SELECT B, A FROM TRN01 WHERE B = 1 ORDER BY A ",false, GxErrorMask.GX_NOMASK | GxErrorMask.GX_MASKLOOPLOCK, false, this,prmP00012,10,0,false,true )
Esta bien que no genere el limit 1 en la sentencia porque el dataprovider de postgresql tiene un bug que hace que cuando es una sentencia single row le agrega automáticamente el limit 1.
Lo que sí esta mal es el 10 ese que esta ahí al final, antes del "0, false, true", deberia ser 1 (que es el blocksize, porque solo cuando es 1 hace que se ejecute como single row y por tanto el driver le agregue el limit 1).


En .NET con ODBC y en JAVA lo esta generando OK, con LIMIT.

Agregué un SAC 26688 y les pido que vean por las vías oficiales de soporte (www.gxtechnical.com/tracking ) de obtener un fix si es necesario.

2) Casos en los que podríamos usar LIMIT y OFFSET y no usamos, por ejemplo en los data providers con SKIP y COUNT o en web panels con paginación.
Esto último es lo que estamos pensando investigando para mejorar en GeneXus X Evolution 1 y sería una optimización que aunque tiene más efecto en PostgreSQL por no contar con el hint "fast first rows" tendría mejoras para todos los DBMSes.

Disculpen las confusiones, espero que se haya esclarecido ahora.
Saludos, Armin

pedro.cardoso

unread,
Jun 30, 2009, 4:10:02 PM6/30/09
to GUG Porto Alegre/Brasil
Olá Armin,

Realmente é isso que está acontecendo, minha aplicação é .NET, ADO.
Observei no código gerado e está com 10 ao invés de 1:

new GeneXus.Data.NTier.ADO.CursorDef("P000I5", "scmdbuf",false,
GxErrorMask.GX_NOMASK | GxErrorMask.GX_MASKLOOPLOCK, false,
this,prmP000I5,10,0,true,false ) // lembrando que foi na string
scmdbuf que eu concatenei o limit depois, e funcionou

Eu suspeitava que o exit direto no for each realmente colocava o
limit, acabei ficando confuso hoje com esse caso, mas então estou numa
situação específica de bug. De qualquer forma seria interessante a
cláusula limit no genexus, já que apenas o exit imediatamente antes do
endfor gera o limit, o que na minha situação, forçaria a leitura de um
a um (até achar o desejado claro) e controle do while para continuar
no que parou, sendo que se eu pudesse especificar o limit (500 por
exemplo) eu poderia ler um range maior, assim como no exemplo que eu
fiz, alterando o código fonte.

Abraços e obrigado!

Pedro C.

On 30 jun, 16:33, "Armin Bachmann" <ar...@artech.com.uy> wrote:
> Pedro y todos, disculpen las confusiones.
> Hay 2 temas aquí.
> 1) lo específicamente reportado por Pedro:
> Si se tiene trn1 con A* B y se hace un
> For each
> Where B  = 1
> &A = A
> exit
> Endfor
> Esto A partir de GX 9.0 y la implementación dehttp://wiki.gxtechnical.com/commwiki/servlet/hwiki?For+Each+Exit, debería generar un
> SELECT B, A FROM TRN01 WHERE B = 1 ORDER BY A  LIMIT 1
>
> Hay un BUG que solamente sucede con .NET y ADO.NET. En este caso está generando
> new GeneXus.Data.NTier.ADO.CursorDef("P00012", "SELECT B, A FROM TRN01 WHERE B = 1 ORDER BY A ",false, GxErrorMask.GX_NOMASK | GxErrorMask.GX_MASKLOOPLOCK, false, this,prmP00012,10,0,false,true )
> Esta bien que no genere el limit 1 en la sentencia porque el dataprovider de postgresql tiene un bug que hace que cuando es una sentencia single row le agrega automáticamente el limit 1.
> Lo que sí esta mal es el 10 ese que esta ahí al final, antes del "0, false, true", deberia ser 1 (que es el blocksize, porque solo cuando es 1 hace que se ejecute como single row y por tanto el driver le agregue el limit 1).
>
> En .NET con ODBC y en JAVA lo esta generando OK, con LIMIT.
>
> Agregué un SAC 26688 y les pido que vean por las vías oficiales de soporte (www.gxtechnical.com/tracking) de obtener un fix si es necesario.
Reply all
Reply to author
Forward
0 new messages