[SQL] Semplificare query UNION

giovedì 09 novembre 2006 - 11.56

Hamelin [FL] Profilo | Junior Member

Saluti a tutti

Da una tabella così composta:

CREATE TABLE Tabella
( Id int primary key
DataIn date
DataOut date )

INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (1, '01/01/2006', '02/02/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (2, '01/01/2006', '02/02/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (3, '01/01/2006', '02/03/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (4, '30/01/2006', '02/03/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (5, '30/01/2006', NULL)

Dovrei fare un conteggio di quanti Id ci sono attivi ogni mese: quindi quanti Id hanno la DataIn inferiore del mese considerato, e la DataOut maggiore del mese considerato o NULL. Quindi, in questo caso il risultato:

[Mese] [Quanti]
1 3 ----> l'1 il 2 e il 3
2 3 ----> il 3 il 4 e il 5
3 1 ----> Il 5

Al momento ho risolto in modo piuttosto barbaro così:

SELECT 1 AS Mese, COUNT(Id) AS Quanti
FROM Tabella
WHERE (DataIn <= '01/01/2006') AND (DataOut IS NULL OR DataOut >= '01/02/2006')
UNION
SELECT 2 AS Mese, COUNT(Id) AS Quanti
FROM Tabella
WHERE (DataIn <= '01/02/2006') AND (DataOut IS NULL OR DataOut >= '01/03/2006')
...

Dite che c'è un modo per evitare di utilizzare 12 UNION (visto che la query peraltro è più pesante e gira anche su diversi anni)?

Grazie mille a tutti fin d'ora per l'attenzione

lbenaglia Profilo | Guru

>Dovrei fare un conteggio di quanti Id ci sono attivi ogni mese:
>quindi quanti Id hanno la DataIn inferiore del mese considerato,
>e la DataOut maggiore del mese considerato o NULL.

Ciao Hamelin [FL] ,

mi spiegheresti il significato di questo singolare algoritmo?
Cosa intendi con "id attivi"?

Io a prima vista utilizzerei un algoritmo del genere:

WHERE DataIn BETWEEN primo del mese AND ultimo del mese
OR DataOut BETWEEN primo del mese AND ultimo del mese

Iniziamo a capirci su quello che vuoi, poi proveremo a trovare una soluzione più elegante.
Magari aggiungi altre INSERT che coprano tutte le casistiche che hai, spiegando bene quali e quanti anni devi prendere in considerazione. Il periodo lo passi come parametro di input?

>Grazie mille a tutti fin d'ora per l'attenzione
Prego.

Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

Hamelin [FL] Profilo | Junior Member

Certamente:
In pratica il database raccoglie tutti gli utenti iscritti a una community. Quando un utente si iscrive, viene inserito nel database con la relativa DataIn. Quando un utente si disiscrive, non viene cancellato, ma viene riempito il campo "DataOut". A me interessa avere una tabella che mi dica, mese per mese, quali sono gli utenti attivi. Quindi scansiono il database, e vedo mese per mese quali utenti si sono iscritti prima del mese considerato (e quindi sono potenziali attivi) e si sono disiscritti dopo il mese considerato, o non si sono ancora disiscritti (valore NULL) e quindi sono effettivamente attivi.

Il problema quindi è il "mese per mese". La tabella risultato mi dovrà dire che a gennaio gli utenti attivi erano 3. Perchè sono gli utenti che si sono iscritti prima di gennaio, e si sono disiscritti dopo gennaio, oppure non si sono ancora disiscritti. A febbraio sono di nuovo 3, perchè alcuni che a gennaio erano attivi poi si sono disiscritti, ma altri si sono iscritti, e così via.

Spero di essere stato più chiaro, nel caso ditemi pure e cerco di essere più comprensibile.


Ora veniamo a qualche esempio in più. La tabella degli utenti iscritti, che mi viene fornita già completata:

INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (1, '01/01/2006', '02/02/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (2, '01/01/2006', '02/02/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (3, '01/01/2006', '02/01/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (4, '01/01/2006', '02/03/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (5, '30/01/2006', '02/03/2006')
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (6, '30/01/2006', NULL)
INSERT INTO Tabella (Id, DataIn, DataOut) VALUES (7, '20/02/2006', '30/6/2006')

L'algoritmo parte e scansiona la tabella. A gennaio 2006 quanti sono gli utenti iscritti (DataIn<=01/01/2006)? L'utente 1, l'utente 2, l'utente 3 e l'utente 4. Questi sono tutti ancora iscritti (DataOut>=01/02/2006 o NULL)? No, l'utente 3 si è disiscritto il 2/1/2006, dunque il conteggio totale degil utenti iscritti a gennaio deve fornire 3

Mese Quanti
1 3

Si passa così a febbraio. Quanti sono gli utenti iscritti (DataIn<=01/02/2006) che risultano ancora iscritti (DataOut>=01/03/2006 o NULL)? 1 2 3 4 e 5 soddisfano la prima condizione, ma 1 2 e 3 non soddisfano più la seconda: a febbraio gli utenti iscritti sono 2 (il 4 e il 5)

Mese Quanti
1 3
2 2

Marzo: con dataOut >= 01/04/2006 o NULL ci sono l'utente 6 (che non si è ancora disiscritto) e l'utente 7 (che si è disiscritto a giugno quindi a marzo è ancora ok), quindi per marzo gli utenti iscritti sono 2

Mese Quanti
1 3
2 2
2 2


Possono essere sufficienti come esempi e come casistiche INSERT?

lbenaglia Profilo | Guru

>Si passa così a febbraio. Quanti sono gli utenti iscritti (DataIn<=01/02/2006)
>che risultano ancora iscritti (DataOut>=01/03/2006 o NULL)?
>1 2 3 4 e 5 soddisfano la prima condizione, ma 1 2 e 3 non soddisfano
>più la seconda: a febbraio gli utenti iscritti sono 2 (il 4 e
>il 5)

No, anche il 6 soddisfa entrambe le condizioni.

Prova a vedere il seguente esempio:

USE tempdb; GO CREATE TABLE dbo.Tabella ( Id int NOT NULL PRIMARY KEY, DataIn datetime NOT NULL, DataOut datetime NULL ); INSERT dbo.Tabella VALUES (1, '20060101', '20060202'); INSERT dbo.Tabella VALUES (2, '20060101', '20060202'); INSERT dbo.Tabella VALUES (3, '20060101', '20060102'); INSERT dbo.Tabella VALUES (4, '20060101', '20060302'); INSERT dbo.Tabella VALUES (5, '20060130', '20060302'); INSERT dbo.Tabella VALUES (6, '20060130', NULL); INSERT dbo.Tabella VALUES (7, '20060220', '20060630'); GO CREATE FUNCTION dbo.ufn_DateInterval( @StartDate datetime, @EndDate datetime ) RETURNS @DateInterval TABLE( StartDate datetime NOT NULL, EndDate datetime NOT NULL ) BEGIN DECLARE @Date datetime DECLARE @NextMonth datetime SET @Date = @StartDate WHILE @Date < @EndDate BEGIN -- Primo del mese successivo SET @NextMonth = DATEADD(month, 1, CONVERT(char(6), @Date, 112) + '01') IF @NextMonth > @EndDate SET @NextMonth = @EndDate -- Popolo la tabella INSERT @DateInterval VALUES (@Date, @NextMonth) -- Mese successivo SET @Date = @NextMonth END RETURN END GO /* Query */ SELECT MONTH(DI.StartDate) AS Mese , YEAR(DI.StartDate) AS Anno , COUNT(*) AS UtentiAttivi FROM dbo.Tabella AS T , dbo.ufn_DateInterval('20060101', '20060630') AS DI WHERE T.DataIn <= DI.StartDate AND (T.DataOut IS NULL OR T.DataOut >= DI.EndDate) GROUP BY MONTH(DI.StartDate) , YEAR(DI.StartDate); GO /* Output: Mese Anno UtentiAttivi ----------- ----------- ------------ 1 2006 3 2 2006 3 3 2006 2 4 2006 2 5 2006 2 6 2006 2 (6 row(s) affected) */ DROP FUNCTION dbo.ufn_DateInterval; DROP TABLE dbo.Tabella;

Ciao!
--
Lorenzo Benaglia
Microsoft MVP - SQL Server
http://blogs.dotnethell.it/lorenzo/
http://italy.mvps.org

Hamelin [FL] Profilo | Junior Member

E' perfetto :D (torna all'utente con la mia query union)

Complimenti davvero, e grazie mille per la magistrale semplificazione
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-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5