52 messaggi dal 11 marzo 2008
Ciao,
ho bisogno di una mano nel realizzare una query.Devo estrarre, da una tabella contenente le letture dei contatori elettrici, gli utenti che hanno la stessa lettura da più di due anni.
La tabella in questione è la seguente
TB_LETTURE(COD_CONTATORE,DATA_LETTURA,QTA_LETTURA,CONSUMO,STATO_CONTATORE,ANNO_INSERIMENTO_CONTATORE)
Nel risultato finale non dovranno essere presenti gli utenti che hanno un contatore con STATO_CONTATORE'A' (aperto) da meno di due anni, nè quelli con STATO_CONTATORE 'C' (chiuso)
Qualcuno può darmi una mano?

Io ho pensato in un
SELECT COD_CONTATORE FROM TB_LETTURE WHERE STATO_CONTATORE='A'
AND ANNO_INSERIMENTO_CONTATORE < 2009 AND
GROUP BY COD_CONTATORE
HAVING SUM(CONSUMO)=0

ma non mi pare la soluzione corretta in quanto in questo modo escludo dal risultato, ad esempio, gli utenti che hanno avuto letture differenti fino al 01/01/2008 e poi la stessa lettura da quella data in avanti.

Grazie
salve,
symonx80 wrote:
Ciao,
ho bisogno di una mano nel realizzare una query.Devo estrarre, da una tabella contenente le letture dei contatori elettrici, gli utenti che hanno la stessa lettura da più di due anni.
La tabella in questione è la seguente
TB_LETTURE(COD_CONTATORE,DATA_LETTURA,QTA_LETTURA,CONSUMO,STATO_CONTATORE, ANNO_INSERIMENTO_CONTATORE)
Nel risultato finale non dovranno essere presenti gli utenti che hanno un contatore con STATO_CONTATORE'A' (aperto) da meno di due anni, nè quelli con STATO_CONTATORE 'C' (chiuso)
Qualcuno può darmi una mano?
Io ho pensato in un
SELECT COD_CONTATORE FROM TB_LETTURE WHERE STATO_CONTATORE='A' AND ANNO_INSERIMENTO_CONTATORE < 2009 AND
GROUP BY COD_CONTATORE
HAVING SUM(CONSUMO)=0
ma non mi pare la soluzione corretta in quanto in questo modo escludo dal risultato, ad esempio, gli utenti che hanno avuto letture differenti fino al 01/01/2008 e poi la stessa lettura da quella data in avanti.
Grazie

in queste esposizioni e' sinceramente necessario definire meglio lo scenario, quindi fornire i comandi CREATE TABLE corretti ed un minimo di dati per la popolazione iniziale tramite opportuni comandi INSERT INTO al fine di non richiedere da parte di chi vorrebbe aiutare anche la necessita' di sprecare tempo cercando di ricostruire, appunto, uno scenario sconosciuto... e' anche solitamente indicato esplicitare il motore SQL utilizzato

al di la' di cio', potrebbe forse essere interessante, in SQL Server 2005 o successivi, l'utilizzo di Common Table Expression al fine di produrre risultati logici intermedi da poter raffinare in successive operazioni.. ad esempio si potrebbe ragionare e codificare,

SET NOCOUNT ON;
USE tempdb;
GO
CREATE TABLE dbo.TB_LETTURE(
COD_CONTATORE int,
DATA_LETTURA datetime,
QTA_LETTURA decimal(18,4),
CONSUMO decimal(18,4),
STATO_CONTATORE char(1),
ANNO_INSERIMENTO_CONTATORE int
);
CREATE TABLE dbo.Utenti (
Id int NOT NULL,
Nome varchar(10),
COD_CONTATORE int
);
GO
INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20070101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20071101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20080101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20081101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20090101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20091101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20100101', 1, 1, 'A', 2005 );
INSERT INTO dbo.Utenti VALUES ( 1, 'Andrea', 1 );
GO
WITH cteGrouped AS (
SELECT t.COD_CONTATORE, SUM(t.CONSUMO) AS [Consumo Annuo], DATEPART( YYYY, t.DATA_LETTURA) AS [Anno]
FROM dbo.TB_LETTURE t
WHERE (
t.STATO_CONTATORE <> 'C'
AND (
t.STATO_CONTATORE = 'A'
AND t.ANNO_INSERIMENTO_CONTATORE < 2010
)

escludiamo anche l'anno corrente in quanto non terminato AND DATEPART(YYYY, t.DATA_LETTURA) < DATEPART(YYYY, GETDATE()) )
GROUP BY t.COD_CONTATORE, DATEPART( YYYY, t.DATA_LETTURA)
),
cteDifferenziali AS (
SELECT c.COD_CONTATORE, SUM(c.[Consumo Annuo]) AS [Globale], MIN(c.[Consumo Annuo]) AS [differenziale], COUNT(*) AS [y]
FROM cteGrouped c
GROUP BY c.COD_CONTATORE
HAVING SUM(c.[Consumo Annuo]) / COUNT(*) = MIN(c.[Consumo Annuo]) )
SELECT u.Id, u.Nome, c.COD_CONTATORE
FROM cteDifferenziali c
JOIN dbo.Utenti u ON u.COD_CONTATORE = c.COD_CONTATORE;
GO
DROP TABLE dbo.TB_LETTURE, dbo.Utenti;

dove abbiamo la prima cte che definisce il filtro iniziale di inclusione con preaggregazione a livello di anno solare, un successivo raggruppamento con evidenziazione del differenziale (MIN(consumo)) e del "numero di anni" di accorpamento.. essendo richiesto un consumo == per l'intero arco temporale, possiamo benissimo ridefinire l'espressione con una semplificazione matemetica quale:
somma aggregata negli anni / numero di anni = consumo minimo l'ultima proiezione invece si occupa di relazionare l'eventuale tabella utenti per ottenerne l'attributo da esporre al richiedente.. ma forse come ho inteso i dati o lo scenario non e' corretto rispetto alla tua vera richiesta..
saluti

Andrea Montanari (Microsoft MVP - SQL Server)
http://www.asql.biz - http://italy.mvps.org
http://www.hotelsole.com - http://www.hotelsolericcione.de
salve,
mi sono reso conto di una lacuna nel filtro, che comunque puo' essere comunque semplificato, ad esempio come di seguito:
SET NOCOUNT ON;
USE tempdb;
GO
CREATE TABLE dbo.TB_LETTURE(
COD_CONTATORE int,
DATA_LETTURA datetime,
QTA_LETTURA decimal(18,4),
CONSUMO decimal(18,4),
STATO_CONTATORE char(1),
ANNO_INSERIMENTO_CONTATORE int
);
CREATE TABLE dbo.Utenti (
Id int NOT NULL,
Nome varchar(10),
COD_CONTATORE int
);
GO
INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20070101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20071101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20080101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20081101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20090101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20091101', 1, 1, 'A', 2005 ); INSERT INTO dbo.TB_LETTURE VALUES ( 1, '20100101', 1, 1, 'A', 2005 ); INSERT INTO dbo.Utenti VALUES ( 1, 'Andrea', 1 );
GO
DECLARE @data date = '20100727';
data corrente
DECLARE @minAnni int = 2;
anni di sequenzialita' richiesti
WITH cteGrouped AS (
SELECT t.COD_CONTATORE, SUM(t.CONSUMO) AS [Consumo Annuo], DATEPART( YYYY, t.DATA_LETTURA) AS [Anno]
FROM dbo.TB_LETTURE t
WHERE

(t.STATO_CONTATORE <> 'C') questo e' ovviamente direttamente semplificabile e rimovibile

in quanto interessano solo:

STATO_CONTATORE = 'A' E ANNO_INSERIMENTO_CONTATORE <
AnnoCorrenteEscluso -@minAnni
(t.STATO_CONTATORE = 'A'
AND (
t.ANNO_INSERIMENTO_CONTATORE < (DATEPART(YYYY, @data) -@minAnni) )

includiamo SOLO gli ultimi 2 anni escluso l'anno corrente in quanto non terminato
AND DATEPART( YYYY, t.DATA_LETTURA) BETWEEN (DATEPART(YYYY, @data) -@minAnni) AND (DATEPART(YYYY, @data)-1)
)
GROUP BY t.COD_CONTATORE, DATEPART( YYYY, t.DATA_LETTURA)
),
cteDifferenziali AS (
SELECT c.COD_CONTATORE, SUM(c.[Consumo Annuo]) AS [Globale] FROM cteGrouped c
GROUP BY c.COD_CONTATORE

discriminiamo chi non ha il consumo totale / numero anni = 1 qualunque dei [Consumo Annuo]
HAVING SUM(c.[Consumo Annuo]) / @minAnni = MIN(c.[Consumo Annuo]) )
SELECT u.Id, u.Nome, c.COD_CONTATORE
FROM cteDifferenziali c
JOIN dbo.Utenti u ON u.COD_CONTATORE = c.COD_CONTATORE;
GO
DROP TABLE dbo.TB_LETTURE, dbo.Utenti;

dove in definitiva includiamo nel primo raggruppamento solo l'aggregazione dei consumi di chi ha un contatore aperto da prima di "n" anni da quanto richiesto, che il relativo stato sia esclusivamente "A", e solo per gli anni specificati nel minimo di anni di sequenzialita' richiesta, con l'esclusione dell'anno corrente che ovviamente non ha valori comparabili in quanto in corso di esecuzione..
dopo di cio', ci basta verificare che l'aggregazione globale del consumo diviso il numero minimo di anni sia uguale ad uno qualunque dei consumi annui, che maticamente implica l'uguaglianza sequenziale in caso di 2 anni come, da specifica...
saluti

Andrea Montanari (Microsoft MVP - SQL Server)
http://www.asql.biz - http://italy.mvps.org
http://www.hotelsole.com - http://www.hotelsolericcione.de

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.
Community
Ultimi messaggi
UTENTI ONLINE
In primo piano

I più letti di oggi

Media
In evidenza
MISC