Query di selezione in sql server in base al numero di giorni del mese

martedì 20 gennaio 2015 - 16.59
Tag Elenco Tags  VB.NET  |  SQL Server 2008 R2

trinity Profilo | Guru

ciao ragazzi,
chiedo un vostro consiglio.
Ho questa tabella sql

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

supponiamo di avere i seguenti record:

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

allora ecco io devo creare una query di selezione che mi prende tutti i giorni del mese di gennaio ma potrebbero essere anche altri mesi, comunque sempre un mese alla volta, e visualizzare per ogni giorno il numero di arrivi e partenze e presenza senza calcolare l'idprovenienza.
Nel caso dei dati postati il risultato sarebbe:

data - arrivi - partenze - presenze 01/01/2015 - 3 - 0 - 3 02/01/2015 - 0 - 0 - 3 03/01/2015 - 2 - 0 - 5 04/01/2015 - 3 - 0 - 8 07/01/2015 - 0 - 1 - 7

ho creato questa stored pare che in base a dei dati reali del db funziona, potete vedere se la sintassi si può sistemare o migliorare per aumentare le prestazioni ossia la velocità di elaborazione?

CREATE PROCEDURE [dbo].[sp_get_movimenti_mese] ( @idstruttura int, @fromdata date, @todata date ) AS DECLARE @codstruttura Int DECLARE @arrivo date DECLARE @partenza date DECLARE @nmovimenti int
CREATE TABLE #tblTemp1 ( id INT , data_elaborazione date, arr int, par int ) DECLARE ShiftCursorValue CURSOR FOR SELECT idstruttura, arrivo, partenza, count(idrecord) as movimenti FROM Tab_movimenti WHERE Idstruttura=1 and arrivo between '2015/01/01' and '2015/01/31' group by Idstruttura,arrivo,partenza order by arrivo OPEN ShiftCursorValue FETCH NEXT FROM ShiftCursorValue INTO @codstruttura,@arrivo,@partenza,@nmovimenti WHILE @@FETCH_STATUS = 0 BEGIN if @partenza IS NULL BEGIN insert into #tblTemp1 (id,data_elaborazione,arr,par) values (@codstruttura,@arrivo,@nmovimenti,0) END if @partenza IS NOT NULL AND @partenza<>@arrivo BEGIN Update #tblTemp1 set par=0 where data_elaborazione=@arrivo insert into #tblTemp1 (id,data_elaborazione,arr,par) values (@codstruttura,@arrivo,@nmovimenti,0) insert into #tblTemp1 (id,data_elaborazione,arr,par) values (@codstruttura,@partenza,0,@nmovimenti) END if @partenza IS NOT NULL and @partenza=@arrivo BEGIN insert into #tblTemp1 (id,data_elaborazione,arr,par) values (@codstruttura,@arrivo,@nmovimenti,@nmovimenti) END FETCH NEXT FROM ShiftCursorValue INTO @codstruttura,@arrivo,@partenza,@nmovimenti END CLOSE ShiftCursorValue DEALLOCATE ShiftCursorValue Select id,data_elaborazione,sum(arr) arrivi,sum(par) partiti from #tblTemp1 group by id,data_elaborazione Order by data_elaborazione
[/code]

ciao e grazie
Cirillo Fabio
www.trycontact.com
www.wondernet.biz
fabio@wondernet.biz
http://blogs.dotnethell.it/fabiocirillo/
http://wnetsoftware.blogspot.com

renarig Profilo | Expert

A costo di dire una scemata ( da inesperto quele sono )
....... a me non piacciono le tabelle temporanee,
valuterei di farlo con le CTE

(magari spostandola anche su lato applicazione)

quindi sulla tabella che ci hai descritto verrebbe più o meno cosi,
ho fatto tutti i passaggi ma certamente si puo ancora semplificare

USE TuoDataBase GO DECLARE @fromdata AS date DECLARE @todata AS date SET @fromdata = '2015/01/01' SET @todata = '2015/02/01' ; WITH v01 AS ( SELECT TOP (100) PERCENT arrivo AS data FROM dbo.Tab_movimenti WHERE (arrivo > @fromdata) OR (arrivo < @todata) ), v02 AS ( SELECT TOP (100) PERCENT partenza AS data FROM dbo.Tab_movimenti WHERE (partenza > @fromdata) OR (partenza < @todata) ), v04 AS ( SELECT data FROM v01 UNION ALL SELECT data FROM v02 ), v05 AS ( SELECT DISTINCT data FROM v04 ), v10 AS ( SELECT v05.data, COUNT(TMA.arrivo) AS arrivi, COUNT(TMP.partenza) AS partenze FROM dbo.Tab_movimenti AS TMP RIGHT OUTER JOIN v05 ON TMP.partenza = v05.data LEFT OUTER JOIN dbo.Tab_movimenti AS TMA ON v05.data = TMA.arrivo GROUP BY v05.data ), v15 AS ( SELECT data, arrivi, partenze, arrivi - partenze AS variazione FROM v10 ), v20 AS ( SELECT TOP (100) PERCENT data, arrivi, partenze, variazione, (SELECT SUM(variazione) AS pres FROM v15 AS yy WHERE (data <= v15.data)) AS presenze FROM v15 ), v22 AS ( SELECT TOP (100) PERCENT data, arrivi, partenze, presenze FROM v20 ORDER BY data ) SELECT * FROM v22

data arrivi partenze presenze 2015-01-01 3 0 3 2015-01-03 2 0 5 2015-01-04 3 0 8 2015-01-07 0 1 7




Facci sapere

alx_81 Profilo | Guru

Ciao a tutti,

>A costo di dire una scemata ( da inesperto quele sono )
>....... a me non piacciono le tabelle temporanee, valuterei di farlo con le CTE
le tabelle temporanee, usate correttamente, sono uno degli strumenti più potenti che hai con SQL Server per ragionare a set.
Le CTE sono veramente valide quando si parla di ricorsione, mentre, usate come resultset temporanei o subquery, a volte si comportano un po' stranamente (va sempre controllato il piano non è che non vadano bene eh).
Per tabelle temporanee io intendo ANCHE (ma non solo) le variabili tabella, veramente molto importanti con resultset piccoli come questi. Considera che una temp table in variabile (es: DECLARE @ttt tABLE) lavora SOLO in memoria, e, solamente nel caso di memory pressure, viene trasformata in una temporary reale (#ttt). Quindi le valuterei quando ho a che fare con spezzettamenti di set necessari.

>(magari spostandola anche su lato applicazione)
bravo, torno a ripetermi sempre, queste logiche non sono da database a mio avviso. La logica di business dovrebbe ricevere il set di dominio su cui lavorare e poi applicare le logiche NON SET invece che forzare il database a farne troppe. Un relazionale è fatto per "girare" su SET, JOIN, WHERE, GROUP BY, SORT, ecc.. non su cicli o procedure troppo impegnative.

Questa è la mia proposta, logicamente simile alla tua @renarig (no, non sei una variabile ), ma con l'utilizzo delle temp var. La struttura è:
- creazione di una tabella delle giornate del mese disponibili (prettamente una tabella di lavoro)
- creazione del set di dominio per le variazioni
- aggregazione e risultati finali
Eccola:

DECLARE @DataRiferimentoFrom date = '20150101'; DECLARE @DataRiferimentoTo date = DATEADD(MONTH, 1, @DataRiferimentoFrom); DECLARE @DataRiferimentoProgressiva date = @DataRiferimentoFrom; DECLARE @DateDisponibili table ( DataRiferimento date ); DECLARE @Variazioni table ( DataRiferimento date PRIMARY KEY , Partenze smallint , Arrivi smallint , Variazione smallint ); WHILE @DataRiferimentoProgressiva <= @DataRiferimentoTo BEGIN INSERT INTO @DateDisponibili (DataRiferimento) VALUES (@DataRiferimentoProgressiva); SET @DataRiferimentoProgressiva = DATEADD(DAY, 1, @DataRiferimentoProgressiva); END; INSERT INTO @Variazioni (DataRiferimento, Partenze, Arrivi, Variazione) SELECT DD.DataRiferimento , ArriviGiornalieri = COALESCE(PA.Arrivi, 0) , PartenzeGiornaliere = COALESCE(PA.Partenze, 0) , Variazione = COALESCE(PA.Arrivi, 0) - COALESCE(PA.Partenze, 0) FROM @DateDisponibili DD LEFT JOIN ( SELECT DataRiferimento = arrivo , Arrivi = COUNT(arrivo) , Partenze = 0 FROM dbo.Tab_movimenti WHERE arrivo >= @DataRiferimentoFrom AND arrivo < @DataRiferimentoTo GROUP BY arrivo UNION SELECT DataRiferimento = partenza , Arrivi = 0 , Partenze = COUNT(partenza) FROM dbo.Tab_movimenti WHERE partenza >= @DataRiferimentoFrom AND partenza < @DataRiferimentoTo GROUP BY partenza ) PA ON PA.DataRiferimento = DD.DataRiferimento; SELECT V.DataRiferimento , V.Partenze , V.Arrivi , V.Variazione , Presenze = (SELECT SUM(Variazione) FROM @Variazioni WHERE DataRiferimento <= V.DataRiferimento) FROM @Variazioni V;

Noterai che i risultati sono molti di più, questo perchè ho notato che nel resultset inizialmente voluto c'è anche il 2 di gennaio, data che non ha nessuna partenza o arrivo. Quindi ho optato per sviluppare tutto il mese. Basta togliere la left join o cambiare la where per fare toggle della funzionalità.
Spero possa andare bene come quella di @renarig.
ciao
Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/en-us/mvp/Alessandro%20Alpi-4014222

trinity Profilo | Guru

Prima di tutto grazie ad entrambi per il tempo dedicato e gli esempi postati.
Prima di tutto volevo dire a questo punto il codice da me fatto è errato?

Secondo analizzerò ed eseguire entrambi i vostri codici per capire come avete fatto e così potrò metterlo in pratica

Alx_81, nel tuo codice quando lo eseguo mi da questo errore:

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


rispondendo a renarig

in effetti ho pensato di far fare qualche operazione lato applicazione pertanto la mia stored esegue solamente il controllo per tutto il periodo indicato degli arrivi e partenze, ovviamente mettendo sullo stesso giorno arrivi e partenze qualora ci fossero ed infine il calcolo delle presenze lo eseguo sulla applicazione


grazie ;)
Cirillo Fabio
www.trycontact.com
www.wondernet.biz
fabio@wondernet.biz
http://blogs.dotnethell.it/fabiocirillo/
http://wnetsoftware.blogspot.com

alx_81 Profilo | Guru

>Prima di tutto volevo dire a questo punto il codice da me fatto è errato?
Se quanto ottieni è quello che ti serve direi di no, ma eviterei i cursori per una cosa così.

>Alx_81, nel tuo codice quando lo eseguo mi da questo errore:
usando i tuoi dati di esempio e lo script non cambiato non mi sembra avere errori.
Come l'hai cambiato?

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

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/en-us/mvp/Alessandro%20Alpi-4014222

trinity Profilo | Guru

Il tuo script non l'ho cambiato copiato su sql ed eseguito la query, aspe ti posto i dati reali che ci sono nel db:

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

ecco la tabella

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


Cirillo Fabio
www.trycontact.com
www.wondernet.biz
fabio@wondernet.biz
http://blogs.dotnethell.it/fabiocirillo/
http://wnetsoftware.blogspot.com

alx_81 Profilo | Guru

>Il tuo script non l'ho cambiato copiato su sql ed eseguito la
>query, aspe ti posto i dati reali che ci sono nel db:
ok, c'era un caso non definito nei primi dati di esempio che non avevo considerato
Eccolo, fammi sapere:

DECLARE @DataRiferimentoFrom date = '20150101'; DECLARE @DataRiferimentoTo date = DATEADD(MONTH, 1, @DataRiferimentoFrom); DECLARE @DataRiferimentoProgressiva date = @DataRiferimentoFrom; DECLARE @DateDisponibili table ( DataRiferimento date ); DECLARE @Variazioni table ( DataRiferimento date PRIMARY KEY , Partenze smallint , Arrivi smallint , Variazione smallint ); WHILE @DataRiferimentoProgressiva <= @DataRiferimentoTo BEGIN INSERT INTO @DateDisponibili (DataRiferimento) VALUES (@DataRiferimentoProgressiva); SET @DataRiferimentoProgressiva = DATEADD(DAY, 1, @DataRiferimentoProgressiva); END; INSERT INTO @Variazioni (DataRiferimento, Arrivi, Partenze, Variazione) SELECT DD.DataRiferimento , ArriviGiornalieri = SUM(COALESCE(PA.Arrivi, 0)) , PartenzeGiornaliere = SUM(COALESCE(PA.Partenze, 0)) , Variazione = SUM(COALESCE(PA.Arrivi, 0) - COALESCE(PA.Partenze, 0)) FROM @DateDisponibili DD LEFT JOIN ( SELECT DataRiferimento = arrivo , Arrivi = COUNT(arrivo) , Partenze = 0 FROM dbo.Tab_movimenti WHERE arrivo >= @DataRiferimentoFrom AND arrivo < @DataRiferimentoTo GROUP BY arrivo UNION ALL SELECT DataRiferimento = partenza , Arrivi = 0 , Partenze = COUNT(partenza) FROM dbo.Tab_movimenti WHERE partenza >= @DataRiferimentoFrom AND partenza < @DataRiferimentoTo GROUP BY partenza ) PA ON PA.DataRiferimento = DD.DataRiferimento GROUP BY DD.DataRiferimento; SELECT V.DataRiferimento , V.Partenze , V.Arrivi , V.Variazione , Presenze = (SELECT SUM(Variazione) FROM @Variazioni WHERE DataRiferimento <= V.DataRiferimento) FROM @Variazioni V;
Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/en-us/mvp/Alessandro%20Alpi-4014222

trinity Profilo | Guru

Funziona eccetto una piccola modifica, con il tuo codice viene caricato anche il giorno 01/02/2015 invece deve finire al 31/01/2015, ho modificato io questo facendo:

DECLARE @DataRiferimentoTo date = DATEADD(day, -1, DATEADD(month, 1, CONVERT(char( 6),@DataRiferimentoFrom, 112) + '01'))

funziona come visualizzazione dei giorni ma al 3101/2015 non mi calcola un arrivo e quindi le presenze che dovrebbero essere in totale 34 sono 33

inoltre, perdonami, mi sono dimenticato di dire che può accadere che in un mese ci siano più partenze che arrivi, la tua procedura prendere bene in questo caso i dati ma le presenze sono negative, pertanto si può fare qualcosa per portarsi il numero totale delle presenze del mese precedente e quindi poi lavorare con questo dato aggiungendolo alle presenze che si generano nel mese di lavorazione.

Ti spiego

ti faccio vedere un result con il tuo codice del mese di febbraio ovviamente simulato

2015-02-01 2 4 -2 -2
2015-02-02 1 3 -2 -4

come puoi vedere ci sono a febbraio -2 presenze, ecco servirebbe che se gennaio ha un totale di 30 presenze, tale valore sarebbe da riportare nel mese di febbraio al primo giorno così poi tutto il resto si allinea correttamente come in questo esempio:

2015-02-01 2 4 -2 28
2015-02-02 1 3 -2 26

stavo facendo anche un ampliamento al tuo codice nel senso di inserire accanto alla colonna arrivi e partenze che tipologia sono ossia se sono italiani o stranieri e ho scritto così:


DECLARE @DataRiferimentoFrom date = '20150101'; DECLARE @DataRiferimentoTo date = DATEADD(MONTH, 1, @DataRiferimentoFrom); DECLARE @DataRiferimentoProgressiva date = @DataRiferimentoFrom; DECLARE @DateDisponibili table ( DataRiferimento date ); DECLARE @Variazioni table ( DataRiferimento date PRIMARY KEY , Partenze smallint , Arrivi smallint , Variazione smallint , TipoArrivi Varchar(20) , TipoPartenze Varchar(20) ); WHILE @DataRiferimentoProgressiva <= @DataRiferimentoTo BEGIN INSERT INTO @DateDisponibili (DataRiferimento) VALUES (@DataRiferimentoProgressiva); SET @DataRiferimentoProgressiva = DATEADD(DAY, 1, @DataRiferimentoProgressiva); END; INSERT INTO @Variazioni (DataRiferimento, Arrivi, TipoArrivi, Partenze, TipoPartenze, Variazione) SELECT DD.DataRiferimento , ArriviGiornalieri = SUM(COALESCE(PA.Arrivi, 0)) , TipoArrivi , PartenzeGiornaliere = SUM(COALESCE(PA.Partenze, 0)) , TipoPartenze , Variazione = SUM(COALESCE(PA.Arrivi, 0) - COALESCE(PA.Partenze, 0)) FROM @DateDisponibili DD LEFT JOIN ( SELECT DataRiferimento = arrivo , Arrivi = COUNT(arrivo) , Partenze = 0 , TipoArrivi = case when p.codistat=0 then 'STRANIERI' else 'ITALIANI' end FROM dbo.Tab_movimenti JOIN dbo.Tab_comuni_nazioni p ON idprovenienza=p.idrecord WHERE arrivo >= @DataRiferimentoFrom AND arrivo < @DataRiferimentoTo GROUP BY arrivo,p.codistat UNION ALL SELECT DataRiferimento = partenza , Arrivi = 0 , Partenze = COUNT(partenza) , TipoPartenze =case when p.codistat=0 then 'STRANIERI' else 'ITALIANI' end FROM dbo.Tab_movimenti JOIN dbo.Tab_comuni_nazioni p ON idprovenienza=p.idrecord WHERE partenza >= @DataRiferimentoFrom AND partenza < @DataRiferimentoTo GROUP BY partenza,p.codistat ) PA ON PA.DataRiferimento = DD.DataRiferimento GROUP BY DD.DataRiferimento, PA.TipoArrivi; SELECT V.DataRiferimento , V.Arrivi , V.TipoArrivi , V.Partenze , V.TipoPartenze , V.Variazione , Presenze = (SELECT SUM(Variazione) FROM @Variazioni WHERE DataRiferimento <= V.DataRiferimento) FROM @Variazioni V;

solo che mi da questo messaggio:

Messaggio 207, livello 16, stato 1, riga 33
Il nome di colonna 'TipoPartenze' non è valido.

quindi non potendo eseguire la query non so neanche se il codice da me aggiunto sia corretto e funzionante

Dove sbaglio?

Ciao e grazie mille
Cirillo Fabio
www.trycontact.com
www.wondernet.biz
fabio@wondernet.biz
http://blogs.dotnethell.it/fabiocirillo/
http://wnetsoftware.blogspot.com

renarig Profilo | Expert

>...... Considera che una temp table in variabile (es: DECLARE @ttt tABLE) lavora SOLO in memoria, .......
Mi ricredo sulle temporanee, non conoscevo ancora questo tipo
e mi piace molto perché lavora solo in memoria

.

alx_81 Profilo | Guru

Ciao
>solo che mi da questo messaggio:
>Messaggio 207, livello 16, stato 1, riga 33
>Il nome di colonna 'TipoPartenze' non è valido.
certo, nelle union all il primo resultset (la prima query) ha la colonna TipoArrivi, il secondo "sovrascrive" il nome della colonna che userà il compilatore.
Se vuoi averle entrambe, dovrai creare un campo fittizio a null nella prima parte (prima di union all) chiamato TipiPartenze ed uno chiamato TipiArrivo a null nel resultset successivo alla union all.
Poi dovrai decidere come aggregare il campo nella select esterna alla subquery (quindi dovrai scegliere tra funzioni come MAX, SUM oppure fare GROUP BY).


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

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/en-us/mvp/Alessandro%20Alpi-4014222
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