Creazione stored con gestione trimestri

di il
7 risposte

Creazione stored con gestione trimestri

Buongiorno,
avrei bisogno di un vostro aiuto allora io devo creare una query che mi deve restituire dei record in base ai trimestri del range di date che inserisco. La cosa che non riesco a fare è che se un trimestre non ha valori deve riportare i dati del trimestre precedente e se invece dovesse avere dati questi devono essere sommati con i dati del trimestre precedente.

Es. Trim1 20 record
Trim2 (vuoto) (allora deve avere anche esso 20 record che corrispondo a quelli del primo trim.)
Trim3 30 record (perchè è dato dai 20 record del secondo trim. + 10 che fanno parte del terzo)
Trim4 30 record (allora deve avere anche esso 20 record che corrispondo a quelli del terzo trim.)

e così via se dovesse esserci un range di dati di due anni o più.

Il codice che ho scritto è il seguente ma mi riporta solo i dati presenti nei trimestri di riferimento
create table #TableSheetClosed1 (TotalRecord int, QuarterValue varchar(10), anno varchar(4));
  WITH Quarters AS (
   SELECT Q = 'Q1', MonthBegin = 1, MonthEnd = 3 UNION
   SELECT Q = 'Q2', MonthBegin = 4, MonthEnd = 6 UNION
   SELECT Q = 'Q3', MonthBegin = 7, MonthEnd = 9 UNION
   SELECT Q = 'Q4', MonthBegin = 10, MonthEnd = 12
)
insert into #TableSheetClosed1
SELECT
   count(tbl_Arc.idmoc),
   [Quarter] = q.Q + '-' + RIGHT(CONVERT(VARCHAR(8), DateIns, 1),2),
   YEAR (tbl_Arc.DateIns)
   FROM tbl_Arc
   INNER JOIN Quarters q ON
      DATEPART(m, CONVERT(DATETIME, DateIns)) >= q.MonthBegin AND
      DATEPART(m, CONVERT(DATETIME, DateIns)) <= q.MonthEnd
	  
where (tbl_Arc.DateIns >='2021-01-01' and tbl_Arc.DateIns <='2022-12-31') and IDStatus='A'
group by tbl_Arc.IDStatus, tbl_Arc.DateIns, Q

select TotalRecord,QuarterValue,anno from #TableSheetClosed1
group by QuarterValue,anno
order by anno


drop table #TableSheetClosed1
il risultato che genera è il seguente:

24 Q1-21 2021
24 Q3-21 2021
20 Q1-22 2022
18 Q3-22 2022

potete darmi una mano? Grazie

7 Risposte

  • Re: Creazione stored con gestione trimestri

    Salve,
    il tuo result set ha come valorizzazione {TotalRecord int, .... }
    questo per "ogni" trimestre...
    e' questo il "valore" che vuoi?... non pare un in linea con la tua richiesta "scritta" ...
    messa cosi', sembrerebbe un "running total" di COUNT...

    ma mi sembra anche "infinito", nel senso che
    Q1 vuoi i dati Q1 + dati Q0
    Q2 vuoi dati Q2 + dati (Q1 , che e' composto da Q1 + Q0)
    etc

    e' una specie di "sommatoria costante"??

    salutoni romagnoli
    --
    Andrea
  • Re: Creazione stored con gestione trimestri

    Ciao grazie della tua risposta, in pratica si. Secondo me conviene creare un record a zero denominato Q0 e poi la teoria sarebbe la seguente:
    Q1= Q1+Q0
    Q2= Q2 +Q1
    Q3= Q3 +Q2
    Q4= Q4 +Q3

    se nel range di date viene preso un periodo + lungo di un anno per esempio due anni (ma potrebbe essere anche un anno e mezzo)

    allora per il secondo anno sarebbe cosi:
    Q1= Q1 +Q4 (del primo anno)
    e poi via cosi come nel primo anno

    Ciao grazie
  • Re: Creazione stored con gestione trimestri

    Salve,
    da come la metti, parti sempre DALLA PRIMA RIGA che hai... se tu avessi la prima riga nell'anno 2000, partiresti da quella....
    piu' gli eventuali altri dati che recuperi man mano...

    NON li vuoi ruotati in colonne, vero?

    salutoni romagnoli
    --
    Andrea
  • Re: Creazione stored con gestione trimestri

    Salve,
    sono stanco ma ci provo lo stesso... sicuramente altri faranno di meglio...
    prendiamo la popolazione
    
    INSERT INTO dbo.tbl_Arc
    	VALUES ( '2000-01-01' )
    	, ( '2010-01-01' )
    	, ( '2018-01-01' ), (' 2018-08-01')
    	, ( '2019-01-01' ), (' 2018-04-01'), (' 2018-07-01')
    
    	, ( '2020-01-01' )
    	
    		, (' 2020-04-01')
    		, (' 2020-05-01')
    
    		, (' 2020-07-01')
    		, (' 2020-08-01')
    		, (' 2020-09-01')
    		
    		, (' 2020-10-01')
    		, (' 2020-10-02')
    		, (' 2020-11-01')
    		, (' 2020-12-01')
    
    		, (' 2021-01-01');
    
    dove ovviamente ho fatto inserimenti un po' piu' articolati per il 2020... mentre gli altri anni sono scarichi o addirittura vuoti...
    la mia tabella contiente 1 sola colonna visto che devo farne solo il count...

    non so che versione di SQL Server tu stia utilizzando, ma dalla v. 2012 la funzione COUNT(..) supporte le feature di windowing, quindi puoi aggregare COUNT(..) OVER (ORDER BY nomi colonne) per avere il tuo raggruppamento desiderato...
    cio' ti permette di avere il tuo "running total",
    
    -- Costruzione del running total per Anno, Trimestre
    -- utilizzando la windowing feature di COUNT() OVER...
    SELECT DATEPART(YEAR, DataIns) AS Y, DATEPART(QUARTER, DataIns) AS Q 
    	, COUNT(*) OVER(ORDER BY DATEPART(YEAR, DataIns), DATEPART(QUARTER, DataIns)) AS C
    	FROM dbo.tbl_Arc;
    --<-----------
    Y           Q           C
    ----------- ----------- -----------
    2000        1           1
    2010        1           2
    2018        1           3
    2018        2           4
    2018        3           6
    2018        3           6
    2019        1           7
    2020        1           8
    2020        2           10
    2020        2           10
    2020        3           13
    2020        3           13
    2020        3           13
    2020        4           17
    2020        4           17
    2020        4           17
    2020        4           17
    2021        1           18
    
    altra funzionalita' interessante, e' l'utilizzo di common table expresisons ricorsive per la costruzione anche dinamica di working set di tipo calendario, quindi nel nostro caso utilizzeremo una CTE per costruire i numeri di trimestre da 1 a 4, e un'altra CTE per costruire i numeri degli anni da MIN(data in tabella) ad ora (oppure altro filtro che tu vorrai gestire)...
    ti questi 2 set, da 1 a 4 (trimestre) e da 2000 a 2021 (anno), effettueremo il cross join per avere
    2001 T1 + 2001 T2 + 2001 T3 + 2001 T4
    ....
    2021 T1 + 2021 T2 + 2021 T3 + 2021 T4

    tipicamente:
    
    DECLARE @MinYear int;
    
    SELECT @MinYear = MIN(DATEPART(YEAR, DataIns))
    	FROM dbo.tbl_Arc;
    
    -- Costruzione delle Righe Anno + Trimestre
    -- usando delle common table expressions
    -- dove le ricorsioni generano i Q da 1 a 4
    -- in cross join con gli anni da @MinYear a anno corrente
    WITH Qrtr(Q) AS (
    	SELECT 1 
    	UNION ALL
    	SELECT Q + 1 FROM Qrtr
    		WHERE Q < 4
    	),
    Yr(Y) AS (
    	SELECT DATEPART(YEAR, GETDATE()) 
    	UNION ALL
    	SELECT Y - 1 
    		FROM Yr
    		WHERE Y > @MinYear 
    	),
    cteTime AS (
    	SELECT DISTINCT Yr.Y, Qrtr.Q
    		FROM Yr, Qrtr
    	)
    	SELECT *
    		FROM cteTime
    		ORDER BY cteTime.Y, cteTime.Q
    
    sono 88 righe e non le posto qui

    con questi "2" pezzi, possiamo andare a combinare la nostra query...

    mettiamo in join il nostro prodotto temporale (anni + trimestri) con quanto restituito dalla nostra aggregazione, e ci resta solo da "chiudere i buchi", nel senso che gli slot non matchati dalla join saranno NULL per le valorizzazioni...

    allora.... sicuramente c'e' un modo "migliore" non basato su una query innestata come ti vado ad illustrare...
    ma sono stanco e mi fermo a questo approccio :
    
    -- unione delle funzionalita'
    DECLARE @MinYear int;
    
    SELECT @MinYear = MIN(DATEPART(YEAR, DataIns))
    	FROM dbo.tbl_Arc;
    
    WITH Qrtr(Q) AS (
    	SELECT 1 
    	UNION ALL
    	SELECT Q + 1 FROM Qrtr
    		WHERE Q < 4
    	),
    Yr(Y) AS (
    	SELECT DATEPART(YEAR, GETDATE()) 
    	UNION ALL
    	SELECT Y - 1 
    		FROM Yr
    		WHERE Y > @MinYear  
    	),
    cteTime AS (
    	SELECT DISTINCT Yr.Y, Qrtr.Q
    		FROM Yr, Qrtr
    	), 
    -- e questo e' il 	nostro time set....
    	
    --- continuiamo con il running total come prima esemplificato... (OCCHIO CHE LE VIRGOLE CONTINUANO :D)	
    cteCount AS (
    	SELECT DATEPART(YEAR, DataIns) AS Y, DATEPART(QUARTER, DataIns) AS Q 
    		, COUNT(*) OVER(ORDER BY DATEPART(YEAR, DataIns), DATEPART(QUARTER, DataIns)) AS C
    		FROM dbo.tbl_Arc
    	)
    	-- a questo punto ho "tutto", e metto tutto insieme, anche se... vedi la colonna FinalCount
    	SELECT DISTINCT ct.Y, ct.Q
    		-- questo sara' NULL negli slot NON matchati dal JOIN con i dati
    		-- , c1.C -- quindi questa colonna NON andra' proiettata
    		
    		-- quindi, visto che al momento non mi viene in mente niente
    		-- di meglio, utilizzo una sub query per prendere comunque
    		-- il MAX di count prima dello slot corrente, visto che l'aggregazione
    		-- produce il risultato corretto....
    		, CASE WHEN c1.C IS NULL 
    			THEN (SELECT MAX(c2.C)
    					FROM cteCount c2
    					WHERE c2.Y <= ct.Y AND c2.Q <= ct.Q
    					)
    			ELSE c1.C
    			END FinalCount
    		-- MA C'E' SICURAMENTE UN ALTRO MODO,
    		-- MA ORA SONO STANCO :D:D:D
    		
    		FROM cteTime ct 
    			LEFT JOIN cteCount c1 ON c1.Y = ct.Y AND c1.Q = ct.Q
    		ORDER BY ct.Y, ct.Q;
    
    anche queste sono 88 righe e non le posto

    ha senso quello che ho scritto?

    poi gli eventuali filtri li aggiungi TU

    salutoni romagnoli
    --
    Andrea
  • Re: Creazione stored con gestione trimestri

    Grazie mille sei stato grande era quello che mi serviva, ora mi studio meglio il codice ed aggiungo i vari filtri che mi servono
  • Re: Creazione stored con gestione trimestri

    Salve,
    oggi, un po' piu' fresco, ribadisco che la sub query non mi piace.... in generale non amo le sub queries...
    e possiamo risolvere con un OUTER APPLY...
    tipicamente,
    
    
    -- unione delle funzionalita'
    DECLARE @MinYear int;
    
    SELECT @MinYear = MIN(DATEPART(YEAR, DataIns))
    	FROM dbo.tbl_Arc;
    
    WITH Qrtr(Q) AS (
    	SELECT 1 
    	UNION ALL
    	SELECT Q + 1 FROM Qrtr
    		WHERE Q < 4
    	),
    Yr(Y) AS (
    	SELECT DATEPART(YEAR, GETDATE()) 
    	UNION ALL
    	SELECT Y - 1 
    		FROM Yr
    		WHERE Y > @MinYear  
    	),
    cteTime AS (
    	SELECT DISTINCT Yr.Y, Qrtr.Q
    		FROM Yr, Qrtr
    	), 
    -- e questo e' il 	nostro time set....
    	
    --- continuiamo con il running total come prima esemplificato... (OCCHIO CHE LE VIRGOLE CONTINUANO :D)	
    cteCount AS (
    	SELECT DATEPART(YEAR, DataIns) AS Y, DATEPART(QUARTER, DataIns) AS Q 
    		, COUNT(*) OVER(ORDER BY DATEPART(YEAR, DataIns), DATEPART(QUARTER, DataIns)) AS C
    		FROM dbo.tbl_Arc
    	)
    	-- a questo punto ho "tutto", e metto tutto insieme, anche se... vedi la colonna FinalCount
    	SELECT DISTINCT ct.Y, ct.Q
    			-- al posto della sub query, utilizziamo un OUTER APPLY
    			-- il risultato dei piani di esecuzione e' il medesimo,
    			-- ma concettualmente dal mio punto di vista e' molto
    			-- piu' pulito ed elegante... questioni personali :)
    			, ISNULL(c1.C, NotMatched.cNotMatched) AS FinalCount
    		
    		FROM cteTime ct 
    			LEFT JOIN cteCount c1 ON c1.Y = ct.Y AND c1.Q = ct.Q
    			-- outer apply di risoluzione dei not matched
    			OUTER APPLY (
    				SELECT MAX(c2.C) AS cNotMatched
    					FROM cteCount c2
    					WHERE c2.Y <= ct.Y AND c2.Q <= ct.Q
    						AND c1.C IS NULL
    				) NotMatched
    		ORDER BY ct.Y, ct.Q;
    
    salutoni romagnoli
    --
    Andrea
  • Re: Creazione stored con gestione trimestri

    Grazie mille
Devi accedere o registrarti per scrivere nel forum
7 risposte