How to get sum by MonthYear in sql? - sql

I have a table like this
id Date Amount
1 2016-09-29 09:25:37.000 25.13
2 2016-08-01 17:20:39.000 598.00
3 2016-09-29 09:24:47.000 15.60
4 2016-07-28 17:50:11.000 61.80
5 2016-07-28 17:53:56.000 31.40
6 2016-07-22 10:40:27.000 74.16
I'm trying to get two columns like this,
MonthYear Total
Sep 2016 40.73
Aug 2016 598.00
Jul 2016 167.36
But, I want to get most recent year and month to top.

Try this sql,
SELECT CONVERT(CHAR(4), Date, 100) + CONVERT(CHAR(4), Date, 120)
AS MonthYear, SUM(Amount)
AS Total,
CAST(CONVERT(varchar(4), Date, 120) AS int)
AS Year, DATEPART(m, Date) As Month
FROM your_table
GROUP BY CONVERT(CHAR(4), Date, 100) + CONVERT(CHAR(4), Date,120),CAST(CONVERT(varchar(4), Date, 120) AS int), DATEPART(m, Date)
ORDER BY Year DESC, Month DESC

Just an other perspective.
Query
SELECT t.[MonthYear], SUM(t.[Amount]) AS [Total] FROM(
SELECT RIGHT((CONVERT(VARCHAR(11), [Date], 106)), 8) as [MonthYear], [Amount]
FROM [your_table_name]
)t
GROUP BY t.[MonthYear];

Try below
SELECT Datename(MONTH, [Date]) month_name,
Year([Date]) year,
Sum([Amount]),
Month([date]) month_no
FROM #Table1
GROUP BY Datename(MONTH, [Date]),
Year([Date]),
Month([date])
ORDER BY Month([date]) DESC,
Year([Date]),
Datename(MONTH, [Date])

Related

Grouping several years' data by week

I am using Covid world data, which tracks number of new_cases every day. I have data from 2020-01-01 to present day. This is my current query:
SELECT MIN(date) as week, datepart(iso_week, date) week_num, sum(new_cases) as [Total Cases]
FROM covid_data_cleaned
GROUP BY DATEPART(iso_week, date), DATEPART(year, date)
ORDER BY MIN(date) DESC
Which gives me pretty much what I want except for when the year changes:
week week_num Total Cases
2022-01-10 2 20803473
2022-01-03 1 17217248
**2022-01-01 52 2115780**
**2021-12-27 52 7971560**
2021-12-20 51 5561762
I want to figure out the workaround to combining the '52' values together. Alternatively, my dataset has 112 weeks; can I assign the values 1-112 to the whole dataset rather than 1-52 for each year?
You could aggregate on a correction via a CASE WHEN for the iso year.
SELECT
min(weekstart) as [week]
, [iso_week] as weeknum
, sum(new_cases) as [Total Cases]
FROM
(
SELECT
min([date]) as weekstart
, datepart(iso_week, [date]) as [iso_week]
, case
when month(min([date])) = 1
and datepart(iso_week, [date]) >= 52
then year([date]) - 1
when month(min([date])) = 12
and datepart(iso_week, [date]) = 1
then year([date]) + 1
else year([date])
end as iso_year
, sum(new_cases) as new_cases
FROM covid_data_cleaned
GROUP BY year([date]), datepart(iso_week, [date])
) q
GROUP BY iso_year, [iso_week]
ORDER BY [week] DESC;
Instead of iso week, you can use week.
DECLARE #table table(datev date, new_Cases int)
INSERT INTO #table values
('2022-01-01',123),('2022-01-09',432),('2022-01-03',123),('2021-12-27',234);
SELECT MIN(datev) as week, datepart(week, datev) week_num, sum(new_cases) as [Total Cases]
FROM #table
GROUP BY DATEPART(week, datev), DATEPART(year, datev)
ORDER BY MIN(datev) DESC
week
week_num
Total Cases
2022-01-09
3
432
2022-01-03
2
123
2022-01-01
1
123
2021-12-27
53
234

Cumulative total for quarters

I currently have a query which produces data for each quarter, what I would like is for the total to be a running total. So quarter 2's figures will be added on to quarter 1's and so on until the end of the fiscal year, where the process will then start again.
select datepart(year,date) as Year,
CASE
WHEN MONTH(date) BETWEEN 1 AND 3 THEN convert(char(4), YEAR(date) - 1) + 'Q4'
WHEN MONTH(date) BETWEEN 4 AND 6 THEN convert(char(4), YEAR(date) - 0) + 'Q1'
WHEN MONTH(date) BETWEEN 7 AND 9 THEN convert(char(4), YEAR(date) - 0) + 'Q2'
WHEN MONTH(date) BETWEEN 10 AND 12 THEN convert(char(4), YEAR(date) - 0) + 'Q3'
END AS Quarter,
Data1,
Data2,
FROM TABLE
GROUP BY datepart(year,date),
CASE
WHEN MONTH(date) BETWEEN 1 AND 3 THEN convert(char(4), YEAR(date) - 1) + 'Q4'
WHEN MONTH(date) BETWEEN 4 AND 6 THEN convert(char(4), YEAR(date) - 0) + 'Q1'
WHEN MONTH(date) BETWEEN 7 AND 9 THEN convert(char(4), YEAR(date) - 0) + 'Q2'
WHEN MONTH(date) BETWEEN 10 AND 12 THEN convert(char(4), YEAR(date) - 0) + 'Q3'
END
This is what I currently get
year quarter data1 data2
2016 Q1 10 4
2016 Q2 5 6
2016 Q3 4 2
2017 Q4 1 1
I would like the output to look like this
year quarter data1 data2
2016 Q1 10 4
2016 Q2 15 10
2016 Q3 19 12
2017 Q4 20 13
Thanks for any help
You can wrap your query in a CTE and use CROSS APPLY in order to calculate the cumulative sum:
;WITH CTE AS (
-- your query
)
SELECT t1.year, t1.quarter, t1.data1, t3.cumulative_sum
FROM CTE AS t1
CROSS APPLY (
SELECT SUM(data2) AS cumulative_sum
FROM CTE AS t2
WHERE t2.year <= t1.year AND t2.quarter <= t1.quarter) AS t3
First, your query can be simplified to:
SELECT datepart(year, date) as Year,
datename(year, date) + 'Q' + datename(quarter, dateadd(month, -3, date)) as quarter,
SUM(Data1) as Data1, SUM(Data2) as Data2
FROM TABLE
GROUP BY datepart(year, date) as Year,
datename(year, date) + 'Q' + datename(quarter, dateadd(month, -3, date)) as quarter
ORDER BY quarter;
Note the use of datename(). This returns a string rather than a number, which is easier for string concatenation. In addition, this simplifies the logic just by subtracting 3 months to get the quarter.
In SQL Server 2012+, you can use the built in functionality for cumulative sum. In SQL Server 2008, one method uses apply:
WITH t as (
SELECT datepart(year, date) as Year,
datename(year, date) + 'Q' + datename(quarter, dateadd(month, -3, date)) as quarter,
SUM(Data1) as Data1, SUM(Data2) as Data2
FROM TABLE
GROUP BY datepart(year, date) as Year,
datename(year, date) + 'Q' + datename(quarter, dateadd(month, -3, date)) as quarter
)
SELECT t.*, tt.data2
FROM t OUTER APPLY
(SELECT SUM(data1) as data2
FROM t t2
WHERE t2.quarter <= t.quarter
) tt;

Add sub-condition in the where clause

I am not sure what is the proper name to give to this condition in the where clause. I am using Microsoft SQL Server 2008.
Basically, I have an order table with the following columns.
MyOrderTable:
OrderAmount Paid OrderDate
100 Y 2016-08-10 21:15:52.000
120 N 2016-08-10 20:15:12.000
300 Y 2016-08-09 11:10:22.000
500 Y 2016-06-09 10:10:54.000
125 N 2016-06-09 09:15:13.000
325 N 2015-11-09 09:15:13.000
Currently, I have the following SQL query:
SELECT DATEPART(Year, OrderDate) TheYear, DATEPART(Month, OrderDate) TheMonth,
SUM(ISNULL(OrderAmount, 0)) as SumOrderAmount
FROM MyOrderTable
WHERE YEAR(OrderDate) = 2016
GROUP BY DATEPART(Year, OrderDate), DATEPART(Month, OrderDate)
ORDER BY TheYear, TheMonth
The result returned from the above SQL is like the following:
TheYear TheMonth SumOrderAmount
2016 6 625
2016 8 520
How can I query also the SumOrderAmount where the row is Paid='Y'? I want to return the following from the SQL:
TheYear TheMonth SumOrderAmount PaidSumOrderAmount
2016 6 625 500
2016 8 520 400
How can I do that? Any help is greatly appreciated. Thank you.
Use conditional aggregation:
SELECT DATEPART(Year, OrderDate) TheYear, DATEPART(Month, OrderDate) TheMonth,
SUM(ISNULL(OrderAmount, 0)) as SumOrderAmount,
SUM(CASE WHEN Paid = 'Y' THEN OrderAmount ELSE 0 END) as PaidOrderAmount
FROM MyOrderTable
WHERE YEAR(OrderDate) = 2016
GROUP BY DATEPART(Year, OrderDate), DATEPART(Month, OrderDate)
ORDER BY TheYear, TheMonth;
You probably don't need the ISNULL() in SUM(). In SQL, NULL values are ignored in the SUM() aggregation function.
Use a case expression to do a conditional SUM():
SELECT DATEPART(Year, OrderDate) TheYear, DATEPART(Month, OrderDate) TheMonth,
SUM(ISNULL(OrderAmount, 0)) as SumOrderAmount,
SUM(case when Paid='Y' then OrderAmount end)
FROM MyOrderTable
WHERE YEAR(OrderDate) = 2016
GROUP BY DATEPART(Year, OrderDate), DATEPART(Month, OrderDate)
ORDER BY TheYear, TheMonth
Add this clause on select:
SUM(CASE WHEN Paid='Y' THEN OrderAmount ELSE 0 END) AS PaidSumOrderAmount
SELECT DATEPART(Year, OrderDate) TheYear, DATEPART(Month, OrderDate) TheMonth,
SUM(ISNULL(OrderAmount, 0)) as SumOrderAmount,Paid
FROM MyOrderTable
WHERE (YEAR(OrderDate) = 2016) and (Paid = 'Y')
GROUP BY DATEPART(Year, OrderDate), DATEPART(Month, OrderDate)
ORDER BY TheYear, TheMonth

SQL Query Select help, monthly report

I am new to SQL and I need help to make a view from a table in my SQL Server. I have a sales table and need to have a monthly sales report with distinct clientid.
This is what my sales table looks like:
ID Name Amount Date
1 James 300.00 29-09-2015
1 James 250.00 30-09-2015
4 Carl 100.00 30-09-2015
5 Peter 500.50 01-10-2015
5 Peter 300.00 25-10-2015
Desired output:
ID Name Total Month/Year
1 James 550.00 September 2015
4 Carl 100.00 September 2015
5 Peter 800.50 October 2015
You could do something like this:
SELECT
ID,
Name,
SUM(Amount) AS Total,
DATENAME(mm, [Date]) + ' ' + DATENAME(yy, [Date]) AS Month_Year
FROM
<sales table>
GROUP BY
ID,
Name,
DATENAME(mm, [Date]) + ' ' + DATENAME(yy, [Date])
ORDER BY
ID,
Name,
DATENAME(mm, [Date]) + ' ' + DATENAME(yy, [Date])
Aggregating data to produce totals is a very common scenario in SQL, but you should probably start off learning the basics. There are plenty of online resources for this e.g. http://www.w3schools.com/sql/sql_groupby.asp
SELECT
ID,
Name,
SUM(Amount) AS Total,
DATENAME( MONTH , DATEADD( MONTH , DATEPART(MONTH, Date) , -1 )) + ' ' + CAST(DATEPART(YEAR, Date) AS VARCHAR(4)) AS "Month/Year"
FROM SALES_TABLE
GROUP BY
ID, Name, DATEPART(YEAR, Date), DATEPART(MONTH, Date)

tsql join with dateparts in subqueries

I have two queries with subqueries for dateparts which I like to join.
SELECT DateMonth, DateYear, Datestring,
MAX(CouponTotalCount) NoOfCouponsViewed
FROM (
SELECT *, DATEPART(MONTH, DateInsert) DateMonth, DATEPART(YEAR, DateInsert) DateYear,
CONVERT(CHAR(4), DateInsert, 100) + CONVERT(CHAR(4), DateInsert, 120) Datestring
FROM FlurryCouponViewed
) sub
where couponID=249
GROUP BY DateMonth, DateYear, Datestring
SELECT DateMonth, DateYear, Datestring,
MAX(CouponTotalCount) NoOfCouponsRedeemed
FROM (
SELECT *, DATEPART(MONTH, DateInsert) DateMonth, DATEPART(YEAR, DateInsert) DateYear,
CONVERT(CHAR(4), DateInsert, 100) + CONVERT(CHAR(4), DateInsert, 120) Datestring
FROM FlurryCouponRedeemed
) sub
where couponID=249
GROUP BY DateMonth, DateYear, Datestring
The output is of the two queries is:
DateMonth DateYear Datestring NoOfCouponsViewed
----------- ----------- ---------- -----------------
2 2012 Feb 2012 5
3 2012 Mar 2012 12
4 2012 Apr 2012 25
5 2012 May 2012 25
DateMonth DateYear Datestring NoOfCouponsRedeemed
----------- ----------- ---------- -------------------
2 2012 Feb 2012 3
3 2012 Mar 2012 4
4 2012 Apr 2012 5
5 2012 May 2012 11
What I like to achive is two have one joined query giving me:
DateMonth DateYear Datestring NoOfCouponsViewed NoOfCouponsRedeemed
----------- ----------- ---------- ----------------- -------------------
2 2012 Feb 2012 5 3
3 2012 Mar 2012 12 4
4 2012 Apr 2012 25 5
5 2012 May 2012 25 11
How can I do this ?
Make a inner join between the two queries and it should work:
SELECT sub.DateMonth, sub.DateYear, sub.Datestring,
MAX(sub.CouponTotalCount) NoOfCouponsViewed,
MAX(sub2.CouponTotalCount) NoOfCouponsViewed
FROM (
SELECT *, DATEPART(MONTH, DateInsert) DateMonth, DATEPART(YEAR, DateInsert) DateYear,
CONVERT(CHAR(4), DateInsert, 100) + CONVERT(CHAR(4), DateInsert, 120) Datestring
FROM FlurryCouponViewed
) sub
INNER JOIN
( SELECT *, DATEPART(MONTH, DateInsert) DateMonth, DATEPART(YEAR, DateInsert) DateYear,
CONVERT(CHAR(4), DateInsert, 100) + CONVERT(CHAR(4), DateInsert, 120) Datestring
FROM FlurryCouponRedeemed
) sub2 on sub.DateMonth = sub2.DateMonth and sub.DateYear = sub2.DateYear and sub.Datestring = sub2.Datestring
where sub.couponID=249 and sub2.couponID=249
GROUP BY sub.DateMonth, sub.DateYear, sub.Datestring
or a UNION:
SELECT u.DateMonth, u.DateYear, u.Datestring, MAX(u.NoOfCouponsViewed), MAX(u.NoOfCouponsRedeemed)
FROM (
SELECT DateMonth, DateYear, Datestring,
MAX(CouponTotalCount) NoOfCouponsViewed, 0 AS NoOfCouponsRedeemed
FROM (
SELECT *, DATEPART(MONTH, DateInsert) DateMonth, DATEPART(YEAR, DateInsert) DateYear,
CONVERT(CHAR(4), DateInsert, 100) + CONVERT(CHAR(4), DateInsert, 120) Datestring
FROM FlurryCouponViewed
) sub
where couponID=249
GROUP BY DateMonth, DateYear, Datestring
UNION
SELECT DateMonth, DateYear, Datestring, 0 AS NoOfCouponsViewed,
MAX(CouponTotalCount) NoOfCouponsRedeemed
FROM (
SELECT *, DATEPART(MONTH, DateInsert) DateMonth, DATEPART(YEAR, DateInsert) DateYear,
CONVERT(CHAR(4), DateInsert, 100) + CONVERT(CHAR(4), DateInsert, 120) Datestring
FROM FlurryCouponRedeemed
) sub
where couponID=249
GROUP BY DateMonth, DateYear, Datestring
) u
GROUP BY u.DateMonth, u.DateYear, u.Datestring
I would use UNION rather than JOIN
SELECT MonthInserted,
LEFT(DATENAME(MONTH, Datestring), 3) + ' ' + DATENAME(YEAR, MonthInserted) AS DateString
MAX(Viewed) NoOfCouponsViewed,
MAX(Redeemed) NoOfCouponsRedeemed
FROM ( SELECT CouponID,
DATEADD(MONTH, DATEDIFF(MONTH, 0, DateInsert)) [MonthInserted],
CouponTotalCount AS Viewed,
0 AS Redeemed
FROM FlurryCouponViewed
UNION ALL
SELECT CouponID,
DATEADD(MONTH, DATEDIFF(MONTH, 0, DateInsert)),
0,
CouponTotalCount
FROM FlurryCouponRedeemed
) sub
WHERE couponID = 249
GROUP BY MonthInserted
I think a UNION would perform better than the JOIN in the accepted answer as the MAX implies that there are multiple rows per month, and since month is the only common field so it will end up cross joining queries (i.e. if there are 1000 coupons viewed in june 2012 and 500 are redeemed the cross join means you would be selecting the max from 50,000 rows instead of 1500). I am not certain of the schema and logic so this may not be possible, but if there are dates in FlurryCouponRedeemed that are not in FlurryCouponViewed then these will not show.
I also like to keep dates as dates for as long as possible to help the optimiser do it's job, this is why I've replaced DATEPART(YEAR... & DATEPART(MONTH... AND CONVERT(VARCHAR(4), DateInsert... with DATEADD(MONTH, DATEDIFF(MONTH, 0, DateInsert))