Breakdown SUM of INT by Each Month - tsql

Query I use to find the total spend per station for a year. How do I break down the spend of each product by month i.e. each month as a column?
e.DT is a datetime, but only gets populated once a month.
select s.StationID, sum(e.Spend)
from Station s with(nolock)
join Expenditure e with(nolock)
on e.ProductID = s.ProductID
where e.DT between '1 JAN 18' and '1 DEC 18'
group by s.StationID
order by sum(e.Spend) desc

It would be much easier to report your month/year data points as separate rows, rather than columns. I suggest the following query:
SELECT
s.StationID,
LEFT(CONVERT(varchar, e.DT, 120), 7) AS ym,
SUM(e.Spend) AS total_spend
FROM Station s WITH(nolock)
INNER JOIN Expenditure e WITH(nolock)
ON e.ProductID = s.ProductID
WHERE e.DT BETWEEN '2018-01-01' AND '2018-12-01'
GROUP BY
s.StationID,
LEFT(CONVERT(varchar, e.DT, 120), 7)
ORDER BY
s.StationID,
SUM(e.Spend) DESC;

Try with PIVOT this way:
select ProductID, [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]
from (
select Spend, month(DT) m, ProductID
from Expenditure
where DT between '1 JAN 18' and '1 DEC 18'
) r
PIVOT
( sum(Spend)
for m in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
)
AS PivotTable;

Related

Show every week of the Year even if there is no data

I have query that pulls data by week and groups it together. But i does not display weeks that doesn't have any data. I want show all weeks even if they don't have data as null maybe
Here is the query if someone can help me with this it will awesome
SELECT
DATEADD (week, datediff(week, 0, StartDate), -1) as 'WeekOf'
,DATEADD (week, datediff(week, 0, StartDate), +5) as 'to'
,DATEPART(wk, StartDate) as 'WeekNumber'
FROM [DESOutage].[dbo].[OPSInterruption]
Where StartDate > '2020-01-01' and EndDate <'2020-02-01'
Group by DATEADD (week, datediff(week, 0, StartDate), -1),DATEPART(wk, StartDate),DATEADD (week, datediff(week, 0, StartDate), +5)
***************Output***************
As you could see week 2 and 4 is missing out since there is no data being returned. I would still like to see week 2 and 4 in the output with maybe 0 as result.
WeekOf to WeekNumber
2019-12-29 00:00:00.000 2020-01-04 00:00:00.000 1
2020-01-12 00:00:00.000 2020-01-18 00:00:00.000 3
2020-01-26 00:00:00.000 2020-02-01 00:00:00.000 5
You probably need a calendar table. Here is a quick way of generating one, with an untested implementation of your code. I am assuming that the StartDate may contain a time component thus the need to coalesce the dates.
DECLARE #StartYear DATETIME = '20200101'
DECLARE #days INT = 366
;WITH
E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 1*10^1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), -- 1*10^2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), -- 1*10^4 or 10,000 rows
E8(N) AS (SELECT 1 FROM E4 a, E4 b), -- 1*10^8 or 100,000,000 rows
Tally(N) AS (SELECT TOP (#Days) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E8),
Calendar AS (
SELECT StartOfDay = DATEADD(dd,N-1,#StartYear),
EndOfDay = DATEADD(second, -1, DATEADD(dd,N ,#StartYear))
FROM Tally)
SELECT DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay) ), -1) as 'WeekOf'
, DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), +5) as 'to'
, DATEPART(wk, COALESCE(x.StartDate, c.StartOfDay)) as 'WeekNumber'
FROM Calendar c
INNER JOIN [DESOutage].[dbo].[OPSInterruption] x
ON x.StartDate > c.StartOfDay AND x.StartDate <= c.EndOfDay
WHERE c.StartOfDay > '2020-01-01' AND c.StartOfDay <'2020-02-01'
GROUP BY DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), -1),
DATEPART(wk, COALESCE(x.StartDate, c.StartOfDay)),
DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), +5)

How can I remove the null values and make it to 10 rows in Postgresql?

I am new to Postgresql. I have a table called 'sales'.
create table sales
(
cust varchar(20),
prod varchar(20),
day integer,
month integer,
year integer,
state char(2),
quant integer
)
insert into sales values ('Bloom', 'Pepsi', 2, 12, 2001, 'NY', 4232);
insert into sales values ('Knuth', 'Bread', 23, 5, 2005, 'PA', 4167);
insert into sales values ('Emily', 'Pepsi', 22, 1, 2006, 'CT', 4404);
insert into sales values ('Emily', 'Fruits', 11, 1, 2000, 'NJ', 4369);
insert into sales values ('Helen', 'Milk', 7, 11, 2006, 'CT', 210);
insert into sales values ('Emily', 'Soap', 2, 4, 2002, 'CT', 2549);
something like this:
Now I want to find the “most favorable” month (when most amount of the product was
sold) and the “least favorable” month (when the least amount of the product was sold) for each product.
The result should be like this:
I entered
SELECT
prod product,
MAX(CASE WHEN rn2 = 1 THEN month END) MOST_FAV_MO,
MAX(CASE WHEN rn1 = 1 THEN month END) LEAST_FAV_MO
FROM (
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY prod ORDER BY quant ) rn1,
ROW_NUMBER() OVER(PARTITION BY prod ORDER BY quant DESC) rn2
FROM sales
) x
WHERE rn1 = 1 or rn2 = 1
GROUP BY prod,quant;
Then there are null values for each product and there are 20 rows in total:
So how can I remove the null values in these rows and make the total number of rows to 10 (There are 10 distinct products in total)???
I would say that the GROUP BY clause should be
GROUP BY prod
Otherwise you get one line per different quant, which is not what you want.

How to find gap date and minimum date in the same query?

I have a table customer_history which log customer_id and modification_date.
When customer_id is not modified there is no entry in the table
I can find when customer_id haven't been modified (=last_date_with_no_modification). I look for when the date is missing (= Gaps and Islands problem).
But in the same query if no date is missing the value last_date_with_no_modification should
be DATEADD(DAY,-1,min(modification_date)) for the customer_id.
I don't know how to add this last condition in my SQL query?
I use following tables:
"Customer_history" table:
customer_id modification_date
1 2017-12-20
1 2017-12-19
1 2017-12-17
2 2017-12-20
2 2017-12-18
2 2017-12-17
2 2017-12-15
3 2017-12-20
3 2017-12-19
"#tmp_calendar" table:
date
2017-12-15
2017-12-16
2017-12-17
2017-12-18
2017-12-19
2017-12-20
Query used to qet gap date:
WITH CTE_GAP AS
(SELECT ch.customer_id,
LAG(ch.modification_date) OVER(PARTITION BY ch.customer_id ORDER BY ch.modification_date) as GapStart,
ch.modification_date as GapEnd,
(DATEDIFF(DAY,LAG(ch.modification_date) OVER(PARTITION BY ch.customer_id ORDER BY ch.modification_date), ch.modification_date)-1) GapDays
FROM customer_history ch )
SELECT cg.customer_id,
DATEADD(DAY,1,MAX(cg.GapStart)) as last_date_with_no_modification
FROM CTE_GAP cg
CROSS JOIN #tmp_calendar c
WHERE cg.GapDays >0
AND c.date BETWEEN DATEADD(DAY,1,cg.GapStart) AND DATEADD(DAY,-1,cg.GapEnd)
GROUP BY cg.customer_id
Result:
customer_id last_date_with_no_modification
1 2017-12-18
2 2017-12-19
3 2017-12-19 (Row missing)
How to get customer_id 3?
Something this should work:
WITH CTE_GAP
AS
(
SELECT
ch.customer_id,
LAG(ch.modification_date) OVER(PARTITION BY ch.customer_id ORDER BY ch.modification_date) as GapStart,
ch.modification_date as GapEnd,
(DATEDIFF(DAY,LAG(ch.modification_date) OVER(PARTITION BY ch.customer_id ORDER BY ch.modification_date), ch.modification_date)-1) GapDays
FROM #customer_history ch
)
SELECT DISTINCT
C.customer_id
, ISNULL(LD.last_date_with_no_modification, LD_NO_GAP.last_date_with_no_modification) last_date_with_no_modification
FROM
customer_history C
LEFT JOIN
(
SELECT
cg.customer_id,
DATEADD(DAY, 1, MAX(cg.GapStart)) last_date_with_no_modification
FROM
CTE_GAP cg
CROSS JOIN #tmp_calendar c
WHERE
cg.GapDays >0
AND c.date BETWEEN DATEADD(DAY, 1, cg.GapStart) AND DATEADD(DAY, -1, cg.GapEnd)
GROUP BY cg.customer_id
) LD
ON C.customer_id = LD.customer_id
LEFT JOIN
(
SELECT
customer_id
, DATEADD(DAY, -1, MIN(modification_date)) last_date_with_no_modification
FROM customer_history
GROUP BY customer_id
) LD_NO_GAP
ON C.customer_id = LD_NO_GAP.customer_id

How do I code to find 3 or more occurrences within a time period (6 weeks)?

I need to build a patient population based on clinic visits. The qualifying criteria (filter) for this population is 3 visits in a 6 week period over the evaluation year. How can I code this?
DECLARE #Records TABLE (ptID INT, date DATE)
INSERT INTO #Records VALUES
(1, '2016-01-01')
,(1, '2016-01-05')
,(1, '2016-02-01')
,(1, '2016-10-01')
,(2, '2015-12-01')
,(2, '2015-12-10')
,(2, '2015-12-31')
,(2, '2016-01-01')
,(2, '2016-01-05')
,(2, '2016-03-05')
,(3, '2016-01-01')
,(3, '2016-02-01')
,(3, '2016-03-01')
,(3, '2016-04-01')
,(3, '2016-05-01')
,(3, '2016-06-01')
,(3, '2016-07-01')
,(3, '2016-08-01')
select a.ptID , a.date
from #Records a
join #Records b
on a.ptID = b.ptID
and datediff(wk, a.date, b.date) <= 6
and datediff(wk, a.date, b.date) > 0
and DATEPART(yy, a.date) = DATEPART(yy, b.date)
group by a.ptID, a.date
having count(*) >= 2
Paparazzi deserves all the credit for comparing the table to itself. I'm just refining his comparison here.
DECLARE #Records TABLE (
PatientID INT
,VisitDate DATE
)
INSERT INTO #Records VALUES
(1, '2016-01-01')
,(1, '2016-01-05')
,(1, '2016-02-01')
,(1, '2016-10-01')
,(2, '2015-12-01')
,(2, '2016-01-01')
,(2, '2016-01-05')
,(2, '2016-03-05')
;WITH SixWeeks
AS (
SELECT a.PatientID AS PID1, a.VisitDate AS Date1,
b.PatientID AS PID2, b.VisitDate AS Date2,
DATEDIFF(dd, a.VisitDate, b.VisitDate) AS DD
FROM #Records a
JOIN #Records b
ON a.PatientID = b.PatientID
AND DATEDIFF(dd, a.VisitDate, b.VisitDate) <= 42
AND DATEPART(yy,a.VisitDate) = '2016'
WHERE EXISTS (SELECT * FROM #Records WHERE (VisitDate > a.VisitDate AND VisitDate < b.VisitDate))
)
SELECT PID1 FROM SixWeeks
GROUP BY PID1

How to get Quarter from a date in Firebird SQL

I can easily get total sales in this month and previous month.
SELECT ‘This Mount’, SUM(Price) FROM Sales
WHERE EXTRACT(MONTH FROM OrderDate) = EXTRACT(MONTH FROM CURRENT_DATE)
AND EXTRACT(YEAR FROM OrderDate) = EXTRACT(YEAR FROM CURRENT_DATE)
Union All
SELECT ‘Previous Month’, SUM(Price) FROM Sales
WHERE EXTRACT(MONTH FROM OrderDate) = EXTRACT(MONTH FROM CURRENT_DATE)
AND EXTRACT(YEAR FROM OrderDate) = EXTRACT(YEAR FROM CURRENT_DATE)
I want to get the total sales in this quarter and previous quarter.
Getting quarter from a date is very easy with MS-SQL as follows:
SELECT DATEPART(QUARTER, #date)
How can I do this with Firebird?
Use DECODE function in conjunction with EXTRACT:
SELECT
DECODE(EXTRACT(MONTH FROM <date_field>),
1, 'I',
2, 'I',
3, 'I',
4, 'II',
5, 'II',
6, 'II',
7, 'III',
8, 'III',
9, 'III',
'IV')
FROM
<some_table>
Or just
SELECT
(EXTRACT(MONTH FROM <date_field>) - 1) / 3 + 1
FROM
<some_table>
SELECT dates,
EXTRACT(MONTH from dates) as SalesMonth,
floor(((EXTRACT(MONTH from dates)-1) / 3.0 + 1)) as QTR
from CustomerPO
where ((dates > '1/1/2016') and (dates < '12/31/2016'))
order by dates
Here, 'dates' is the field name of Order table 'CustomerPO'
SELECT dates,
EXTRACT(MONTH from dates) as SalesMonth,
ceil(EXTRACT(MONTH from dates) / 3) as QTR
from CustomerPO
where ((dates > '1/1/2016') and (dates < '12/31/2016'))
order by dates