TSQL- adding value of every row to the next - sql

I have 2 columns and I want to add the value(hour) of every row to the next row.
Date Hour
2014-01-13 13:00 0
2014-01-13 14:00 3
2014-01-13 16:00 2
and I want to have a new date column that shows like this:
Date Hour **New_Date**
2014-01-13 13:00 0 2014-01-13 16:00
2014-01-13 14:00 3 2014-01-13 17:00
2014-01-13 16:00 2 2014-01-13 18:00

You can use a subquery with DATEADD:
SELECT [Date], [Hour],
DATEADD(hour, (SELECT TOP 1 t2.[Hour]
FROM dbo.Tablename t2
WHERE t2.[Date] > t1.[Date]
ORDER BY t2.[Date]), [Date])
AS [New_Date]
FROM dbo.TableName t1
ORDER BY [Date]
But as you can see in this sql-fiddle the seond row adds two hours since that is the third row's hour. Also, the last row gets no new-date because there is no next-row. Is your desired result wrong?

Try this..
CREATE TABLE #temp
(
[Date] DATETIME,
[Hour] INT
)
INSERT INTO #temp
([Date],[Hour])
VALUES ('2014-01-13 13:00:00',0),
('2014-01-13 14:00:00',3),
('2014-01-13 16:00:00',2)
;WITH cte
AS (SELECT Row_number()
OVER(
ORDER BY [date]) AS id,
*
FROM #temp)
SELECT a.Date,
a.Hour,
Dateadd(hh, a.Hour, CASE
WHEN a.id != 1 THEN a.[date]
ELSE (SELECT TOP 1 date
FROM cte
ORDER BY id DESC)
END) newdate
FROM cte a
LEFT JOIN cte b
ON a.id = b.id + 1
output
Date Hour newdate
2014-01-13 13:00:00.000 0 2014-01-13 16:00:00.000
2014-01-13 14:00:00.000 3 2014-01-13 17:00:00.000
2014-01-13 16:00:00.000 2 2014-01-13 18:00:00.000

Related

SQL calculate the day difference between current row and next row based on GroupId

Original table is as below, the table has been ordered by GroupId and Date in ascending order. I'd like to calculate the day difference between current and next row for the same GroupId:
GroupId
Date
1
2022-02-20 00:00:00.000
1
2022-02-27 00:00:00.000
1
2022-03-02 00:00:00.000
2
2022-02-03 00:00:00.000
2
2022-02-17 00:00:00.000
The target output should be like this:
GroupId
Date
Previous_Date
Day_Difference
1
2022-02-20 00:00:00.000
null
null
1
2022-02-27 00:00:00.000
2022-02-20 00:00:00.000
7
1
2022-03-02 00:00:00.000
2022-02-27 00:00:00.000
3
2
2022-02-03 00:00:00.000
null
null
2
2022-02-17 00:00:00.000
2022-02-03 00:00:00.000
14
I got the script as below, but it's getting the Previous_Date from the last row and does the calculation, but I would like to keep the Previous_Date and Day_Difference as NULL for the first row as the target table above.
Can someone please help?
My script is:
SELECT
GroupId,
[Date],
LAG([Date]) OVER (ORDER BY [Date]) AS Previous_Date,
DATEDIFF(DAY, LAG([Date]) OVER (ORDER BY [Date]), [Date]) AS Day_Difference
FROM
TestTable
ORDER BY
GroupId
You can try to use PARTITION BY GroupId in OVER clause. PARTITION BY that divides the query result set into partitions.
SELECT
GroupId,
[Date],
lag([Date]) OVER (PARTITION BY GroupId ORDER BY [Date]) as Previous_Date,
DATEDIFF(day, lag([Date]) OVER (PARTITION BY GroupId ORDER BY [Date]), [Date]) AS Day_Difference
FROM TestTable
order by GroupId
sqlfiddle

split the date range by every 6 months dynamically. Start date can be any thing, but add up to month range

Please help to split the date range by every 6 moths and the start date could be anything but using the start date we need to add up to 09-30 only and the next day which is 10/01 should become start date. I tried using recursive cte but still not getting the exact result
startdate enddate
06-22-2018 09-30-2022
output
startdate enddate
06-22-2018 09-30-2018
10-01-2018 03-31-2019
04-01-2019 09-30-2019
10-01-2019 03-31-2020
04-01-2020 09-30-2020
Here is another option which uses an ad-hoc tally table
Example
Declare #YourTable table (startdate date, enddate date)
Insert Into #YourTable values
('06/22/2018','09/30/2022')
;with cte as (
Select *
,Grp = sum( case when day(D)=1 and month(D) in (4,10) then 1 else 0 end) over (order by d)
From #YourTable A
Cross Apply (
Select Top (DateDiff(DAY,startdate,enddate)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),startdate)
From master..spt_values n1,master..spt_values n2
) B
)
Select StartDate = min(D)
,EndDate = max(D)
From cte
Group by Grp
Order By min(D)
Returns
StartDate EndDate
2018-06-22 2018-09-30
2018-10-01 2019-03-31
2019-04-01 2019-09-30
2019-10-01 2020-03-31
2020-04-01 2020-09-30
2020-10-01 2021-03-31
2021-04-01 2021-09-30
2021-10-01 2022-03-31
2022-04-01 2022-09-30
Option where we JOIN to an ad-hoc calendar table (note the TOP 10000 and base date of 2000-01-01)
Declare #YourTable table (id int,startdate date, enddate date)
Insert Into #YourTable values
(1,'06/22/2018','09/30/2022')
;with cte as (
Select A.*
,B.D
,Grp = sum( case when day(D)=1 and month(D) in (4,10) then 1 else 0 end) over (order by d)
From #YourTable A
Join (
Select Top 10000 D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),'2000-01-01')
From master..spt_values n1,master..spt_values n2
) B on D between startDate and EndDate
and (D in (startdate,EndDate)
or ( day(D) in (1,day(eomonth(d))) and month(D) in (3,4,9,10))
)
)
Select ID
,StartDate = min(D)
,EndDate = max(D)
From cte
Group by ID,Grp
Order By ID,min(D)
Returns
ID StartDate EndDate
1 2018-06-22 2018-09-30
1 2018-10-01 2019-03-31
1 2019-04-01 2019-09-30
1 2019-10-01 2020-03-31
1 2020-04-01 2020-09-30
1 2020-10-01 2021-03-31
1 2021-04-01 2021-09-30
1 2021-10-01 2022-03-31
1 2022-04-01 2022-09-30
You can use a recursive CTE:
with cte as (
select startdate, eomonth(datefromparts(year(startdate), 9, 1)) as enddate, enddate as orig_enddate
from t
union all
select dateadd(day, 1, enddate), eomonth(dateadd(month, 5, dateadd(day, 1, enddate))) as enddate, orig_enddate
from cte
where enddate < orig_enddate
)
select *
from cte;
Here is a db<>fiddle.
It is unclear what year you want for the first row. As per your question, this uses Sep 30th of the year of the startdate.
If you need more than 100 dates, then add option max(recursion 0).

How to filter a column based on values of another column in SQL

I need to build a new column in my existing table.
So there are two columns,
'cmg' and 'effdate' and I need to create a new column called 'enddate'.
So for a particular cmg,
the max of eff_date = current date
and the dates in next row should be equal to the date in the previous row - 1.
For example,
cmg | effdate
1 23/10/2018
1 1/10/2018
1 1/1/2018
3 1/12/2018
3 1/3/2018
I need to 1st filter the rows based on the values in cmg column and then get enddate as below:
cmg | effdate | enddate
1 23/10/2018 currentdate
1 1/10/2018 22/10/2018
1 1/1/2018 30/9/2018
3 1/12/2018 currentdate
3 1/3/2018 30/11/2018
Use LEAD and OVER
SELECT cmg, effdate, LEAD(DATEADD(DAY, -1, effdate),1, getdate()) OVER (PARTITION BY cmg ORDER BY effdate ASC) as enddate
FROM testcmg
ORDER BY cmg, effdate DESC
The following query should do what you want:
CREATE TABLE #temp (cmg INT, effdate DATE)
INSERT INTO #temp VALUES
(1,'2018-10-23'),
(1,'2018-10-01'),
(1,'2018-01-01'),
(3,'2019-03-01'),
(3,'2018-12-01')
SELECT cmg, effdate, LAG(DATEADD(DAY,-1,effdate),1, GETDATE()) OVER (PARTITION BY cmg ORDER BY effdate DESC) as enddate
FROM #temp
ORDER BY cmg, effdate DESC
The result is as below,
cmg effdate enddate
1 2018-10-23 2019-06-11
1 2018-10-01 2018-10-22
1 2018-01-01 2018-09-30
3 2019-03-01 2019-06-11
3 2018-12-01 2019-02-28

Append data to split rows

I want to know how many people weren't available in months historically, for that I have an historicTable which contains data from 2012 to 2018 and each row contains how much time an employee wasn't available (vacations, sickness, etc.) this is one example:
idUser startDate endDate daysUn reason nameEmp
--------------------------------------------------------
123 25/01/2018 09/02/2018 12 Sickness John Doe
This is what I need for every row
idUser startDate endDate daysUn reason nameEmp
--------------------------------------------------------
123 25/01/2018 31/01/2018 5 Sickness John Doe
123 01/01/2018 09/02/2018 7 Sickness John Doe
I know this been asked hundred of times here but I'm having trouble doing this for an entire table, for what I've tried in different answers all process work for specific given startdate and enddate columns, and what I need it's to append ALL data to this table and save it as-is so the analyst will be able to study specific cases and specific employees. This is what I get with my current code:
original_INI original_FIN new_INI new_FIN
----------------------- ----------------------- ----------------------- -----------------------
2017-10-15 00:00:00.000 2018-01-06 00:00:00.000 2017-10-15 00:00:00.000 2017-10-31 00:00:00.000
2017-10-15 00:00:00.000 2018-01-06 00:00:00.000 2017-11-01 00:00:00.000 2017-11-30 00:00:00.000
2017-10-15 00:00:00.000 2018-01-06 00:00:00.000 2017-12-01 00:00:00.000 2017-12-31 00:00:00.000
2017-10-15 00:00:00.000 2018-01-06 00:00:00.000 2018-01-01 00:00:00.000 2018-01-06 00:00:00.000
This is the code, original dates are ok as I can sort data more globally but it could print and save the rest of the data so it's more readable:
;WITH n(n) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY [object_id])-1 FROM sys.all_columns
),
d(n,f,t,md,bp,ep) AS
(
SELECT n.n, d.INI, d.FIN,
DATEDIFF(MONTH, d.INI, d.FIN),
DATEADD(MONTH, n.n, DATEADD(DAY, 1-DAY(INI), INI)),
DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(MONTH, n.n,
DATEADD(DAY, 1-DAY(INI), INI))))
FROM n INNER JOIN archivoFuente AS d
ON d.FIN >= DATEADD(MONTH, n.n-1, d.INI)
)
SELECT original_INI = f, original_FIN = t,
new_INI = CASE n WHEN 0 THEN f ELSE bp END,
new_FIN = CASE n WHEN md THEN t ELSE ep END
FROM d WHERE md >= n
ORDER BY original_INI, new_INI;
Any help with the query it's appreciated.
It's pretty easy actually, I used the same code for my requirements, you need to call each column in each select statement so it exist when you split the rows, check this code:
;WITH n(n) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY [object_id])-1 FROM sys.all_columns
),
d(n,f,t,md,bp,ep,
--CALL YOUR COLUMNS HERE EG: name, id, bla, ble
) AS
(
SELECT n.n,d.INI, d.FIN,
DATEDIFF(MONTH, d.INI, d.FIN),
DATEADD(MONTH, n.n, DATEADD(DAY, 1-DAY(INI), INI)),
DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(MONTH, n.n,
DATEADD(DAY, 1-DAY(INI), INI)))),
--CALL YOUR COLUMNS HERE AGAIN, PAY ATTENTION TO NAMES AND COMMAS
d.id_hr,d.Tipo,d.ID_tip,d.Nom_inc,d.RUT,d.Nombre,d.ID_emp,d.Nom_pos,d.Dias_durac,d.Num_lic,d.ID_usu_ap,d.ult_act
FROM n INNER JOIN archivoFuente AS d
ON d.FIN >= DATEADD(MONTH, n.n-1, d.INI)
)
SELECT --PUT ONCE AGAIN YOUR COLUMNS HERE, THIS WILL WORK FOR THE DISPLAYED RESULT
original_INI = f, original_FIN = t,
new_INI = CASE n WHEN 0 THEN f ELSE bp END,
new_FIN = CASE n WHEN md THEN t ELSE ep END
FROM d
WHERE md >= n
ORDER BY original_INI, new_INI;
Now, to save the table, I'd recommend using an INSERT statement to a new table, how will you do it, I don't know, I'am in the same spot as you. Hope someone check this question.

Sql Pivot on time

Table 1: Daily attendance data:
att_date emp_code emp_name in_time out_time
2018-10-21 9999 Test 2018-10-21 08:00:00.000 2018-10-22 06:00:00.000
Table 2: Trnevents
emp_readr_id DT EVENTID
9999 2018-10-24 07:00:00.000 0
9999 2018-10-24 05:00:00.000 0
9999 2018-10-24 03:00:00.000 0
9999 2018-10-23 21:00:00.000 0
9999 2018-10-23 19:00:00.000 0
9999 2018-10-23 06:00:00.000 0
9999 2018-10-22 06:00:00.000 0
9999 2018-10-21 08:00:00.000 0
I used this query to get all times in between in time and out time ,below query works fine but i try to make in row by using pivot. While using pivot out time shows in next row.
declare #tempProcesstable as table(
[id] [nvarchar](200) NULL,
[time_stamp] datetime NULL,
[AccessType] varchar(3) NULL)
insert into #tempProcesstable
select distinct t1.emp_Reader_id, t1.DT,t1.eventid from daily_attendance_data t2 join trnevents t1
on t1.emp_reader_id=t2.emp_reader_id where (CONVERT(VARCHAR(26), t2.att_Date, 23) >=CONVERT(VARCHAR(26), '2018-10-20', 23)
and CONVERT(VARCHAR(26), t2.att_date, 23) <=CONVERT(VARCHAR(26), '2018-10-21', 23))
and
(t1.DT >=t2.in_time
and t1.DT <=t2.out_time)
-- and t1.emp_reader_id=1000
group by t1.emp_Reader_id,t1.dt,t1.eventid order by t1.emp_reader_id,DT asc
; With CheckIns
As (Select Rowemp_reader_id = Row_Number() Over (Partition by id, Cast(time_stamp As Date) Order By time_stamp),
id, time_stamp,
[Date] = Cast(time_stamp As Date),
[Time] = Cast(time_stamp As Time(0))
From #tempProcesstable)
Select Pvt.id,B.emp_name , [Date], CHECK1, CHECK2,Cast(dateadd(ss,datediff(ss,CHECK1,CHECK2),0) As Time(0)) Total1,
CHECK3, CHECK4,Cast(dateadd(ss,datediff(ss,CHECK3,CHECK4),0) As Time(0)) Total2
From (Select id, [Date], [Time],
CHECKNum = 'CHECK' + Cast(Rowemp_reader_id As varchar(11))
From CheckIns) As P
Pivot (Min([Time])
For CheckNum In (Check1, [Check2], Check3, Check4)
) As Pvt
LEFT OUTER JOIN
dbo.employee AS B ON Pvt.id= B.emp_reader_id
My output:
id emp_name Date CHECK1 CHECK2 Total1 CHECK3 CHECK4 Total2
1048 Singh 2018-10-21 07:06:07 17:34:05 10:27:58 NULL NULL NULL
9999 Test 2018-10-21 08:00:00 NULL NULL NULL NULL NULL NULL
9999 Test 2018-10-22 06:00:00 NULL NULL NULL NULL NULL NULL
Expected output:
I want all times between in time and out time in night to morning also.
can any one help me to rectify this.
id emp_name Date CHECK1 CHECK2 Total1 CHECK3 CHECK4 Total2
1048 Singh 2018-10-21 07:06:07 17:34:05 10:27:58 NULL NULL NULL
9999 Test 2018-10-21 08:00:00 06:00:00 NULL NULL NULL NULL NULL
You can try to use ROW_NUMBER() window function make row number by each date.
then use condition aggregate function to do pivot
SELECT emp_readr_id,
emp_name,
[Date],
MAX(CASE WHEN RN = 1 THEN time END) CHECK1,
MAX(CASE WHEN RN = 2 THEN time END) CHECK2,
MAX(CASE WHEN RN = 3 THEN time END) CHECK3,
MAX(CASE WHEN RN = 4 THEN time END) CHECK4
FROM (
SELECT emp_readr_id,
emp_name,
CONVERT(VARCHAR(10),DT,120) 'Date',
ROW_NUMBER() OVER(PARTITION BY CONVERT(VARCHAR(10),DT,120) ORDER BY DT) rn,
CONVERT(VARCHAR(10),DT,108) time
FROM Daily d
JOIN Trnevents t on t.DT between d.in_time and d.out_time
) t1
group by emp_readr_id,
emp_name,
[Date]
sqlifddle