[SQL] Comparire in una tabella una volta al mese

mercoledì 19 aprile 2006 - 14.25

Hamelin [FL] Profilo | Junior Member

Un saluto a tutti

Ho una piccola richiesta su una possibile soluzione di un'interrogazione SQL

In pratica attualmente ho una SELECT CASE che definisce un utente in base al numero di volte che compare nella colonna "Data Login", tipo

SELECT
CASE
WHEN count([Data Login]) > 10 THEN 'Super'
WHEN count([Data Login]) > 1 AND count([Data Login]) < 10 THEN 'Normal'
ELSE 'Sleeper'
END
AS TipoUtente

Dovrei modificare la seconda condizione in modo che utente sia sì "Normal" se ha fatto da 1 a 10 login, ma anche che abbia anche fatto almeno 1 login al mese negli ultimi 3 mesi. Cioè un utente che ha fatto 5 login 3 mesi fa sia "Sleeper", mentre un utente che ha fatto 1 login nell'ultimo mese, 1 nel mese prima, e 1 nel mese ancora precedente sia "Normal"

Purtroppo non sono ancora riuscito a trovare una soluzione per questo piccolo rompicapo... qualcuno mi sa aiutare?

Spero di essere stato chiaro, in caso contrario fatemi un fischio e vedo di spiegarmi meglio. Grazie mille

lbenaglia Profilo | Guru

>Purtroppo non sono ancora riuscito a trovare una soluzione per
>questo piccolo rompicapo... qualcuno mi sa aiutare?

Ciao Hamelin [FL],

aiutaci tu postando la struttura della tabella (CREATE TABLE), alcune righe di prova (INSERT INTO) ed il result set che vorresti ottenere.

Ciao!

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

Hamelin [FL] Profilo | Junior Member

La tabella ha le colonne "UserId" (intero) e "Data Login" (data), ad esempio

[UserId] [Data Login]
1 01/01/2006
2 01/01/2006
2 02/01/2006
1 01/02/2006
2 01/02/2006
1 01/03/2006

La query quindi dovrebbe restituire:

[UserId] [TipoUtente]
1 Super
2 Normal

Però mi servirebbe che questa cosa sia inserita nella SELECT CASE postata sopra, perchè in realtà questa è una semplificazione di una struttura più complessa che devo implementare in quel modo.
Insomma, io riesco già a fare la tabella "UserId - TipoUtente", ma al momento mi creerebbe 2 utenti super perchè entrambi hanno fatto 3 login. Invece mi servirebbe che il 2 venga definito Normal perchè non ha fatto una login al mese, ma 2 in un mese e 1 in un altro

Spero di avervi fornito gli strumenti necessari

lbenaglia Profilo | Guru

>Spero di avervi fornito gli strumenti necessari

Direi che non ci siamo. Io ho chiesto la struttura della tabella (quindi il comando CREATE TABLE) e alcune righe di prova (comando INSERT INTO). Inoltre se applichi la tua SELECT ai dati che hai postato non otterrai il risultato voluto.

E' così difficile fare un esempio completo e funzionante?

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

Hamelin [FL] Profilo | Junior Member

Il fatto è che non uso il create table e l'insert into perchè uso SQL Server reporting services che si comporta tipo access, comunque provo a farli io a mano sperando che siano giusti :)

CREATE TABLE TableType
( UserId int
DataLogin date )

INSERT INTO TableType (UserId, DataLogin) VALUES (1, '01/01/2006') ... (ecc come quelli postati sopra)

E quindi la query dovrebbe essere qualcosa tipo:

SELECT UserId,
CASE
WHEN count([DataLogin]) > 2 THEN 'Super'
WHEN count([DataLogin]) > 1 AND count([DataLogin]) <= 2 AND *** THEN 'Normal'
ELSE 'Sleeper'
END
AS TipoUtente
FROM TableType

E dovrei fare la modifica nella parte "AND ***" in modo che non sia sufficiente il numero di volte in cui un utente compare nella tabella a definirlo, ma serva anche che abbia fatto almeno 1 accesso al mese (e non da 1 a 2 accessi indifferentemente)

Se non mi sbaglio ora come ora e senza la parte "AND ***" la query dovrebbe fornire

1 Super
2 Super

Invece mi servirebbe che dia

1 Super
2 Normal

Spero questa volta di aver dato le informazioni corrette

Hamelin [FL] Profilo | Junior Member

Scusate se riuppo, sarà l'ultima volta poi mi rassegnerò :)

Ho dato di nuovo informazioni poco chiare/sono stato scortese, oppure il problema sembra non avere soluzione?

Grazie comunque per l'attenzione

escaflowne Profilo | Junior Member

>Scusate se riuppo, sarà l'ultima volta poi mi rassegnerò :)
>
>Ho dato di nuovo informazioni poco chiare/sono stato scortese,
>oppure il problema sembra non avere soluzione?
>
>Grazie comunque per l'attenzione

ciao,
io credo che lbenaglia voglia proprio gli script (di creazione della tabella, di insert dei dati ecc). Così può replicare la situazione sul suo db e darti una soluzione puntuale.

Comunque, mi par di capire, che sia solo un problema di logica della clausola where.
cioè qualcosa tipo

Normal se 0<login<11
and (login > 1 and dataLogin = meseCorrente - 1)
and (login > 1 and dataLogin = meseCorrente - 2)
and (login > 1 and dataLogin = meseCorrente - 3)

da tradurre in sql (scusami ma ora non ho tempo di provare)

Hamelin [FL] Profilo | Junior Member

In tal caso credo sia colpa della mia ignoranza, non saprei cos'altro fornire oltre a quello che ho dato ^^" (ma se posso, ovviamente, scrivo volentieri)

Comunque sì, forse si può risolvere anche solo con un po' di logica nel WHERE (o meglio, nelle clausole WHEN del CASE perchè il WHERE non lo posso toccare), e in effetti una soluzione come quella che proponi, se sviluppata, potrebbe portare a quanto richiesto... beh, provo a smanettarci un po' :)

***

Eccoci dopo la prova di smanettamento: dicevi qualcosa del genere?

WHEN count(distinct [Data Login]) > 1 AND count(distinct [Data Login]) <= 10
AND (([Data Login] > DATEADD(day, -30, @DataInizio)) AND (count(distinct [Data Login]) > 1))
AND (([Data Login] BETWEEN DATEADD(day, -60, @DataInizio) AND DATEADD(day, -30, @DataInizio)) AND (count(distinct [Data Login]) > 1))
THEN 'Normal'

O è completamente errato dal punto di vista logico? ^^"

escaflowne Profilo | Junior Member

>In tal caso credo sia colpa della mia ignoranza, non saprei cos'altro
>fornire oltre a quello che ho dato ^^" (ma se posso, ovviamente,
>scrivo volentieri)
>
>Comunque sì, forse si può risolvere anche solo con un po' di
>logica nel WHERE (o meglio, nelle clausole WHEN del CASE perchè
>il WHERE non lo posso toccare), e in effetti una soluzione come
>quella che proponi, se sviluppata, potrebbe portare a quanto
>richiesto... beh, provo a smanettarci un po' :)
>
>***
>
>Eccoci dopo la prova di smanettamento: dicevi qualcosa del genere?
>
>WHEN count(distinct [Data Login]) > 1 AND count(distinct [Data
>Login]) <= 10
>AND (([Data Login] > DATEADD(day, -30, @DataInizio)) AND (count(distinct
>[Data Login]) > 1))
>AND (([Data Login] BETWEEN DATEADD(day, -60, @DataInizio) AND
>DATEADD(day, -30, @DataInizio)) AND (count(distinct [Data Login])
>> 1))
>THEN 'Normal'
>
>O è completamente errato dal punto di vista logico? ^^"

Si, credo che si possa fare questo tentativo. Pensavo che, in realtà, per essere normal è necessario che l'utente abbia fatto ALMENO 3 accessi (uno per ogni mese negli ultimi tre) quindi
count(distinct [Data Login]) > 2 AND count(distinct[DataLogin]) <= 10

Hai provato se funziona?

Hamelin [FL] Profilo | Junior Member

Mi dà errore di sintassi perchè dice che la colonna [Data Login] dovrebbe comparire anche nella GROUP BY :(

La qual cosa mi sembra piuttosto improbabile: finchè la query è una

SELECT
CASE
WHEN count(distinct [Data Login]) > 5 THEN 'Normal'

Va tutto bene e non la vuole nella group by. Quando invece la trasformo in quanto scritto sopra, mi dice che la colonna [Data Login] è invalida perchè non compare nella Group By O_o

Ma se metto anche la data nella group by mi sballa tutto... e poi non dovrebbe servirmi metterlo... possibile che inserendo il

SELECT
CASE WHEN count(distinct DataLogin) > 5 AND DataLogin BETWEEN @DataInizio-30 AND @DataInizio AND count(distinct DataLogin) >2 THEN 'Normal'

Voglia la data login anche nella group by? E' un errore fake che ne nasconde un altro o sbaglio qualcosa io?

escaflowne Profilo | Junior Member

......
>SELECT
>CASE WHEN count(distinct DataLogin) > 5 AND DataLogin BETWEEN
>@DataInizio-30 AND @DataInizio AND count(distinct DataLogin)
>>2 THEN 'Normal'
......

intanto metti un po' di parentesi, meglio abbondare che deficere ;)

Hamelin [FL] Profilo | Junior Member

Lì ho scritto la versione semplificata, quella completa è:

FUNZIONANTE:
------------------

SELECT @UserId,count(distinct [Data Login]),
CASE
WHEN count(distinct [Data Login]) > 48 THEN '5.Super'
WHEN count(distinct [Data Login]) > 12 AND count(distinct [Data Login]) <= 48 THEN '4.Normal'
WHEN count(distinct [Data Login]) > 0 AND count(distinct [Data Login]) <= 12 THEN '3.Occasionale'
END


ERRORE SUL GROUP BY:
------------------------------

SELECT @UserId,count(distinct [Data Login]),
CASE
WHEN count(distinct [Data Login]) > 48 THEN '5.Super'
WHEN count(distinct [Data Login]) > 12 AND count(distinct [Data Login]) <= 48
AND (([Data Login] BETWEEN DATEADD(day, -15, @DataInizio) AND @DataInizio) AND (count(distinct [Data Login]) > 2))
AND (([Data Login] BETWEEN DATEADD(day, -30, @DataInizio) AND DATEADD(day, -15, @DataInizio)) AND (count(distinct [Data Login]) > 2))
AND (([Data Login] BETWEEN DATEADD(day, -45, @DataInizio) AND DATEADD(day, -30, @DataInizio)) AND (count(distinct [Data Login]) > 2))
THEN '4.Normal'
WHEN count(distinct [Data Login]) > 0 AND count(distinct [Data Login]) <= 12 THEN '3.Occasionale'
END

escaflowne Profilo | Junior Member


>
>ERRORE SUL GROUP BY:
>------------------------------
>
>SELECT @UserId,count(distinct [Data Login]),
>CASE
>WHEN count(distinct [Data Login]) > 48 THEN '5.Super'
>WHEN count(distinct [Data Login]) > 12 AND count(distinct
>[Data Login]) <= 48
> AND (([Data Login] BETWEEN DATEADD(day, -15, @DataInizio) AND
>@DataInizio) AND (count(distinct [Data Login]) > 2))
> AND (([Data Login] BETWEEN DATEADD(day, -30, @DataInizio) AND
>DATEADD(day, -15, @DataInizio)) AND (count(distinct [Data Login])
>> 2))
> AND (([Data Login] BETWEEN DATEADD(day, -45, @DataInizio) AND
>DATEADD(day, -30, @DataInizio)) AND (count(distinct [Data Login])
>> 2))
>THEN '4.Normal'
>WHEN count(distinct [Data Login]) > 0 AND count(distinct [Data
>Login]) <= 12 THEN '3.Occasionale'
>END

Sembra, quindi, che il data Login presente nel between dia il problema.

Hamelin [FL] Profilo | Junior Member

Già, e chissà perchè.... non capisco perchè, se lo metto lì, lo vuole anche nella GROUP BY, e se ci sia un modo per risolvere...

Hamelin [FL] Profilo | Junior Member

Mi hanno dato una possibile prova di soluzione, e cioè implementare una logica del genere:

Select Q.UserID, Q.Conteggio,

(CASE WHEN Conteggio > 48 THEN '5.Super'
ELSE
CASE WHEN (Conteggio > 12 AND Conteggio <= 48)
AND (CDL15 > 2) AND (CDL3015 > 2) AND (CDL4530 >2)
THEN '4.Normal'
ELSE '3.Occasionale'
END
END) AS Tipo

FROM
(
SELECT UserId,count(distinct DataLogin) AS Conteggio,
(
select count(distinct DataLogin) FROM TabellaLogin
where UserID=T.UserID
and DataLogin BETWEEN DATEADD(day, -15, @DataInizio) AND @DataInizio
) as CDL15,

(
select count(distinct DataLogin) FROM TabellaLogin
where UserID=T.UserID
and DataLogin BETWEEN DATEADD(day, -30, @DataInizio) AND DATEADD(day, -15, @DataInizio)
) as CDL3015,

(
select count(distinct DataLogin) FROM TabellaLogin
where UserID=T.UserID
and DataLogin BETWEEN DATEADD(day, -45, @DataInizio) AND DATEADD(day, -30, @DataInizio)
) as CDL4530

FROM TabellaLogin T
WHERE T.DataLogin BETWEEN '01/01/2006' AND '01/04/2006'
group by UserID
) as q

Purtroppo però, per provarla, dovrei inserirla nella stored procedure che mi avevano fornito tempo fa, e qui inizia il caos ^^"

Beh, per completezza ho postato in questo thread la proposta di soluzione, per il resto, visto che ora il problema è con la stored procedure, la discussione di come implementarla al suo interno l'ho continuata qui:

http://www.dotnethell.it/forum/messages.aspx?ThreadID=6915

che mi sembra più appropriato come thread... Se qualcuno però avesse soluzioni alternative ben venga ^^"

Hamelin [FL] Profilo | Junior Member

Mi è stato detto che l'errore è dovuto al fatto che nella seconda query utilizzo DataLogin senza operatori di raggruppamento, e questo non è concesso, a meno di non riportare DataLogin nella clausola "GROUP BY" (cosa che, però, andrebbe contro la semantica dell'interrogazione).

Mi hanno fornito una soluzione alternativa, la seguente:

SELECT UserId,count(distinct DataLogin) AS Conteggio,
CASE
WHEN count(distinct DataLogin) > 48 THEN '5.Super'
WHEN count(distinct DataLogin) > 12
AND count(distinct DataLogin) <= 48
AND (((select DataLogin from TabellaLogin where DataLogin BETWEEN '01/01/2006' AND '01/04/2006') BETWEEN DATEADD(day, -15, @DataInizio) AND @DataInizio ) AND ( count(distinct DataLogin) > 2 ) )
AND (((select DataLogin from TabellaLogin where DataLogin BETWEEN '01/01/2006' AND '01/04/2006') BETWEEN DATEADD(day, -30, @DataInizio) AND DATEADD(day, -15, @DataInizio)) AND (count(distinct DataLogin) > 2))
AND (((select DataLogin from TabellaLogin where DataLogin BETWEEN '01/01/2006' AND '01/04/2006') BETWEEN DATEADD(day, -45, @DataInizio) AND DATEADD(day, -30, @DataInizio)) AND (count(distinct DataLogin) > 2))
THEN '4.Normal'
ELSE '3.Occasionale'
END AS Tipo
FROM TabellaLogin
WHERE DataLogin BETWEEN '01/01/2006' AND '01/04/2006'
GROUP BY UserId

La quale, però, mi dà il seguente errore:

Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression

Penso sia dovuto al fatto che stiamo dicendo
CASE WHEN SELECT DataLogin
che non gli piace molto... a logica, direi che si dovrebbe trasformare in qualcosa tipo

CASE WHEN ... AND count(((select DataLogin from TabellaLogin where DataLogin BETWEEN '01/01/2006' AND '01/04/2006') BETWEEN DATEADD(day, -15, @DataInizio) AND @DataInizio )) > 2 ]

Qualcuno avrebbe qualche idea di come poter rendere questa cosa in modo da utilizzare questa metodologia?

Grazie ancora per l'attenzione
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-2023
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5