how to calculate sum value from group by - sql

I have a table like
person type date
Tom day 4/1/2018
Tom night 3/2/2018
Tom rest 4/3/2018
Jack day 4/1/2018
Jack day 4/2/2018
Jack night 4/3/2018
Peter day 4/1/2018
Peter day 3/2/2018
Peter day 4/3/2018
I want to count each one's working hours in April. Day shift is 8 hours and night is 11 hours. so the result is like
Person hours
Tom 8
Jack 27
Peter 16
I tried a SQL like
select person,count(Type),
case type when 'day' then count(type)*8 when 'night' then count(type)*11
from table where date>'3/30/2018'
group by person,type
and it work
then I try to treat it like a table and add a group by outside like
select *
from (select person,count(Type),
case type when 'day' then count(type)*8 when 'night' then count(type)*11
from table where date>'3/30/2018' group by person,type)
and it doesn't work. Why? any help apppreciated.

You could use the following case-when to convert the type to the corrisponding number of hours:
case
when type='day' then 8
when type='night' then 11
end
then you can just sum the corresponding number of hours:
select
person,
sum(
case
when type='day' then 8
when type='night' then 11
end
) as hours
from
table_name
where
date>='4/01/2018' and date<'5/01/2018'
group by
person

try this:
SELECT person,
SUM(Case WHEN type='day' THEN 8
WHEN type='night' THEN 11
ELSE 0 END)
FROM Table1
WHERE [date]>'2018/03/30'
Group by person
SQL Fiddle: http://sqlfiddle.com/#!18/5b946/1

The key to get your result is to use case statement to "convert" type to appropriate amount of hours. Try this:
select person, SUM([hrs]), [date] from (
select person,
case [TYPE_ID] when 'day' then 8
when 'night' then 11
else 0 end [hrs],
[date]
from MY_TABLE
where date between '4/01/2018' and '5/01/2018'
) [a] group by person

Related

How to average a score in groups based on the month and year in SQL

I have a table called reviews. Reviews has two columns I care about: score (int) and created_at (datetime).
Imagine the following data
id
score
created_at
1
5
2022-01-15
2
1
2022-01-15
3
2
2022-02-01
4
3
2022-02-04
5
5
2022-03-30
I want to return the average score grouped by the month and the year. To get the following result
date
average_score
Jan 2022
3
Feb 2022
2.5
Mar 2022
5
My current code is below. It is almost working but it isn't grouping by the date and I can't figure out why. Please let me know if I am way off here but I think I am so close I just need the data to group
SELECT TO_CHAR(reviews.created_at, 'Mon YYYY') AS date,
AVG(reviews.score) OVER (PARTITION BY TO_CHAR(reviews.created_at, 'Mon YYYY')) AS average_score
FROM "reviews"
WHERE "reviews"."score" IS NOT NULL
GROUP BY date, score
You don't need the window function:
use the corresponding AVG aggregation function
remove the score from the GROUP BY clause
remove the null scores check, as the aggregation functions skip null values
SELECT TO_CHAR(created_at, 'Mon YYYY') AS date,
AVG(score) AS average_score
FROM "reviews"
GROUP BY date
Check the demo here.
Note: in your code the GROUP BY clause is doing nothing as long as there's no aggregation function applied to any field called in your SELECT clause.

SQL select dates from multi rows and datediff total hours

Hi have records entered into a table, I want to get the hours worked between rows.
id memberid dayname datesigned orderinout
310 987654321 Friday 2021-08-13 09:22:42 1
311 987654321 Friday 2021-08-13 10:15:50 2
312 987654321 Friday 2021-08-13 10:20:00 3
313 987654321 Friday 2021-08-13 12:36:15 4
314 987654321 Friday 2021-08-13 13:01:55 5
315 987654321 Friday 2021-08-13 18:55:41 6
Ideally I would like to work select a member and get the date signed, easy. then do a datediff to work out the hh:mm:ss difference. all good with 2 dates but multi on the same day? little stuck.
SELECT TIMEDIFF(MAX(datesigned),MIN(datesigned)) AS HoursIn
WHERE memberid = '987654321'
AND dayname = 'Friday'
when the date is saved, it will assign a number, first record will be 1 and so on for the member and the date.
so need to get the results for 1+2 then 3+4, 5+6 so on. might even be an odd one.
Any suggestions as im totally lost.
Use the LAG function to achieve the next record. Arrange the columns using orderinout and access the next row with the LAG function. 1 and 2 , 3 and 4 and .............
The TIMEDIFF function exists in mysql, and assuming your database management system is mysql, the following code.
in mysql
SELECT
id,
memberid,
dayname,
datesigned,
orderinout,
TIMEDIFF(datesigned,lag(datesigned,1) over(partition by memberid order by orderinout)) as HoursIn
from t
WHERE memberid = '987654321'
AND dayname = 'Friday'
demo in db<>fiddle
in sql-server
SELECT
id,
memberid,
dayname,
datesigned,
orderinout,
CONVERT (TIME, datesigned - lag(datesigned,1) over(partition by memberid order by orderinout)) as HoursIn
from t
WHERE memberid = '987654321'
AND dayname = 'Friday'
demo in db<>fiddle
If you want to calculate for all members and every day, use the LAG function as follows.
lag(datesigned,1) over(partition by memberid,dayname order by orderinout)
full query
SELECT
id,
memberid,
dayname,
datesigned,
orderinout,
TIMEDIFF(datesigned,lag(datesigned,1) over(partition by memberid,dayname order by orderinout)) as HoursIn
from t

select repeat visitors given two time periods

I would like to find the percent of repeat visitors to my site. Currently I am selecting users for one month and dividing that month by the following month. to calculate those that have returned. Is this the best way to write that query?
it is yielding results that seem to be accurate, but wondering if there is a more elegant solution.
SELECT (
SELECT COUNT(table.user) as Total
FROM table
WHERE table.event IN ('event1','event2','event3')
AND table.month IN ('october')
) /
(
SELECT COUNT(table.user) as newTotal
FROM table
WHERE table.event IN ('event1','event2','event3')
AND (table.month IN ('october') OR table.month IN ('november'))
) AS percent_return
table structure looks like this, you have the same user purchasing multiple events for the same month or the same time period.
order_number user month event gross
1 jack october event2 30
2 jack november event3 20
3 jack november event3 20
4 jack november event2 30
5 sam november event2 30
6 john october event3 20
7 john october non_event 20
You don't need two subqueries to do what you want. Just use conditional aggregation:
SELECT (SUM(s.month IN (october)) / SUM(s.month IN (october, november))
) as percent_return
FROM sales s
WHERE s.event IN ('event1', 'event2', 'event3') AND
s.gross > 0;
Also, there is no need for single quotes around numeric constants.

Count day of week occurances

I have a list of members of a club together with datetime that they attended the club. They can attend the club several times in a single day. I need to know how many Sundays did each member attend over a given period (regardless how many times within a single Sunday). I have a table that lists each attendance, made up of member number and the attendance datetime.
Eg In this example 13/1 and 20/1 are Sundays
MEMBER ATTENDANCE
12345 13/1/13 09:00
12345 13/1/13 15:00
12345 14/1/13 08:00
56789 13/1/13 10:00
56789 13/1/13 15:00
56789 13/1/13 21:00
56789 14/1/13 10:00
56789 20/1/13 09:00
24680 14/1/13 08:00
24680 15/1/13 07:00
Ideally I would like to see this returned:
MEMBER # OF SUNDAYS
12345 1
56789 2
24680 0
I think you need this:
select Member,
count(distinct dateadd(day, datediff(day, 0, Attendance), 0)) as NumberOfSundays
from t
where datepart(dw, Attendance) = 6
group by Member ;
The complicated count is really doing:
count(distinct cast(Attendance as date))
but the date data type is not supported in SQL Server 2005.
EDIT:
Instead of datepart(dw, Attendance) = 6, you can use datename(dw, Attendance) = 'Sunday'.
Try this
SELECT MEMBER,
SUM(CASE DATENAME(dw,ATTENDANCE) WHEN 'Sunday' THEN 1 ELSE 0 END) As [# OF SUNDAYS]
FROM MemberTable
WHERE ATTENDANCE between [YourStartDateAndTime] AND [YourEndDateAndTime] -- replace [YourStartDateAndTime] AND [YourEndDateAndTime] with your value or variable
GROUP BY MEMBER
Take a look into this example, it may work also:
select Member
, count( case when datepart( dw, Attendance ) = 6 then 1 else 0 end ) NumberOfSundays
from YourTable
where Attendance between '2013-01-13' and '2013-01-20'
group by Member
Avoid use functions on your where clause that'll modify the original value of the field. By doing this, SQL Server will create an execution plan using a Table Scan, ignoring any index that would speed up your query!

How to aggregate 7 days in SQL

I was trying to aggregate a 7 days data for FY13 (starts on 10/1/2012 and ends on 9/30/2013) in SQL Server but so far no luck yet. Could someone please take a look. Below is my example data.
DATE BREAD MILK
10/1/12 1 3
10/2/12 2 4
10/3/12 2 3
10/4/12 0 4
10/5/12 4 0
10/6/12 2 1
10/7/12 1 3
10/8/12 2 4
10/9/12 2 3
10/10/12 0 4
10/11/12 4 0
10/12/12 2 1
10/13/12 2 1
So, my desired output would be like:
DATE BREAD MILK
10/1/12 1 3
10/2/12 2 4
10/3/12 2 3
10/4/12 0 4
10/5/12 4 0
10/6/12 2 1
Total 11 15
10/7/12 1 3
10/8/12 2 4
10/9/12 2 3
10/10/12 0 4
10/11/12 4 0
10/12/12 2 1
10/13/12 2 1
Total 13 16
--------through 9/30/2013
Please note, since FY13 starts on 10/1/2012 and ends on 9/30/2012, the first week of FY13 is 6 days instead of 7 days.
I am using SQL server 2008.
You could add a new computed column for the date values to group them by week and sum the other columns, something like this:
SELECT DATEPART(ww, DATEADD(d,-2,[DATE])) AS WEEK_NO,
SUM(Bread) AS Bread_Total, SUM(Milk) as Milk_Total
FROM YOUR_TABLE
GROUP BY DATEPART(ww, DATEADD(d,-2,[DATE]))
Note: I used DATEADD and subtracted 2 days to set the first day of the week to Monday based on your dates. You can modify this if required.
Use option with GROUP BY ROLLUP operator
SELECT CASE WHEN DATE IS NULL THEN 'Total' ELSE CONVERT(nvarchar(10), DATE, 101) END AS DATE,
SUM(BREAD) AS BREAD, SUM(MILK) AS MILK
FROM dbo.test54
GROUP BY ROLLUP(DATE),(DATENAME(week, DATE))
Demo on SQLFiddle
Result:
DATE BREAD MILK
10/01/2012 1 3
10/02/2012 2 4
10/03/2012 2 3
10/04/2012 0 4
10/05/2012 4 0
10/06/2012 2 1
Total 11 15
10/07/2012 1 3
10/08/2012 4 7
10/10/2012 0 4
10/11/2012 4 0
10/12/2012 2 1
10/13/2012 2 1
Total 13 16
You are looking for a rollup. In this case, you will need at least one more column to group by to do your rollup on, the easiest way to do that is to add a computed column that groups them into weeks by date.
Take a lookg at: Summarizing Data Using ROLLUP
Here is the general idea of how it could be done:
You need a derived column for each row to determine which fiscal week that record belongs to. In general you could subtract that record's date from 10/1, get the number of days that have elapsed, divide by 7, and floor the result.
Then you can GROUP BY that derived column and use the SUM aggregate function.
The biggest wrinkle is that 6 day week you start with. You may have to add some logic to make sure that the weeks start on Sunday or whatever day you use but this should get you started.
The WITH ROLLUP suggestions above can help; you'll need to save the data and transform it as you need.
The biggest thing you'll need to be able to do is identify your weeks properly. If you don't have those loaded into tables already so you can identify them, you can build them on the fly. Here's one way to do that:
CREATE TABLE #fy (fyear int, fstart datetime, fend datetime);
CREATE TABLE #fylist(fyyear int, fydate DATETIME, fyweek int);
INSERT INTO #fy
SELECT 2012, '2011-10-01', '2012-09-30'
UNION ALL
SELECT 2013, '2012-10-01', '2013-09-30';
INSERT INTO #fylist
( fyyear, fydate )
SELECT fyear, DATEADD(DAY, Number, DATEADD(DAY, -1, fy.fstart)) AS fydate
FROM Common.NUMBERS
CROSS APPLY (SELECT * FROM #fy WHERE fyear = 2013) fy
WHERE fy.fend >= DATEADD(DAY, Number, DATEADD(DAY, -1, fy.fstart));
WITH weekcalc AS
(
SELECT DISTINCT DATEPART(YEAR, fydate) yr, DATEPART(week, fydate) dt
FROM #fylist
),
ridcalc AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY yr, dt) AS rid, yr, dt
FROM weekcalc
)
UPDATE #fylist
SET fyweek = rid
FROM #fylist
JOIN ridcalc
ON DATEPART(YEAR, fydate) = yr
AND DATEPART(week, fydate) = dt;
SELECT list.fyyear, list.fyweek, p.[date], COUNT(bread) AS Bread, COUNT(Milk) AS Milk
FROM products p
JOIN #fylist list
ON p.[date] = list.fydate
GROUP BY list.fyyear, list.fyweek, p.[date] WITH ROLLUP;
The Common.Numbers reference above is a simple numbers table that I use for this sort of thing (goes from 1 to 1M). You could also build that on the fly as needed.