SQL select dates from multi rows and datediff total hours - sql

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

Related

Cumulative sum() every 3 days SQL

I have a table like this
date amount
2020-02-01 5
2020-02-02 2
2020-02-03 10
2020-02-04 2
2020-02-06 3
2020-02-07 1
And I need sum() every 3 days as below:
date amount sum
2020-02-01 5 5
2020-02-02 2 7
2020-02-03 10 17
2020-02-04 2 2
2020-02-06 3 5
2020-02-07 1 1
...
So when a difference between days is 3, the summation should start over. Some days may not be in the table.
I tried to do this with window function like sum(amount) over (order by date) but I have no idea how to set a fixed number of days and get the date difference in cumulative sum like this. Is it possible in any SQL?
In MS Sql Server
select t.[date], t.Amount, sum(t.Amount) over(partition by datediff(d, '2020-02-01', t.[date])/3 order by t.[date]) cum
from tbl t
'2020-02-01' is a starting date you want.
Disclaimer
The following solution was written based on a Preview version of SQL Server 2022, and thus may not reflect the final release.
For a bit of fun, if you had access to SQL Server 2022 (which went into preview yesterday) you could use DATE_BUCKET to "round" the date in the PARTITION BY to 3 days, using the minimum date as the starting date.
DECLARE #StartDate date,
#EndDate date;
SELECT #StartDate = MIN(date),
#EndDate = MAX(date)
FROM dbo.YourTable;
SELECT date,
SUM(amount) OVER (PARTITION BY DATE_BUCKET(DAY,3,date,#StartDate) ORDER BY date) AS Amount
FROM dbo.YourTable
WHERE date >= #StartDate
AND date <= #EndDate; --Incase this would be parametrised
Image of results as expected, as Fiddles of 2022 don't exist:

How to subtract next row from first one for each account id in SQL?

The question I am trying to answer is how can I return the correct order and sequence of weeks for each ID? For example, while it is true the first week for each ID will always start at 1 (its the first week in the series), it could be the following date in the series may also be within the first week (e.g., so should return 1 again) or perhaps be a date that falls in the 3rd week (e.g., so should return 3).
The code I've written so far is:
select distinct
row_number() over (partition by ID group by date) row_nums
,ID
,date
from table_a
Which simply returns the running tally of dates by ID, and doesn't take into account what week number that date falls in.
But what I'm looking for is this:
Here's some setup code to assist:
CREATE TABLE random_table
(
ID VarChar(50),
date DATETIME
);
INSERT INTO random_table
VALUES
('AAA',5/14/2021),
('AAA',6/2/2021),
('AAA',7/9/2021),
('BBB', 5/25/2021),
('CCC', 12/2/2020),
('CCC',12/6/2020),
('CCC',12/10/2020),
('CCC',12/14/2020),
('CCC',12/18/2020),
('CCC',12/22/2020),
('CCC',12/26/2020),
('CCC',12/30/2020),
('CCC',1/3/2021),
('DDD',1/7/2021),
('DDD',1/11/2021)
with adj as (
select *, dateadd(day, -1, "date") as adj_dt
from table_a
)
select
datediff(week,
min(adj_dt) over (partition by id),
adj_dt) + 1 as week_logic,
id, "date"
from adj
This assumes that your idea of weeks corresponds with ##datefirst set as Sunday. For a Sunday to Saturday definition you would find 12/06/2020 and 12/10/2020 in the same week, so presumably you want something like a Monday start instead (which also seems to line up with the numbering for 12/02/2020, 12/14/2020 and 12/18/2020.) I'm compensating by sliding backward a day in the weeks calculation. That step could be handled inline without a CTE but perhaps it illustrates the approach more clearly.
Your objective isn't clear but I think you would benefit from a Tally-Table of the weeks and then LEFT JOIN to your source data.
This will give you a row for each week AND source data if it exists
SELECT
CASE WHEN ROW_NUMBER() OVER (PARTITION BY ID ORDER BY [date])=1 THEN 1
ELSE DATEPART(WK, (DATE) ) - DATEPART(WK, FIRST_VALUE([DATE]) OVER (PARTITION BY ID ORDER BY [date])) END PD,
ID,
CONVERT(VARCHAR(10), [date],120)
FROM random_table rt
ORDER BY ID,[date]
DBFIDDLE
output:
PD
ID
(No column name)
1
AAA
2021-05-14
3
AAA
2021-06-02
8
AAA
2021-07-09
1
BBB
2021-05-25
1
CCC
2020-12-02
1
CCC
2020-12-06
1
CCC
2020-12-10
2
CCC
2020-12-14
2
CCC
2020-12-18
3
CCC
2020-12-22
3
CCC
2020-12-26
4
CCC
2020-12-30
-47
CCC
2021-01-03
1
DDD
2021-01-07
1
DDD
2021-01-11
Dates are in the format YYYY-MM-DD.
I will leave the -47 in here, so you can fix it yourself (as an exercise) 😁😉

Query to find rows with nearest date in future

I'm trying to display a result set based on a min date value and today's date but can't seem to make it work. It's essentially a date sensitive price list.
Example Data
ID Title Value ExpireDate
1 Fred 10 2019-03-01
2 Barney 15 2019-03-01
3 Fred2 20 2019-06-01
4 Barney2 25 2019-06-01
5 Fred3 30 2019-07-01
6 Barney3 55 2019-07-01
Required Results:
Display records based on minimum date > GetDate()
3 Fred2 20 2019-06-01
4 Barney2 25 2019-06-01
Any assistance would be great - thank you.
Use where clause to filter all future rows and row_number() to find the first row per group:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Title ORDER BY ExpireDate) AS rn
FROM t
WHERE ExpireDate >= CAST(CURRENT_TIMESTAMP AS DATE)
) AS x
WHERE rn = 1
Based on your revised question, you can simply do this:
SELECT TOP 1 WITH TIES *
FROM t
WHERE ExpireDate >= CAST(CURRENT_TIMESTAMP AS DATE)
ORDER BY ExpireDate

how to calculate sum value from group by

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

Assign a counter in SQL Server to records with sequential dates, and only increment when dates not sequential

I am trying to assign a Trip # to records for Customers with sequential days, and increment the Trip ID if they have a break in sequential days, and come later in the month for example. The data structure looks like this:
CustomerID Date
1 2014-01-01
1 2014-01-02
1 2014-01-04
2 2014-01-01
2 2014-01-05
2 2014-01-06
2 2014-01-08
The desired output based upon the above example dataset would be:
CustomerID Date Trip
1 2014-01-01 1
1 2014-01-02 1
1 2014-01-04 2
2 2014-01-01 1
2 2014-01-05 2
2 2014-01-06 2
2 2014-01-08 3
So if the Dates for that Customer are back-to-back, it is considered the same Trip, and has the same Trip #. Is there a way to do this in SQL Server? I am using MSSQL 2012.
My initial thoughts are to use the LAG, ROW_NUMBER, or OVER/PARTITION BY function, or even a Recursive Table Variable Function. I can paste some code, but in all honesty, my code isn't working so far. If this is a simple query, but I am just not thinking about it correctly, that would be great.
Thank you in advance.
Since Date is a DATE (ie has no hours), you could for example use DENSE_RANK() by Date - ROW_NUMBER() days which will give a constant value for continuous days, something like;
WITH cte AS (
SELECT CustomerID, Date,
DATEADD(DAY,
-ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY Date),
Date) dt
FROM trips
)
SELECT CustomerID, Date,
DENSE_RANK() OVER (PARTITION BY CustomerID ORDER BY dt)
FROM cte;
An SQLfiddle to test with.