

| com exemplos em VB |
| Componente para deixar forms em Vb semelhantes às telas do winnamp |
| Componente para colocar sua aplicação VB no Systray |
| Componente para transformar sua aplicação VB em serviço |
| Ferramentas úteis para quem usa Olap Server |
| |

![]() |
||||||||
|
|
||||||||
Quer
saber mais?
Não deixe escapar essa oportunidade!
Faça um treinamento para Webdeveloper na Búfalo Informática
Paginação com ASP e servidores de banco
Muitos de vocês já devem ter lido artigos sobre paginação de dados em outros sites. Todos os artigos sobre esse assunto que vi até hoje sempre explicam o uso das propriedades do recordset (absolutepage,pagecount,pagesize, etc.), mas nenhum deles explica claramente que para essas propriedades funcionarem o cursor tem que estar no client (cursorlocation=3), ou seja, a cada página de 10 registros exibida, todos os dados do select são recuperados do servidor de banco e transmitidos pela rede para o servidor web, que então decide qual conjunto de 10 registros irá exibir.
Quando estamos trabalhando com bancos Access, então não tem alternativa, por mais que se faça um algorítimo melhor o access sempre trabalhará desta forma. Mas quando estamos trabalhando com SQL Server ou Oracle isso é definitivamente um mal uso dos recursos que temos em mãos.
Assim sendo, esse algorítimo de paginação que utiliza as propriedades do recordset para realizar a paginação não deveria nunca ser utilizado com um bom servidor de dados. O impacto em performance, nos casos mais críticos, pode ser considerável.
Como resolver então ?
Temos que fazer a paginação por conta própria. A instrução select não pode ser sempre a mesma, temos que alterar a instrução select de forma a que ela nos traga sempre uma página de registros diferente. A instrução será alterada conforme seja a 1a entrada na página, o usuário esteja indo para a próxima página ou voltando para a anterior.
Precisamos, ainda assim, de um "marcador", de alguma coisa que nos indique em que página estamos, qual é a próxima e qual é a anterior. Esse "marcador" precisará estar no banco de dados, pois com base nele iremos alterar a instrução SELECT para garantir que iremos buscar no servidor de dados a página certa e trazer para o servidor web apenas os 10 registros que serão exibidos.
Esse "marcador", então, precisará ser um campo da tabela para o qual estaremos utilizando um order by. No nosso exemplo faremos a paginação sobre a tabela PRODUCTS do northwind e utilizaremos o PRODUCTID como campo "marcador".
Para complicar um pouco mais o exemplo, vamos também fazer uma filtragem por CategoryID, a categoria do produto. Então precisamos primeiramente de uma página para perguntar ao usuário a categoria do produto. Não há nada de novo neste código :
<HTML>
<BODY>
<form method=post action="exibir.asp">
Categoria : <select name="txtcat">
<% dim cn,rs
set cn=createobject("adodb.connection")
set rs=createobject("adodb.recordset")
cn.open Application("strConexao")
rs.open "Select categoryid,categoryname from categories",cn
while not rs.eof
response.write("<option value=" & rs.fields("categoryid") & ">" & rs.fields("categoryname") & "</option>")
rs.movenext
wend
rs.close
cn.close
set rs=nothing
set cn=nothing
%>
</select>
<br>
<input type=submit value="Pesquisar">
</form>
</body>
</html>
Vamos chamar este arquivo
de "Pesquisar.ASP". Observe que neste exemplo estou considerando a
string de conexão guardada em uma variável de applicação,
o que poderia ser feito no Global.ASA, mas isso é detalhe. O banco é
SQL Server.
Neste exemplo é recuperado um recordset com os campos categoryid e categoryname da tabela categories e é feita a montagem de options de uma combo, sendo o value dos options o categoryid.
Montada esta página, vamos partir para a montagem da página "Exibir.asp". Observe como o Action do formulário aponta para esta página.
Antes de entrarmos em detalhes de código é importante entender um pouco a estrutura da página exibir.ASP. Esta página precisará ter botões "próximo" e "anterior". Esses botões irão chamar a própria página Exibir.ASP, mas não são simples links : Eles precisam transmitir informações para a página Exibir.ASP, informações que digam a ela em que página ela estava, qual os marcadores atuais e se a movimentação tem que ser para frente ou para trás.
Essas informações transmitidas serão utilizadas logo no início da Exibir.ASP para decidir qual SELECT deverá ser feito. Mas o código de montagem dos formulários que irão transmitir essas informações só estará no final do Exibir.ASP.
Vamos analisar o código do Exibir.ASP aos poucos. Vamos começar com um pequeno pedaço :
<HTML>
<body>
<%
dim cn,rs,tp,a,codprev,codnext
tp=""
set cn=createobjecT("adodb.connection")
set rs=createobject("adodb.recordset")
cn.open "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=."
rs.cursorlocation=2
rs.cursortype=3
if request.form("cod")="" then
rs.open "select top 6 * from products where categoryid=" & request.form("txtcat") & " order by productid",cn
else
if request.form("tp")="prev" then
rs.open "select top 6 * from products where categoryid=" & request.form("txtcat") & " and productid<" & request.form("cod") & " order by productid desc",cn
tp="prev"
else
rs.open "select top 6 * from products a where categoryid=" & request.form("txtcat") & " and productid>" & request.form("cod") & " order by productid",cn
end if
end if
São definidas variáveis, que serão utilizadas durante
o cõdigo. São instanciados os objetos de conexão e recordset,
a conexão é aberta e o cursorlocation do recordset definido
como 2 (server, por estranho que pareça, é mais ágil
que client, mesmo quando pegamos poucos registros) e cursortype 3 (keyset,
precisaremos de movimentações no recordset).
Como citei anteriormente, a página Exibir.ASP chama a si mesma, recursivamente. Nestas chamadas, 3 variáveis são transmitidas : COD, que é o marcador utilizado, TP, que é a identificação do botão pressionado, próximo ou anterior, e TXTCAT, que é a categoria escolhida no pesquisar.ASP, que precisa ser continuamente retransmitida para que a filtragem possa ser mantida.
Usando essas variáveis, faz-se a abertura do recordset. Observe que temos 3 opções de abertura, repare primeiramente como elas são identificadas : Se o código estiver vazio, então é a primeira chamada da Exibir.asp, a página foi chamada diretamente da pesquisar e fará seu primeiro SELECT. Caso contrário verifica-se o botão pressionado (Variável TP). Para o botão anterior existe um SELECT, para o botão próximo existe outro SELECT.
Agora observemos os SELECTs. No primeiro caso, o primeiro SELECT, basta usar o top 5 para retorna no máximo 5 registros e filtrar pelo categoryID. No botão próximo, o 3o caso, além da filtragem por categoryID faz-se uma filtragem por productid, procurando o productid que seja maior que o PRODUCTID guardado em COD, que, no caso do botão próximo, é o último PRODUCTID exibido.
Já no select para o botão anterior exite alguma complexidade a mais. O botão anterior tomou o cuidado de manter na variável COD o primeiro código exibido na página que o usuário estava vendo. Então precisamos mostrar ao usuário os 5 registros que tem código imediatamente inferior a este. É como se fosse um top ao contrário, pegar os 5 últimos registros. Para que isso seja possível fazemos um order by desc, mantemos o top 5 e fazemos a filtragem pelos productIDs inferiores ao PRODUCTID contido na variável COD.
Observe que com o order by DESC obteremos os registros ao contrário, do último para o primeiro, problema que teremos que resolver no trecho de código seguinte :
response.write("<table border=1><tr><td>Produto</td><td>Preço</td></tr>")
if tp="prev" then
rs.movelast
if rs.recordcount=6 then
rs.moveprevious
end if
end if
codprev=rs.fields("productid")
for a=1 to 5
if rs.eof or rs.bof then
exit for
end if
response.write("<tr><td>")
response.write(rs.fields("productname"))
response.write("</td><td>")
response.write(rs.fields("unitprice"))
response.write("</td></tr>")
if tp="prev" then
rs.moveprevious
else
rs.movenext
end if
next
if a > 1 then
if tp<>"prev" then
rs.moveprevious
else
rs.movenext
end if
codnext=rs.fields("productid")
if tp<>"prev" then
rs.movenext
else
rs.moveprevious
end if
end if
Nesse trecho de código vamos exibir o recordset. Observe que se o usuário clicou no botão anterior teremos que fazer a exibição ao contrário, por isso começamos com um if que verifica o TP e faz um movelast.
Antes de exibir o primeiro registro já pegamos seu código e guardamos na variável codprev para utilizarmos posteriormente. Em seguida iniciamos o FOR. Nele garantiremos a quantidade de registros que serão exibidos, 5.
Iniciamos o FOR verificando se atingimos BOF ou EOF, caso sim, saimos do FOR pois chegamos ao final dos registros. Caso contrário mostramos os dados, só dois campos, productname e unitprice.
Ao final do FOR passamos para o próximo registro, mas a movimentação também depende do TP : Se for anterior, precisamos fazer moveprevious, se for próximo precisamos fazer movenext.
Após o FOR iniciamos alguns testes para pegar o codnext, código do último registro exibido. Tudo envolto em um if a>1 (tinha algum registro ?). Se o TP for próximo fazemos um moveprevious (estamos no EOF), se for anterior, fazemos um movenext (estamos no bof). Obtemos o código que desejamos e fazemos outro if para voltarmos exatamente para o ponto em que estavamos.
Vamos então montar os botões próximo e anterior :
if (tp="prev" and rs.recordcount>5) or (tp<>"prev" and request.form("cod")<>"") then
response.write("<form method=post action='exibir.asp'>")
response.write("<input type=hidden name='tp' value='prev'>")
response.write("<input type=hidden name='cod' value='" & codprev & "'>")
response.write("<input type=hidden name='txtcat' value='" & request.form("txtcat") & "'>")
response.write("<input type=submit value='<< Anterior'>")
response.write("</form>")
end if
if tp="prev" or (tp<>"prev" and not rs.eof) then
response.write("<form method=post action='exibir.asp'>")
response.write("<input type=hidden name='tp' value='next'>")
response.write("<input type=hidden name='cod' value='" & codnext & "'>")
response.write("<input type=hidden name='txtcat' value='" & request.form("txtcat") & "'>")
response.write("<input type=submit value='Proximo >>'>")
response.write("</form>")
end if
Vamos analisar o código aos poucos. Cada botão é cercado de um IF, que tem como objetivo decidir se o botão tem mesmo que aparecer. Veja a lógica :
Anterior : Se o tipo for anterior e existirem mais de 5 registros no recordset (trouxe 6 de propósito) então exibe. Ou se o tipo for próximo (não for anterior e não estivermos na primeira exibição). Então é porque existe uma página anterior e vamos exibir o botão anterior.
Próximo : Se o tipo for anterior, então existe um próximo e o botão tem que ser exibido. Mas se o tipo não for anterior mas ainda não estivermos no EOF (trouxe 6 registros, lembra?) então o botão próximo deve ser exibido.
Vejamos então como os botões são criados. Eles são basicamente formulários onde a única coisa visível é o botão propriamente. Temos no formulário 3 campos hidden, os 3 que já havia citado : TP, COD e TXTCAT, TP é fixo, diferente apenas de um botão para o outro. TXTCAT é a categoria que vem sendo passada em todas as chamadas e COD contém o valor ou de codnext ou de codprev, dependendo do botão.
Por fim, falta apenas a destruição dos objetos e o fechamento das tags. Veja como fica o código completo :
<HTML>
<body>
<%
dim cn,rs,tp,a,codprev,codnext
tp=""
set cn=createobjecT("adodb.connection")
set rs=createobject("adodb.recordset")
cn.open "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=."
rs.cursorlocation=2
rs.cursortype=3
if request.form("cod")="" then
rs.open "select top 6 * from products where categoryid=" & request.form("txtcat") & " order by productid",cn
else
if request.form("tp")="prev" then
rs.open "select top 6 * from products where categoryid=" & request.form("txtcat") & " and productid<" & request.form("cod") & " order by productid desc",cn
tp="prev"
else
rs.open "select top 6 * from products a where categoryid=" & request.form("txtcat") & " and productid>" & request.form("cod") & " order by productid",cn
end if
end if
response.write("<table border=1><tr><td>Produto</td><td>Preço</td></tr>")
if tp="prev" then
rs.movelast
if rs.recordcount=6 then
rs.moveprevious
end if
end if
codprev=rs.fields("productid")
for a=1 to 5
if rs.eof or rs.bof then
exit for
end if
response.write("<tr><td>")
response.write(rs.fields("productname"))
response.write("</td><td>")
response.write(rs.fields("unitprice"))
response.write("</td></tr>")
if tp="prev" then
rs.moveprevious
else
rs.movenext
end if
next
if a > 1 then
if tp<>"prev" then
rs.moveprevious
else
rs.movenext
end if
codnext=rs.fields("productid")
if tp<>"prev" then
rs.movenext
else
rs.moveprevious
end if
end if
if (tp="prev" and rs.recordcount>5) or (tp<>"prev" and request.form("cod")<>"") then
response.write("<form method=post action='exibir.asp'>")
response.write("<input type=hidden name='tp' value='prev'>")
response.write("<input type=hidden name='cod' value='" & codprev & "'>")
response.write("<input type=hidden name='txtcat' value='" & request.form("txtcat") & "'>")
response.write("<input type=submit value='<< Anterior'>")
response.write("</form>")
end if
if tp="prev" or (tp<>"prev" and not rs.eof) then
response.write("<form method=post action='exibir.asp'>")
response.write("<input type=hidden name='tp' value='next'>")
response.write("<input type=hidden name='cod' value='" & codnext & "'>")
response.write("<input type=hidden name='txtcat' value='" & request.form("txtcat") & "'>")
response.write("<input type=submit value='Proximo >>'>")
response.write("</form>")
end if
rs.close
cn.close
set rs=nothing
set cn=nothing
%>
</table>
</body>
</html>
Com a personalização dos SELECTS feita por este código garantimos que estamos fazendo o mínimo de transferência de dados entre servidor web e servidor de banco a cada página de dados que é exibida.
Dennes Torres
MCSD,MCSE,MCDBA
� Búfalo Informática,
Treinamento e Consultoria -
Rua Ãlvaro Alvim, 37 Sala 920 - Cinelândia - Rio de Janeiro / RJ
Tel.: (21)2262-1368 (21) 9240-5134 (21) 9240-7281 e-Mail: contato@bufaloinfo.com.br