Copy shifts from leap year to non-leap year - tsql

I need to copy all the shifts from 2012 to 2013 using T-SQL 2008 R2. There are 3 shifts per day. Start date and shift date are always same. end date (for shift c) is the next day.
As you can see, if I just used dateadd(year, 1, Col), I get 2 sets of records for 2013-02-28. The rows 4, 6 and 8 shouldn't be there (and will cause PK violations). row 8 is wrong as the end time for shift C should be previous calendar day.
I have 67,000-ish rows in total to copy
Only thing I can think of off top of my head is insert to temp table and then somehow identify dupes/incorrect records, delete and then insert back into shifts table. I'm sure there must be a better way
Anyone got a cunning plan?
I'd like to create a general purpose Stored procedure that can copy leap year to non-leap year and vice versa
Regards
Mark

Maybe try a DISTINCT list combined with a WHERE End > Start, as in this simplified example:
CREATE TABLE Shifts(ShiftCode CHAR, ShiftStart DATETIME, ShiftEnd DATETIME);
GO
INSERT Shifts
VALUES('A','2/26/2012 07:00:00','2/26/2012 15:00:00')
, ('B','2/26/2012 15:00:00','2/26/2012 23:00:00')
, ('C','2/26/2012 23:00:00','2/27/2012 07:00:00')
, ('A','2/27/2012 07:00:00','2/27/2012 15:00:00')
, ('B','2/27/2012 15:00:00','2/27/2012 23:00:00')
, ('C','2/27/2012 23:00:00','2/28/2012 07:00:00')
, ('A','2/28/2012 07:00:00','2/28/2012 15:00:00')
, ('B','2/28/2012 15:00:00','2/28/2012 23:00:00')
, ('C','2/28/2012 23:00:00','2/29/2012 07:00:00')
, ('A','2/29/2012 07:00:00','2/29/2012 15:00:00')
, ('B','2/29/2012 15:00:00','2/29/2012 23:00:00')
, ('C','2/29/2012 23:00:00','3/1/2012 07:00:00')
, ('A','3/1/2012 07:00:00','3/1/2012 15:00:00')
, ('B','3/1/2012 15:00:00','3/1/2012 23:00:00')
, ('C','3/1/2012 23:00:00','3/2/2012 07:00:00');
GO
SELECT DISTINCT ShiftCode
, ShiftStart = DATEADD(YYYY,1,ShiftStart)
, ShiftEnd = DATEADD(YYYY,1,ShiftEnd)
FROM Shifts
WHERE DATEADD(YYYY,1,ShiftEnd) > DATEADD(YYYY,1,ShiftStart)
ORDER BY DATEADD(YYYY,1,ShiftStart), ShiftCode
GO
Result:
A 2013-02-26 07:00:00.000 2013-02-26 15:00:00.000
B 2013-02-26 15:00:00.000 2013-02-26 23:00:00.000
C 2013-02-26 23:00:00.000 2013-02-27 07:00:00.000
A 2013-02-27 07:00:00.000 2013-02-27 15:00:00.000
B 2013-02-27 15:00:00.000 2013-02-27 23:00:00.000
C 2013-02-27 23:00:00.000 2013-02-28 07:00:00.000
A 2013-02-28 07:00:00.000 2013-02-28 15:00:00.000
B 2013-02-28 15:00:00.000 2013-02-28 23:00:00.000
C 2013-02-28 23:00:00.000 2013-03-01 07:00:00.000
A 2013-03-01 07:00:00.000 2013-03-01 15:00:00.000
B 2013-03-01 15:00:00.000 2013-03-01 23:00:00.000
C 2013-03-01 23:00:00.000 2013-03-02 07:00:00.000

I figured it out BUT then found some resources were missing shifts for 2012
Ended up creating with tally table and just doing fresh inserts for every shift for the year
SELECT
rh.PlanPressID
,DATEADD(hh,(24 / #NoOfShifts) * (t.N - 1),#StartDateTime) AS ShiftStart
,DATEADD(hh,(24 / #NoOfShifts) * (t.N),#StartDateTime) AS ShiftEnd
,CHAR((t.N - 1) % #NoOfShifts + 65) AS ShiftCode
,DATEADD(dd,0,DATEDIFF(dd,0,DATEADD(hh,(24 / #NoOfShifts) * (t.N - 1),#StartDateTime))) AS ShiftDate
,0 AS Personnel
FROM
dbo.Tally t
CROSS JOIN dbo.ResourceHeader AS rh

Related

PostgreSQL Data Selection

Is it possible to write PostgreSQL code that looks at the sample data in the selects only the persons who have been active for the whole first quarter( 01/01/2018 to 03/31/2018) as shown in the desired output? Note that person H should not be selected because they are missing January.
Sample Data
Person Start Date End Date
A 1/1/2018 1/31/2018
A 2/1/2018 2/28/2018
A 3/1/2018 3/31/2018
B 1/1/2018 2/28/2018
C 1/1/2018 2/28/2018
C 3/1/2018 3/31/2018
D 2/1/2018 3/31/2018
E 2/1/2018 2/28/2018
F 1/1/2018 3/31/2018
G 1/1/2018 4/30/2018
H 2/1/2018 4/30/2018
Desired Output
Person
A
C
F
G
Assuming your columns are proper DATE columns and there are no overlaps, you could do something like this:
select person
from the_table
group by person
having sum(end_date - start_date + 1) >= date '2018-03-31' - date '2018-01-01' + 1
order by person;
Subtracting one date from another yields the number of days between those two dates. Then the sum of all differences is compared to the difference between the start and end date of the quarter.
Online example: https://rextester.com/OIN10602

SAS: Separate date_from & date_to into separate lines

I've got an example like this:
data date_table;
stop;
length id $32.;
length name $32.;
length date_from date_to 8.;
format date_from date_to datetime19.;
run;
proc sql;
insert into date_table
values ('1', 'Mark', '13Jun2019 08:39:00'dt, '13Jun2019 11:39:00'dt)
values ('2', 'Bart', '13Jun2019 13:39:00'dt, '13Jun2019 17:39:00'dt);
quit;
I need some smart join (maybe with separate hour mapping table) to achieve something like this:
What I've been trying now was using mapping table
and join like:
proc sql;
create table testing as
select t1.id,
t1.name,
t1.date_from,
t1.date_to
from DATE_TABLE t1 inner join
WORK.CAL_TIME t2 on t1.date_from >= t2.Time and
t1.date_to <= t2.Time;
quit;
But of course the result is empty table because date dpoens't want t join. I might cut date_from and date_to to full hours but still such a join doens't work.
Help.
Looks like you are comparing apples (DATETIME) with oranges (TIME). The order of magnitude of those numbers are totally different.
684 data _null_;
685
686 dt = '13Jun2019 08:39:00'dt ;
687 tm = '08:00't ;
688 put (dt tm) (=comma20.);
689 run;
dt=1,876,034,340 tm=28,800
You probably just want to compare the time of day part of your datetime values to your time values. Also round your start times down and your end times up to the hour.
data date_table;
length id name $32 date_from date_to 8;
format date_from date_to datetime19.;
input id name (date:) (:datetime.);
cards;
1 Mark 13Jun2019:08:39:00 13Jun2019:11:39:00
2 Bart 13Jun2019:13:39:00 13Jun2019:17:39:00
;
data cal_time;
do time='08:00't to '21:00't by '01:00't ;
output;
end;
format time time5.;
run;
proc sql;
create table testing as
select t1.id
, t1.name
, max(t1.date_from,dhms(datepart(t1.date_from),0,0,t2.time))
as datetime_from format=datetime19.
, min(t1.date_to,dhms(datepart(t1.date_to),0,0,t2.time+'01:00't))
as datetime_to format=datetime19.
, t2.time
from DATE_TABLE t1
inner join WORK.CAL_TIME t2
on t2.time between intnx('hour',timepart(t1.date_from),0,'b')
and intnx('hour',timepart(t1.date_to),0,'e')
;
quit;
Result
Obs id name datetime_from datetime_to time
1 1 Mark 13JUN2019:08:39:00 13JUN2019:09:00:00 8:00
2 1 Mark 13JUN2019:09:00:00 13JUN2019:10:00:00 9:00
3 1 Mark 13JUN2019:10:00:00 13JUN2019:11:00:00 10:00
4 1 Mark 13JUN2019:11:00:00 13JUN2019:11:39:00 11:00
5 2 Bart 13JUN2019:13:39:00 13JUN2019:14:00:00 13:00
6 2 Bart 13JUN2019:14:00:00 13JUN2019:15:00:00 14:00
7 2 Bart 13JUN2019:15:00:00 13JUN2019:16:00:00 15:00
8 2 Bart 13JUN2019:16:00:00 13JUN2019:17:00:00 16:00
9 2 Bart 13JUN2019:17:00:00 13JUN2019:17:39:00 17:00

Break into multiple rows based on date range of a single row

I have a table which captures appointments, some are single day appointments and some are multi day appointments, so the data looks like
AppointmentId StartDate EndDate
9 2017-04-12 2017-04-12
10 2017-05-01 2017-05-03
11 2017-06-01 2017-06-01
I want to split the multi day appointment as single days, so the result I am trying to achieve is like
AppointmentId StartDate EndDate
9 2017-04-12 2017-04-12
10 2017-05-01 2017-05-01
10 2017-05-02 2017-05-02
10 2017-05-03 2017-05-03
11 2017-06-01 2017-06-01
So I have split the appointment id 10 into multiple rows. I checked a few other questions like
here but those are to split just based on a single start date and end date and not based on table data
You can use a Calendar or dates table for this sort of thing.
For only 152kb in memory, you can have 30 years of dates in a table with this:
/* dates table */
declare #fromdate date = '20000101';
declare #years int = 30;
/* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
select top (datediff(day, #fromdate,dateadd(year,#years,#fromdate)))
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
into dbo.Dates
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date];
create unique clustered index ix_dbo_Dates_date
on dbo.Dates([Date]);
Without taking the actual step of creating a table, you can use it inside a common table expression with just this:
declare #fromdate date = '20161229';
declare #thrudate date = '20170103';
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top (datediff(day, #fromdate, #thrudate)+1)
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date]
)
select [Date]
from dates;
Use either like so:
select
t.AppointmentId
, StartDate = d.date
, EndDate = d.date
from dates d
inner join appointments t
on d.date >= t.StartDate
and d.date <= t.EndDate
rextester demo: http://rextester.com/TNWQ64342
returns:
+---------------+------------+------------+
| AppointmentId | StartDate | EndDate |
+---------------+------------+------------+
| 9 | 2017-04-12 | 2017-04-12 |
| 10 | 2017-05-01 | 2017-05-01 |
| 10 | 2017-05-02 | 2017-05-02 |
| 10 | 2017-05-03 | 2017-05-03 |
| 11 | 2017-06-01 | 2017-06-01 |
+---------------+------------+------------+
Number and Calendar table reference:
Generate a set or sequence without loops - 1 - Aaron Bertrand
Generate a set or sequence without loops - 2 - Aaron Bertrand
Generate a set or sequence without loops - 3 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand
tsql Function to Determine Holidays in sql Server - Aaron Bertrand
F_table_date - Michael Valentine Jones
Clearly a Calendar/Tally table would be the way to go as SqlZim illustrated (+1), however you can use an ad-hoc tally table with a CROSS APPLY.
Example
Select A.AppointmentId
,StartDate = B.D
,EndDate = B.D
From YourTable A
Cross Apply (
Select Top (DateDiff(DD,A.StartDate,A.EndDate)+1) D=DateAdd(DD,-1+Row_Number() Over (Order By Number),A.StartDate)
From master..spt_values
) B
Returns
AppointmentId StartDate EndDate
9 2017-04-12 2017-04-12
10 2017-05-01 2017-05-01
10 2017-05-02 2017-05-02
10 2017-05-03 2017-05-03
11 2017-06-01 2017-06-01

One SQL Stored Procedure to get cut off date of two different cut off date format

I have one system that read from two client databases. For the two clients, both of them have different format of cut off date:
1) Client A: Every month at 15th. Example: 15-12-2016.
2) Client B: Every first day of the month. Example: 1-1-2017.
The cut off date are stored in the table as below:
Now I need a single query to retrieve the current month's cut off date of the client. For instance, today is 15-2-2017, so the expected cut off date for both clients should be as below:
1) Client A: 15-1-2017
2) Client B: 1-2-2017
How can I accomplish this in a single Stored Procedure? For client B, I can always get the first day of the month. But this can't apply to client A since their cut off is last month's date.
Might be something like this you are looking for:
DECLARE #DummyClient TABLE(ID INT IDENTITY,ClientName VARCHAR(100));
DECLARE #DummyDates TABLE(ClientID INT,YourDate DATE);
INSERT INTO #DummyClient VALUES
('A'),('B');
INSERT INTO #DummyDates VALUES
(1,{d'2016-12-15'}),(2,{d'2017-01-01'});
WITH Numbers AS
( SELECT 0 AS Nr
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 9
UNION ALL SELECT 10
UNION ALL SELECT 11
UNION ALL SELECT 12
UNION ALL SELECT 13
UNION ALL SELECT 14
UNION ALL SELECT 15
UNION ALL SELECT 16
UNION ALL SELECT 17
UNION ALL SELECT 18
UNION ALL SELECT 19
UNION ALL SELECT 20
UNION ALL SELECT 21
UNION ALL SELECT 22
UNION ALL SELECT 23
UNION ALL SELECT 24
)
,ClientExt AS
(
SELECT c.*
,MIN(d.YourDate) AS MinDate
FROM #DummyClient AS c
INNER JOIN #DummyDates AS d ON c.ID=d.ClientID
GROUP BY c.ID,c.ClientName
)
SELECT ID,ClientName,D
FROM ClientExt
CROSS APPLY(SELECT DATEADD(MONTH,Numbers.Nr,MinDate)
FROM Numbers) AS RunningDate(D);
The result
ID Cl Date
1 A 2016-12-15
1 A 2017-01-15
1 A 2017-02-15
1 A 2017-03-15
1 A 2017-04-15
1 A 2017-05-15
1 A 2017-06-15
1 A 2017-07-15
1 A 2017-09-15
1 A 2017-10-15
1 A 2017-11-15
1 A 2017-12-15
1 A 2018-01-15
1 A 2018-02-15
1 A 2018-03-15
1 A 2018-04-15
1 A 2018-05-15
1 A 2018-06-15
1 A 2018-07-15
1 A 2018-08-15
1 A 2018-09-15
1 A 2018-10-15
1 A 2018-11-15
1 A 2018-12-15
2 B 2017-01-01
2 B 2017-02-01
2 B 2017-03-01
2 B 2017-04-01
2 B 2017-05-01
2 B 2017-06-01
2 B 2017-07-01
2 B 2017-08-01
2 B 2017-10-01
2 B 2017-11-01
2 B 2017-12-01
2 B 2018-01-01
2 B 2018-02-01
2 B 2018-03-01
2 B 2018-04-01
2 B 2018-05-01
2 B 2018-06-01
2 B 2018-07-01
2 B 2018-08-01
2 B 2018-09-01
2 B 2018-10-01
2 B 2018-11-01
2 B 2018-12-01
2 B 2019-01-01

To calculate min and max of record for huge volume of record

My problem is to get max and minimum date for ABC. I have to do this for around 200 000 records which takes more time. How to improve the performance
ROW_ID DATE C value
----------------------------------------------
1 2012-08-01 00:00:00.0 ABC 87
2 2012-09-01 00:00:00.0 ABC 87
3 2012-10-01 00:00:00.0 ABC 87
4 2012-11-01 00:00:00.0 ABC 87
5 2012-12-01 00:00:00.0 ABC 87
6 2013-01-01 00:00:00.0 CBA 87
7 2013-02-01 00:00:00.0 ABC 87
8 2013-03-01 00:00:00.0 ABC 87
You should be able to do this easily using something like:
select c,
min(date) min_date,
max(date) max_date
from yt
where c='ABC'
group by c;
See SQL Fiddle with Demo.
Edit, since you are attempting to use this data to update another table in Sybase you have a few options. Sybase does not allow derived tables in UPDATE statements so I would suggest using a temp table to get the min/max date for each c and then use this table in your UPDATE with JOIN:
select c,
min(date) min_date,
max(date) max_date
into #temp2
from yt
where c='ABC'
group by c;
update t
set t.min_date = t1.min_date,
t.max_date = t1.max_date
from temp t
inner join #temp2 t1
on t.c = t1.c;
See SQL Fiddle with Demo