How to sum and group in one column - sql-server

I created an example just for this question so don't look at the table itself.
I have this table (see image) which is filled automatically therefore I cannot change the columns.
I am trying to summarize the table with a query and save it in an another table. I need to group by the DepartmentName, Date and Turn and also create a "list" which shows me the sum of Nationalities depending on the Turn (For example: 5 EN, 5 US, 5 ES, 5 FR).
This should be the output (where Quantity is the total amount based on Turn and OtherNationalities is the list of nationalities based on Turn):
DepartmentName
Date
Turn
Quantity
OtherNationalities
Marketing
2022-11-17
Afternoon
30
4 ES, 6 FR
Marketing
2022-11-17
Morning
30
4 ES, 6 FR
Sales
2022-11-17
Afternoon
20
5 EN, 5 US
Sales
2022-11-17
Morning
20
5 EN, 5 US
Sales
2022-11-17
Night
20
5 EN, 5 US
Sales
2022-11-18
Afternoon
20
5 EN, 5 US
Sales
2022-11-18
Morning
20
5 EN, 5 US
Sales
2022-11-18
Night
20
5 EN, 5 US
This is the code that I have done so far:
SELECT DepartmentName, Date, Turn,
SUM(CASE
WHEN Turn = 'Morning' THEN MorningQuantity
WHEN Turn = 'Afternoon' THEN AfternoonQuantity
WHEN Turn = 'Night' THEN NightQuantity
END) AS Quantity,
CASE
WHEN Turn = 'Morning' THEN STRING_AGG(CAST(MorningQuantity AS VARCHAR(3)) + ' ' + Nationality, ', ')
WHEN Turn = 'Afternoon' THEN STRING_AGG(CAST(AfternoonQuantity AS VARCHAR(3)) + ' ' + Nationality, ', ')
WHEN Turn = 'Night' THEN STRING_AGG(CAST(NightQuantity AS VARCHAR(3)) + ' ' + Nationality, ', ')
END AS OtherNationalities
FROM DummyTable
GROUP BY DepartmentName, Date, Turn
And this is the output: current output.
It almost work but it can happen that the data comes with some duplicates (don't ask me why because I don't know...) but they have to be added to the Quantity and OtherNationalities columns.
I have no clue how to group and sum that column (OtherNationalities) in order to get "4 ES, 6 FR". I appreciate any kind of advice and help!

A self join would be one idea:
SELECT DepartmentName, Date, Turn,
SUM(CASE
WHEN Turn = 'Morning' THEN MorningQuantity
WHEN Turn = 'Afternoon' THEN AfternoonQuantity
WHEN Turn = 'Night' THEN NightQuantity
END) AS Quantity,
q2.OtherNationalities
FROM
DummyTable d1
cross appy
(
select OtherNationalities = string_agg(q1.Nationality,',')
from
(
select distinct Nationality
from DummyTable d2
where d2.DepartmentName=d1.DepartmentName and d2.Date=d1.Date and d2.Turn=d1.Turn
)q1
)q2
GROUP BY DepartmentName, Date, Turn, q2.OtherNationalities

Related

Cumulative sum for a range of weeks where some weeks have no value

I am using this query to get rolling 5 week sum
select reportdate,
department,
count(orders) as count,
sum(count(orders)) over (
partition by department order by reportdate rows between 4 preceding and current row
) as 5 weeksum
from orders
sum(count(orders) works fine when there is value for every department every reportdate.
however if there are no orders for certain reportdates then the sum(count(orders)) does not work.
For eg.
reportdate 1-Jan Dept A count(Orders) is 10
7-Jan dept A 'NO ORDERS' which means blank column
14-Jan dept A count(Orders) is 12
21-Jan dept A count(Orders) is 14
28-jan dept A count(Orders) is 25
31-jan dept A count(Orders) is 5
result for sum(count(orders))
1-Jan 10
7-Jan BLANK
while I expect 7-Jan to show 10
Any suggestions as how that can be achieved?

SQL Server - monthly avg of count

I want to be able to find out the monthly average of a count
My code at the moment is
SELECT
company,
COUNT(company) AS 'count'
FROM Information
GROUP BY company
I basically need it to be
SELECT company,
count(company) as 'count'
avg(count(company)) per month as 'average'
FROM Information
group by company
I want the result to look something like this
company count monthly average
a 5 6
b 13 14
c 2 2
d 45 45
e 23 21
f 6 5
A very simple approach would be to count per company and month first and then aggregate this data to get total and avarage per company.
select
company,
sum(cnt) as records,
avg(cnt) as records_per_month
from
(
select company, year(start_date), month(start_date), count(*) as cnt
from information
group by company, year(start_date), month(start_date)
) agg
group by company;
But read my comment to your question.
SELECT YEAR(yourDate) * 100 + MONTH(yourDate) YYMM,
company,
count(company) as 'count'
avg(count(company)) per month as 'average'
FROM Information
group by company
,YEAR(yourDate) * 100 + MONTH(yourDate)

Count of person with Age Range to display in Bar Chart

I have a table with thousands of records and sample is given below.
There is only [DateOfBirth] field in the table and I need to calculate age range with total count :
ID Name DateOfBirth
1 John 1980-11-20 00:00:00.000
2 Denial 1940-04-10 00:00:00.000
3 Binney 1995-12-25 00:00:00.000
4 Sara 1960-11-20 00:00:00.000
5 Poma 1980-11-20 00:00:00.000
6 Cameroon 1980-11-20 00:00:00.000
.....
.....
I need to write the SQL Query to display output 10 years age range to fit it in the bar chart.
AgeRange Count
1-10 100
11-20 200
21-30 400
31-40 0
41-50 800
51-60 700
61-70 200
.....
.....
and so on if the age person is present in the table.
Please try the below query
select
cast( 1 + DateDiff(YY,DATEADD(YY,-1,DateOfBirth),getdate())/10 *10 as varchar)+ '-'+ cast(10+ DateDiff(YY,DATEADD(YY,-1,DateOfBirth),getdate())/10 *10 as varchar) as AgeRange
,
count(1) as count
from tbl
group by
DateDiff(YY,DATEADD(YY,-1,DateOfBirth),getdate())/10
Demo link here
update: based on your custom range, you should use this query
select
cast(case when
DateDiff(YY,DATEADD(YY,-1,DateOfBirth),getdate())/10 =0
then 0
else
1 end+ DateDiff(YY,DATEADD(YY,-1,DateOfBirth),getdate())/10 *10 as varchar)+ '-'+ cast(10+ DateDiff(YY,DATEADD(YY,-1,DateOfBirth),getdate())/10 *10 as varchar) as AgeRange
,
count(1) as count
from tbl
group by
DateDiff(YY,DATEADD(YY,-1,DateOfBirth),getdate())/10
this seems to work for me
SELECT count(*) as [count]
, cast(datediff(YYYY, value, getdate())/10 * 10 as varchar(10)) + ' - ' +
cast(datediff(YYYY, value, getdate())/10 * 10 + 9 as varchar(10)) as range
FROM [docSVdate]
group by datediff(YYYY, value, getdate())/10
order by datediff(YYYY, value, getdate())/10 desc
this start with 0 then just adds 9
you need to be consistent as 0 - 10 is a range of 11
if you need to start on 1 then I think you need also change the group by

SQL query to fetch all results in one query

Test data:
id date company location
----------------------------------
1 03/01/2016 ABC india
2 03/25/2016 ABC us
3 02/24/2016 ABC india
4 02/25/2016 ABC us
5 03/02/2016 ABC india
Query #1
select count(id)
from table
where company = 'ABC'
and date between 03/01/2016 and 03/31/2016
Query #2
select count(id)
from table
where company = 'ABC'
and date between 02/01/2016 and 02/29/2016
Need to calculate location wise count for current and previous months.
How to write a SQL query to return location wise like the below in one query?
Expected result:
company currentmonth Previousmonth location
ABC 2 1 india
ABC 1 1 us
Try:
select company,
sum(case when month(date)=month(getdate())
then 1 else 0 end) as currentMonth,
sum(case when month(date)=month(dateadd(MONTH,-1,getdate()))
then 1 else 0 end) as Previuosmonth,
location
from yourTable
where month(date) between month(dateadd(MONTH,-1,getdate())) and month(getdate())
AND company='ABC'
group by company, location
Since you made two queries and filter both with current month (march) and previous. I decided to use the function MONTH to extract the month from the current date (getdate()) of the system and subtract 1 that way you will always have the current month and the previous one.
EDIT
As pointed out by #DhananjayaKuppu I fixed the problem for when you have a month as January, since the month function returns an integer it would return 0 in my calculation. Now it is fixed.
You may try some thing like subtract current date yearmm format (ex: 201603) and yearmm format of date from table (ex:201603) if it result to 0 treat as current month, else previous month, hope it will help:
SELECT company,
location,
sum(iif(d = 0, 0, 1)) cm,
sum(iif(d = 0, 1, 0)) pm
FROM (SELECT company,
location,
CONVERT(INT,
CONVERT(CHAR(4), GetDate(), 120) +
CONVERT(CHAR(2), GetDate(), 101)
) -
CONVERT(INT,
CONVERT(CHAR(4), date, 120) +
CONVERT(CHAR(2), date, 101)
) as d
FROM table) vw
GROUP BY company, location

SQL Server: How to get a rolling sum over 3 days for different customers within same table

This is the input table:
Customer_ID Date Amount
1 4/11/2014 20
1 4/13/2014 10
1 4/14/2014 30
1 4/18/2014 25
2 5/15/2014 15
2 6/21/2014 25
2 6/22/2014 35
2 6/23/2014 10
There is information pertaining to multiple customers and I want to get a rolling sum across a 3 day window for each customer.
The solution should be as below:
Customer_ID Date Amount Rolling_3_Day_Sum
1 4/11/2014 20 20
1 4/13/2014 10 30
1 4/14/2014 30 40
1 4/18/2014 25 25
2 5/15/2014 15 15
2 6/21/2014 25 25
2 6/22/2014 35 60
2 6/23/2014 10 70
The biggest issue is that I don't have transactions for each day because of which the partition by row number doesn't work.
The closest example I found on SO was:
SQL Query for 7 Day Rolling Average in SQL Server
but even in that case there were transactions made everyday which accomodated the rownumber() based solutions
The rownumber query is as follows:
select customer_id, Date, Amount,
Rolling_3_day_sum = CASE WHEN ROW_NUMBER() OVER (partition by customer_id ORDER BY Date) > 2
THEN SUM(Amount) OVER (partition by customer_id ORDER BY Date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
END
from #tmp_taml9
order by customer_id
I was wondering if there is way to replace "BETWEEN 2 PRECEDING AND CURRENT ROW" by "BETWEEN [DATE - 2] and [DATE]"
One option would be to use a calendar table (or something similar) to get the complete range of dates and left join your table with that and use the row_number based solution.
Another option that might work (not sure about performance) would be to use an apply query like this:
select customer_id, Date, Amount, coalesce(Rolling_3_day_sum, Amount) Rolling_3_day_sum
from #tmp_taml9 t1
cross apply (
select sum(amount) Rolling_3_day_sum
from #tmp_taml9
where Customer_ID = t1.Customer_ID
and datediff(day, date, t1.date) <= 3
and t1.Date >= date
) o
order by customer_id;
I suspect performance might not be great though.

Resources