Newsgroup: it.comp.software.database
Titolo: ADO, recordset e paginazione - SQLserver2000

Salve a tutti, ho una domanda da fare. Sto facendo un progetto su
piattaforma MS conIIS5.1 e SQLserver 2000. In pratica sto costruendo (in
ASP/ADO) una pagina che mostra una lista di risultati di una query. Fin
qui nessun problema, imposto la paginazione del recordset e mi muovo
senza problemi. Mi è però sorto un dubbio: il risultato della mia query
potrà produrre migliaia di record, ora ogni volta il recordset
risultante viene inviato da SQLserver ad IIS e li avviene la paginazione
(impostando ActivePage). Dove sta l'inghippo? 1) SQLserver invia ad IIS
l'intero rs? vero o falso? 2) Solo su IIS (che funge da client rispetto
ad IIS) avviene la scelta della pagina da mostrare? vero o falso? 3) se
il rs risultante è immenso per poi mostrare solo 10-20 risultati per
pagina c'è un evidente spreco di risorse, come fare? In pratica, se fo
una query ad un motore di ricerca ed esso mi trova un milioncino (ma
anche solo qualche centinaio) di link questi non sono certo paginati sul
web server. Come posso fare? grazie a chiunque vorrà illuminarmi visto
che probabilmente ho leidee poco chiare! Lorenzo.
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 Come posso fare?
Non ritornare i risultati se sono piu' di un centinaio. Nessuno si
"scorre" un'elenco di 100 record, manco per pagine. Presenta una maschera
di ricerca ed aspetta che l'utente selezioni dei criteri di ricerca. Se
escono piu' di un centinaio di record avvisa l'utente e suggerisci di
migliorare i criteri di ricerca.
Davide

 ha scritto nel messaggio
news:ai5nf1$10vs1b$1...@ID-18487.news.dfncis.de...

Interessa anche a me. Ma se cerco "Mario Rossi" ed è quello l'unico criterio
di ricerca, anche perchè il database contiene solo quelle informazioni e
altre che non conosco e, comunque, mi interessa l'intero elenco (lo so, sto
tirando il cinghiale per la coda) e di Mario Rossi ce n'è 15.000?

 di ricerca, anche perchè il database contiene solo quelle informazioni e
Se il database contiene solo Nome e Cognome, non puoi avere piu' di _un_
"Mario Rossi" (chiave duplicata), anche sull'elenco del telefono i nomi
sono suddivisi e ti metti a cercarli in ordine alfabetico, ma anche per
citta' (quanti Mario Rossi ci sono a Milano ? quanti ce ne sono in
"Via della Spiga" ?). Se il db contiene piu' di un "Mario Rossi" e tu non
sei in grado di definire quale e' quello che ti interessa, vedere l'elenco
non ti da' nessun vantaggio.
Puoi sempre domandare all'utente "Ci sono 145.346.190.191,3 risultati... li
vuoi vedere tutti ?" Se l'utente dice "Si" memorizzalo e manda la neurodeliri
a casa sua che ne ha tanto bisogno...
Davide

 a casa sua che ne ha tanto bisogno...
Davide, supponiamo che un utente (pazzo da neurodeliri) voglia vedere
_tutti_ i suoi articoli per, che so, verificare che la descrizione sia
corretta. Sono 30.000. Che fare se non caricare tutto?

 wrote in message
news:71dc933d8743e799eb...@mygate.mailgate.org...

Ciao Lorenzo,
per quanto ne so io il fatto che SQL Server mandi l'intero recordset al
client e' determinato dal tipo di cursore associato al recordset.
Se il cursore e' client side allora l'intero recordset viene spedito
al web server che decide su quale pagina si deve muovere il cursore
(e' il cursore che leggendo riga per riga "visualizza" i dati).
Nota che il codice solitamente apre la connessione, apre il recordset,
sceglie la pagina, stampa i record, chiude il cursore ed infine chiude
la connessione. Alla prossima chiamata (ad esempio per muoversi sulla
seconda pagina) tutte queste operazioni deve essere nuovamente eseguite
(i dati dovranno essere rispediti).
Se invece il cursore e' server side il client riceve solo i dati richiesti
muovendo il cursore (nota che non vengono spedite le singole righe ma
blocchi di righe).
Il rovescio della medaglia dei cursori server side e' che sono piuttosto
onerosi da mantenere; a seconda della tipologia (adOpenStatic, adOpenKeyset
oppure adOpenDynamic) viene creata una copia del result, o una copia delle
chiavi primarie, nel tempdb. Inoltre ogni n movimenti del cursore e'
necessario che il client parli con il server per coordinare il movimento
del cursore stesso (che vive sul server).
Anche in questo caso vale quanto detto per i cursori client side: ogni
volta che la pagina viene chiamata, con la tecnica sopra descritta, il
cursore deve essere ricreato (tanto per fare un esempio potrebbero essere
decine di migliaia di righe da copiare nel tempdb, magari moltiplicato
per un centinaio di utenti che stanno accedendo concorrentemente al
web server...)
D'altra parte pensare di aprire connessione e recordset e tenerli attivi
per la sessione e' una pratica non raccomandata perche' potenzialmente
i client potrebbero non richiedere mai piu' niente (si disconnettono
dall'applicazione) e gli oggetti rimangono allocati fino al raggiungimento
del timeout della sessione. Inoltre si perdono i vantaggi del "connection
pooling".
Che fare allora ?
Dipende, se non ci sono problemi di scalabilita' (pochi utenti, poco
traffico) la soluzione di paginare come hai prospettato puo' comunque
essere la piu' semplice da realizzare e quindi la piu' opportuna
(la scelta del cursore lato client o lato server sarebbe determinato
solo dal numero di record totali).
Attenzione al fatto che l'opportunita' e' data anche dalla
consapevolezza che l'applicativo se incontrera' problemi di
scalabilita' dovra' poi essere reingegnerizzato.
In alternativa puoi pensare di implementare l'applicativo a piu'
livelli, in questo caso uno strato di oggetti potrebbe implementare
efficacemente la cache dei dati e la loro paginazione.
Un'altra soluzione potrebbe essere quella proposta in questo articolo
http://www.sql-server-performance.com/asp_sql_server.asp
(ricerca per "paging" nella pagina) e cioe' di fare la
paginazione direttamente con una stored procedure.
Tutto questo a livello tecnico, personalmente condivido al 100%
cio' che ha detto Davide.
In passato la maggior parte delle applicazioni sono state realizzate
sul modello della "scrolling application", dove cioe' l'utente apre
consultazione una tabella e nella maggior parte dei casi o fa
riconoscimento visivi, oppure applica dei filtri (vedi Access o
un qualunque ambiente ISAM della vecchia leva).
Ad esempio: anagrafico dei contatti, l'utente apre in consultazione
l'intera tabella (1000 contatti) poi preme l'iniziale del nome
(o del cognome) o si muove con la "scroll-bar" verso i nomi che
hanno quell'iniziale. A questo punto inizia il riconoscimento
visivo (se ci sono piu' Mario Rossi l'utente guardera' anche
il valore degli altri attributi).
Per dare questo tipo di funzionalita' in un ambiente a piu'
livelli (generalmente si spendo molte risorse) quando sarebbe
piu' efficace educare l'utente ad usare delle tecniche di filtro
impostando dei criteri di ricerca (che sono molto graditi agli
RDBMS se ci sono gli indici opportuni) limitando l'insieme di
record da visualizzare.
I miei .02 cents.
Ciao,
--
Gianluca Hotz
Backoffice MVP (SQL Server) MCP SQL Server and MCP Windows
Vice-President of the Italian User Group for SQL Server
Founding Member of the Italian User Group for .NET
http://www.ghotz.com
http://www.ugiss.org

 wrote in message
news:6Nv19.17463$n04.5...@twister2.libero.it...

Ciao Lupo,
supponiamo che l'utente impiegi 5 secondi per controllare
ogni singolo articolo, ci vogliono quasi 42 ore per
terminare il lavoro (piu' o meno una settimana lavorativa).
In un caso come questo potrebbe essere piu' opportuno dare
i dati all'utente in un formato piu' pratico da maneggiare
(ad esempio un foglio Excel) che non farglielo fare online
su un web-server.
Oppure potrebbe essere vincente il fatto di paginarli
"logicamente" ad esempio per categoria, per iniziale
del nome, dall'articolo numero all'articolo numero
(sarebbero delle query specifiche, non la paginazione
su un sovrainsieme dei dati che gli servono di volta
in volta perche' tanto alla fine li' dovra' visionare
tutti).


 terminare il lavoro (piu' o meno una settimana lavorativa).
Ciao Gianluca, ovvio che mi pongo il problema perchč qualche utente che lo
vuole c'č, altrettanto ovvio che il tuo esempio "esasperato" mi trova
assolutamente d'accordo, ma il problema č piů "sottile" e anche realistico:
Supponiamo che io voglia vedere tutti i miei articoli per poterli scorrere
come piace a me. Non mi interessa di leggerli tutti, ma voglio, per esempio
ordinarli per unitŕ di misura, poi per giacenza, poi per prezzo, cosě,
perchč mi va. Oppure voglio stamparli tutti! Ora, sappiamo tutti che nella
realtŕ tutto ciň avviene raramente, ma il problema, per quelle poche volte,
esiste.
Io che faccio? Se non ci sono scelte compongo tutto il recordset (Select *
From Articoli) e poi ne espongo un centinaio di righe in una griglia.
L'operatore scorre la griglia e puň andare a pagina seguente, precedente,
prima, ultima, cercare per iniziale e/o per contenuti, etc. e deve (IMHO)
poterlo fare. Se il mio recset č completo io posso spostarmi al suo interno
come voglio e mostro sempre la "finestra" di 100 righe correnti, ma se il
recordset ne contiene, supponiamo, solo 1000 (delle 30.000 totali) come
posso dire al motore di spostarsi in una zona (recset) ... che non c'č?

 posso dire al motore di spostarsi in una zona (recset) ... che non c'č?
Questo se lo deve gestire il motore stesso. Se gli chiedi un pezzo di
recordset che non e' stato caricato, se lo dovra' caricare (e ci mettera'
il tempo che ci vuole). E questo e' cio' che di solito i driver 
"abbastanza furbi" fanno. Caricano il minimo indispensabile e poi si
gestiscono le richieste in base a quello che c'e' in memoria ed a quello
che non c'e'.
Davide

 che probabilmente ho leidee poco chiare! Lorenzo.
Mi sono scontrata con il tuo stesso problema qualche tempo fa ed ho risolto
con una procedurache avevo trovato e che ho
poi modificato per adattarla al caso, effettua una "paginazione" ed ogni
volta ti restituisce
i record nel numero e nel range da te richiesto:
Prende in input il numero di pagina e il numero di record che vuoi
per pagina (es: sp_paginazione 2,10 ti restituisce i record dall`undicesimo
al ventesimo)
Non so se puo` esserti utile.
FRILLA
***************
CREATE PROCEDURE SP_Paginazione
 (
  @Pagina int,
  @NumRecPerPagina int
 )
AS
SET NOCOUNT ON
/*Creazione della tabella variabile di appoggio*/
Declare @Temp Table
(
 ID int IDENTITY,
 Nome varchar(50)
)
/*Inserisco i record che servono nella variabile tabella*/
INSERT INTO @Temp
SELECT Nome FROM utenti
/*Determino il primo e l`ultimo record da visualizzare*/
DECLARE @First int, @Last int
SELECT @First = (@Pagina - 1) * @NumRecPerPagina
SELECT @Last = (@Pagina * @NumRecPerPagina + 1)
/*Si restituiscono i record chiesti piu` il campo morerecords che indica
quanti ne restano a seguire*/
SELECT *,
       MoreRecords =
 (
  SELECT COUNT(*)
  FROM @temp
  WHERE ID >= @Last
 )
FROM @temp
WHERE ID > @First AND ID < @Last
SET NOCOUNT OFF

Wow! non credevo mi rispondeste in così tanti e così accuratamente.
Devo dire che tutte le vostre risposte mi hanno migliorato la visione
della faccenda. Oggi ho provato la paginazione impostando il pagesize
del recordset e l'absolutepage per scegliere la pagina. Il fatto è che
uso un cursore client side per sfruttare il metodo Sort del recordset
per ordinare il campo, il fatto è che avendo il cursore lato client in
pratica ho l'intero rs percui anche se mostro solo 10 risultati per
pagina e poi magari l'utente li ordina per data.. viene riordinato
l'intero rs e i dati della pagina attuale si ridistribuiscono :-(
Peccato perchè avevo realizzato tutto per benino...
Per evitare confusione ecco cosa vorrei fare:
ho una lista di interventi di tecnici memorizzata su SQLserver.
Gli estremi di tale lista (cliente, tecnico, descrizione intervento)
sono su più tabelle del DB.
Ora io vorrei mostrare al tecnico la lista degli interventi svolti (da
lui e dai suoi colleghi) negli ultimi  mesi.. e fin qui non c'è
problema... una bella select risolve tutto... se non che questi
interventi sono molti e vorrei fare una paginazione...
Anche qui, usando il cursore lato client, absolutepage, pagesize ed un
paio di pulsanti avanti/indietro riesco a muovermi nel rs. Ora premesso
che la dimensione di questo rs non sarà mai maggiore di qualche
centinaio e che i campi del rs sono si e no una decina è una soluzione
giusta la mia??
Se il tecnico sta a premere 20 volte il pulsante avanti per vedere un
intervento lontano... non mi importa... voglio solo implementare una
piccola navigazione locale a paginazione sugli ultimi interventi
svolti.. lasciando solo la possiilità di fare un paio di  "avanti" in
più nel caso servissero.
Nel caso la mia soluzione fosse accettabile ecco il problema numero 2:
cliccando sull'intestazione della colonna vorrei far ordinare le righe
VISIBILI su quel criterio. Usando recset.Sort riesco si ad ordinare per
quel criterio.. ma l'intero rs! (difatti coi cursori client side ce l'ho
tutto disponibile sul client) perdendo di fatto la "vista" attuale sulle
righe che stavo visualizzando.
Per ora ho risolto ignobilmente la cosa riportando l'utente sulla prima
pagina del rs appena ordinato (he he così poi deve farsi un'altra
sgropponata di click per tornare più o meno dove era)...
Usando sort devo forzatamente avere il cursore lato client e quindi vi
chiedo: che posso fare?
La comodità di sort è tanta perchè mi evita di fare ulteriori query al
db (anche complesse visto che le colonne mostrate sono il frutto della
query:
SELECT ha_ident, ha_descr, ha_oreviag, ha_data ,
       ha_cliente=COALESCE(ha_cliente,-1) ,
       hn_fullname,
       COALESCE(an_descr1,ha_clientena) AS nomeCliente,
       COALESCE(hs_nome,'Nessuna') AS nomeSede, "
       COALESCE(co_descr1,'Nessuna') AS nomeCommessa "
       FROM hhattivt
       JOIN hhsnt ON hn_ident=ha_tecnico
       LEFT JOIN anagra ON an_conto=ha_cliente
       LEFT JOIN hhsedi ON hs_ident=ha_sede
       LEFT JOIN commess ON co_comme=ha_commess
       WHERE DateDiff(Month,ha_data,GetDate())<3
	 ORDER BY ha_data DESC
)
Domani proverò la SP postata da Frilla (grazie Frilla!)
ma non so se è la soluzione giusta per me visto che in pratica ho già
tutto funzionante... mi basterebbe risolvere il problema
dell'ordinamento!
Via ora vado a letto che è tardi!
Grazie a tutti quelli che mi hanno risposto (Gianluca Hotz in
particolare!)
ciao
Lorenzo



oppure adotta una politica differente , cioe ritornane non piu di un
centinaio.
tanto piu di cento non ne visualizzera mai, quindi.....
DD