Declare cursor for

martedì 10 settembre 2013 - 20.06
Tag Elenco Tags  VB.NET  |  .NET 4.0  |  SQL Server 2008 R2  |  SQL Server 2008  |  SQL Server 2005  |  SQL Server 2000  |  SQL Server Express

gsistemi Profilo | Junior Member

Devo ottimizzare una routine al momento implementata su vb.net che impiega 1-2 ore per essere elaborata, quindi volevo provare a mettere tutto in una stored procedure per vedere se riesco a ridurre i tempi di elaborazione. Il problema è un po' complesso provo a spiegarlo:

select tabella1.id from tabella1 /* sono circa 60-70 record */

per ognuno di questi record faccio un'altra select:

select dettaglio.id, sum(dettaglio.valore), count(dettaglio.id) from dettaglio where dettaglio.idtabella = @id group by dettaglio /* sono circa 1000 record per ogni id di tabella1 */

eseguo dei calcoli utilizzando i valori della query /* sono 30-40 righe di codice ma semplici moltiplicazioni e divisioni */
fatti i calcoli aggiorno una terza tabella:

update progressivi set tot = @tot, num = @num where progressivi.iddettaglio = @id if @@rowcount = 0 insert into progressivi ( iddettaglio, tot, num ) values ( @iddettaglio, @tot, @num )

ciclo fino alla fine dei record della tabella dettaglio (ogni giro impiega circa 4 minuti)

ciclo fino alla fine dei record della tabella tabella1 (4 minuti moltiplicato 60-70 fate voi il conto!)

in linea di massima il codice è questo:

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

Mi sono avventurato nel tentativo di tradurre questo codice in sql, al di là del fatto se sia il caso o no, ho incontrato questa difficoltà utilizzando i cursori.

Nella definizione di un cursore dopo "FOR" mi richiede una SELECT, io però ho la necessità di metterci una variabile di testo con dentro il codice da eseguire, del tipo:

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

E' possibile?

Grazie!

alx_81 Profilo | Guru

>Devo ottimizzare una routine al momento implementata su vb.net
>che impiega 1-2 ore per essere elaborata, quindi volevo provare a
>mettere tutto in una stored procedure per vedere se riesco
>a ridurre i tempi di elaborazione.
sono veramente tante.. ma non credo sia il problema di stored procedure o vb.net.

>select tabella1.id from tabella1 /* sono circa 60-70 record */
quindi molto pochi

>per ognuno di questi record faccio un'altra select:
>select dettaglio.id, sum(dettaglio.valore), count(dettaglio.id)
>from dettaglio where dettaglio.idtabella = @id group by dettaglio
>/* sono circa 1000 record per ogni id di tabella1 */
ok, perchè non una join invece che un ciclo?

>eseguo dei calcoli utilizzando i valori della query /* sono 30-40
>righe di codice ma semplici moltiplicazioni e divisioni */
e questo non puoi farlo direttamente in una sola query? se sono operazioni semplici all'interno della stessa riga, con la join di cui sopra ti conviene molto.

>fatti i calcoli aggiorno una terza tabella:
>update progressivi set tot = @tot, num = @num where progressivi.iddettaglio
>= @id if @@rowcount = 0 insert into progressivi ( iddettaglio,
>tot, num ) values ( @iddettaglio, @tot, @num )
E per questo, con la clausola output secondo me puoi passare dalla query precedente gli id e i dati che ti servono per fare l'update/insert (puoi pensare anche ad una merge)

>E' possibile?
io cambierei completamente il punto di vista come ti dicevo sopra.
Non sarebbe male avere le due tabelle con dati finti e le tabelle da aggiornare.. così possiamo provare la soluzione se non è chiara.

>Grazie!
di nulla!
Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/profiles/Alessandro.Alpi

gsistemi Profilo | Junior Member

>>Devo ottimizzare una routine al momento implementata su vb.net
>>che impiega 1-2 ore per essere elaborata, quindi volevo provare a
>>mettere tutto in una stored procedure per vedere se riesco
>>a ridurre i tempi di elaborazione.
>sono veramente tante.. ma non credo sia il problema di stored
>procedure o vb.net.
>
>>select tabella1.id from tabella1 /* sono circa 60-70 record */
>quindi molto pochi
>
>>per ognuno di questi record faccio un'altra select:
>>select dettaglio.id, sum(dettaglio.valore), count(dettaglio.id)
>>from dettaglio where dettaglio.idtabella = @id group by dettaglio
>>/* sono circa 1000 record per ogni id di tabella1 */
>ok, perchè non una join invece che un ciclo?
ok potrei unire i due cicli in uno solo da 60.000/70.000 record. Ci guadagno in termini di tempo?
>
>>eseguo dei calcoli utilizzando i valori della query /* sono 30-40
>>righe di codice ma semplici moltiplicazioni e divisioni */
>e questo non puoi farlo direttamente in una sola query? se sono
>operazioni semplici all'interno della stessa riga, con la join
>di cui sopra ti conviene molto.
le operazioni sono moltiplicazioni e divisioni ma contengono molte if/else e replicare il calcolo su una sola riga sarebbe quantomeno confuso, comunque se l'idea è quella di fare una cosa del tipo:
update progressivi set tot = ( select valore_calcolato from tabella inner join dettaglio on tabella.id = dettaglio.idtabella where dettaglio.id = progressivi.iddettaglio )
potrei provare.
>
>>fatti i calcoli aggiorno una terza tabella:
>>update progressivi set tot = @tot, num = @num where progressivi.iddettaglio
>>= @id if @@rowcount = 0 insert into progressivi ( iddettaglio,
>>tot, num ) values ( @iddettaglio, @tot, @num )
>E per questo, con la clausola output secondo me puoi passare
>dalla query precedente gli id e i dati che ti servono per fare
>l'update/insert (puoi pensare anche ad una merge)
>
>>E' possibile?
>io cambierei completamente il punto di vista come ti dicevo sopra.
>Non sarebbe male avere le due tabelle con dati finti e le tabelle
>da aggiornare.. così possiamo provare la soluzione se non è chiara.
>
ti mando tutto il codice anche se l'esempio è molto ridotto all'osso e non so che cosa ci si può capire!

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

>>Grazie!
>di nulla!
>Alessandro Alpi | SQL Server MVP
>MCP|MCITP|MCTS|MCT
>
>http://blogs.dotnethell.it/suxstellino
>http://suxstellino.wordpress.com
>http://mvp.microsoft.com/profiles/Alessandro.Alpi

alx_81 Profilo | Guru

>ok potrei unire i due cicli in uno solo da 60.000/70.000 record. Ci guadagno in termini di tempo?
beh di certo una join evitando un ciclo su sql (che non andrebbe praticamente mai fatto, visto che le operazioni veloci su un relazionale sono quelle basate su insiemi di dati, SET BASED) è molto meglio.

>le operazioni sono moltiplicazioni e divisioni ma contengono
>molte if/else e replicare il calcolo su una sola riga sarebbe
>quantomeno confuso, comunque se l'idea è quella di fare una cosa
>del tipo:
>update progressivi set tot = ( select valore_calcolato from tabella
>inner join dettaglio on tabella.id = dettaglio.idtabella where
>dettaglio.id = progressivi.iddettaglio ) potrei provare.
qui dipende dal calcolo, per quello mi serve il codice..

>ti mando tutto il codice anche se l'esempio è molto ridotto all'osso
>e non so che cosa ci si può capire!
ok appena ho un secondo vedo cosa possiamo fare
Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/profiles/Alessandro.Alpi

alx_81 Profilo | Guru

Con il codice che mi hai mandato posso solo dirti che è un po' troppo arzigogolato per essere facilmente compreso
In linea di principio però, io farei una stored procedure in cui eviterei i primi due cicli, joinando tra di loro le tabelle e determinando le casistiche sulla select (almeno in un primo inizio, poi puoi provare funzioni sql).

Personalmente farei:
- creazione di una tabella temporanea che conterrà l'elenco dei campi (senza calcoli) che ti servono poi
- join tra le entità ed inserimento del risultato all'interno della suddetta tabella
- creazione di eventuali indici sulla tabella temporanea (per rendere più veloci i calcoli, in base alle where o alle eventuali aggregazioni)
- applicazione dei calcoli su di una select direttamente dalla tabella temporanea (che ha già i dati che ti servono)
- applicazione della clausola output su di una ulteriore tabella per "salvare" gli id che vuoi aggiornare successivamente
- aggiornamento a partire da quest'ultima tabella come filtro

questo in prima analisi.. Purtroppo così ci sono pochi dettagli, dovrei perderci veramente tanto tempo per darti una mano come si deve.. Intanto prova a cambiare approccio come ti suggerisco, almeno qualche punto. Poi vedi le performance.


Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/profiles/Alessandro.Alpi

gsistemi Profilo | Junior Member

>Con il codice che mi hai mandato posso solo dirti che è un po'
>troppo arzigogolato per essere facilmente compreso

ok immaginavo che ci volesse troppo tempo!!!
>In linea di principio però, io farei una stored procedure in
>cui eviterei i primi due cicli, joinando tra di loro le tabelle
>e determinando le casistiche sulla select (almeno in un primo
>inizio, poi puoi provare funzioni sql).
>
>Personalmente farei:
>- creazione di una tabella temporanea che conterrà l'elenco dei
>campi (senza calcoli) che ti servono poi
>- join tra le entità ed inserimento del risultato all'interno
>della suddetta tabella
>- creazione di eventuali indici sulla tabella temporanea (per
>rendere più veloci i calcoli, in base alle where o alle eventuali
>aggregazioni)
>- applicazione dei calcoli su di una select direttamente dalla
>tabella temporanea (che ha già i dati che ti servono)
>- applicazione della clausola output su di una ulteriore tabella
>per "salvare" gli id che vuoi aggiornare successivamente
>- aggiornamento a partire da quest'ultima tabella come filtro
>
ok proverò a seguire il tuo consiglio e mettere tutto in una stored procedure ed unire alcune select.

>questo in prima analisi.. Purtroppo così ci sono pochi dettagli,
>dovrei perderci veramente tanto tempo per darti una mano come
>si deve.. Intanto prova a cambiare approccio come ti suggerisco,
>almeno qualche punto. Poi vedi le performance.
>
ovviamente ti ringrazio tanto!!!
>
>Alessandro Alpi | SQL Server MVP
>MCP|MCITP|MCTS|MCT
>
>http://blogs.dotnethell.it/suxstellino
>http://suxstellino.wordpress.com
>http://mvp.microsoft.com/profiles/Alessandro.Alpi

gsistemi Profilo | Junior Member

Quello in cui ho difficoltà è appunto unire i primi due cicli, perchè in un campo del primo ciclo ho il codice sql per filtrare il secondo.
Provo a fare un esempio semplice ma più simile al problema reale.

Tabella Eventi
==============
id | descr | filtro
1 | Vincitrice Serie A | and squadre.campionato = 'Serie A'
2 | Vincitrice Premier | and squadre.campionato = 'Premier'
3 | Vincitrice Liga | and squadre.campionato = 'Liga'
4 | Campione d'Inverno Serie A | and squadre.campionato = 'Serie A' and partite.data < '01/01/2015'

Tabella Squadre
=================
id | descri | campionato
1 | Milan | Serie A
2 | Roma | Serie A
3 | Napoli | Serie A
4 | Real Madrid | Liga
5 | Barcellona | Liga
6 | Manchester Utd | Premier
7 | Arsenal | Premier
8 | Chelsea | Premier

Tabella Partite
=====================
id | squadra1 | squadra2 | gol1 | gol2 | data
...

quello che mi suggerisci e fare una tabella del tipo
idevento | descr_evento | id_squadra | descr_squadra | pos

come posso fare la join tra eventi e squadre + partite (che serve per calcolare la classifica) avendo un campo della tabella eventi che serve per fare il filtro?
Forse è pensata male la struttura ma non so trovare una soluzione diversa!

alx_81 Profilo | Guru

ti sto finendo un esempio.. che però ti porterà almeno a pensare di cambiare un po' le cose, o, in alternativa più probabile, ad abbandonare la mia proposta.
L'esempio è il seguente:

CREAZIONE OGGETTI:
USE tempdb; GO CREATE TABLE dbo.Testata ( idTestata int IDENTITY(1, 1) , nome varchar(20) , PRIMARY KEY ( idTestata ) ); GO CREATE TABLE dbo.Dettagli ( idTestata int , idDettaglio int IDENTITY(1, 1) , dataDettaglio datetime , valoreInt int , valoreDec decimal , PRIMARY KEY ( idDettaglio ) ); GO CREATE TABLE dbo.Risultati ( idTestata int , valore decimal , PRIMARY KEY ( idTestata ) ); GO SET NOCOUNT ON; GO DECLARE @i int = 1; DECLARE @id int; WHILE @i <= 100000 BEGIN INSERT INTO dbo.Testata (nome) VALUES ('Test ' + CAST(@i AS varchar(10))); SET @id = SCOPE_IDENTITY(); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i, @i/0.3); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*2, @i/0.3*2); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*3, @i/0.3*3); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*4, @i/0.3*4); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*5, @i/0.3*5); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*6, @i/0.3*6); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*7, @i/0.3*7); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*8, @i/0.3*8); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*9, @i/0.3*9); INSERT INTO dbo.Dettagli (idTestata, dataDettaglio, valoreInt, valoreDec) VALUES (@id, GETDATE(), @i*10, @i/0.3*10); SET @i = @i + 1; END; SELECT COUNT(*) FROM dbo.Testata; SELECT COUNT(*) FROM dbo.Dettagli; --DROP TABLE dbo.Dettagli; --GO --DROP TABLE dbo.Testata; --GO --DROP TABLE dbo.Risultati; --GO


1) fatto con cursore:
Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra


2) fatto set based
Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra


>come posso fare la join tra eventi e squadre + partite (che serve
>per calcolare la classifica) avendo un campo della tabella eventi
>che serve per fare il filtro?
Purtroppo la tua è una questione di bad design. Mettere una logica di filtro all'interno della tabella ti obbliga a fare delle considerazioni che purtroppo non sono molto database oriented.
Quindi mi spiace, ma o cambi un po' il design oppure credo che il ciclo te lo dovrai tenere, e, di conseguenza, anche la durata. Credo che devi proprio intervenire sul modello, non vedo altri rimedi.
Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/profiles/Alessandro.Alpi
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2025
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5