Create work pattern SQL table - sql

I'm looking to create what appears to be quite a simple table in SQL, however, I'm struggling to create it.
The first date at which the work pattern starts is 01/01/1990 (UK date format: dd/mm/yyyy - happy to have this as 1990/01/01 if necessary). The end date of the first period is 8 weeks from the start date (26/02/1990). The start date of the next period is the day after the previous end date (27/02/1990) and so on. I'd want the last end date to be some time in the future (at least 10 years from now).
This is ideally how I want the table to look:
+--------+------------+------------+
| Period | Start Date | End Date |
+--------+------------+------------+
| 1 | 01/01/1990 | 26/02/1990 |
| 2 | 27/02/1990 | 24/04/1990 |
| 3 | 25/04/1990 | 20/06/1990 |
| 4 | 21/06/1990 | 16/08/1990 |
| 5 | 17/08/1990 | 12/10/1990 |
+--------+------------+------------+
Any help much appreciated.

If you are just adding 8 weeks and not considering the weekends you can follow something like this.
DECLARE #tempTable TABLE(
Period INT IDENTITY PRIMARY KEY,
StartDate DateTime,
EndDate DateTime
);
DECLARE #startDate DATETIME = GETDATE();
DECLARE #endDate DATETIME;
DECLARE #currYear INT = DATEPART(YY,#startDate);
DECLARE #endYear INT = #currYear + 10;
WHILE (#currYear <= #endYear)
BEGIN
SET #endDate = DATEADD(WEEK,8,#startDate);
INSERT INTO #tempTable (StartDate, EndDate) VALUES(#startDate, #endDate);
SET #startDate = DATEADD(dd,1,#endDate);
SET #currYear = DATEPART(YY,#startDate);
END;
SELECT Period, FORMAT(StartDate,'dd/MM/yyyy') AS StartDate, FORMAT(EndDate,'dd/MM/yyyy') AS EndDate FROM #tempTable

CREATE TABLE your_table
(
Period INT,
StartDate DATE,
EndDate DATE
)
INSERT INTO your_table
VALUES
(1,'1990-01-01','1990-02-26')
--etc

Related

Raise Error/ Throw Exception for Certain Condition SQL Server

http://www.sqlfiddle.com/#!18/c913b/1
CREATE TABLE Table1 (
[ID] int PRIMARY KEY,
[Date] date,
[Name] varchar(40)
);
INSERT INTO Table1 (ID, date, Name )
Values ('1','07-20-18','Fred'),
('2','07-15-18','Sam'),
('3','07-20-18','Ben'),
('4','07-19-18','Simon'),
('5','07-25-18','Dave');
Current Query:
DECLARE #StartDate AS DATETIME,
#CurrentDate AS DATETIME
SET #StartDate = GETDATE() -30
SET #CurrentDate = GETDATE()
SELECT [ID], [Date], [Name]
FROM Table1
WHERE [Date] BETWEEN #StartDate AND #CurrentDate
Current result:
| ID | Date | Name |
|----|------------|-------|
| 1 | 2018-07-20 | Fred |
| 2 | 2018-07-15 | Sam |
| 3 | 2018-07-20 | Ben |
| 4 | 2018-07-19 | Simon |
This query compares with the range of day set for the #startdate variable. This can be changed to anything by anyone who wants to run the query. The issue is if someone selects a really large range of dates it crashes. Is there a way i can set an if statement that throws an error message if for example they tried to run a query for longer than 100 days.
Result: ERROR - Please lower the date range
Is this possible at all?

Discard existing dates that are included in the result, SQL Server

In my database I have a Reservation table and it has three columns Initial Day, Last Day and the House Id.
I want to count the total days and omit those who are repeated, for example:
+-------------+------------+------------+
| | Results | |
+-------------+------------+------------+
| House Id | InitialDay | LastDay |
+-------------+------------+------------+
| 1 | 2017-09-18 | 2017-09-20 |
| 1 | 2017-09-18 | 2017-09-22 |
| 19 | 2017-09-18 | 2017-09-22 |
| 20 | 2017-09-18 | 2017-09-22 |
+-------------+------------+------------+
If you noticed the House Id with the number 1 has two rows, and each row has dates but the first row is in the interval of dates of the second row. In total the number of days should be 5 because the first shouldn't be counted as those days already exist in the second.
The reason why this is happening is that each house has two rooms, and different persons can stay in that house on the same dates.
My question is: how can I omit those cases, and only count the real days the house was occupied?
In your are using SQL Server 2012 or higher you can use LAG() to get the previous final date and adjust the initial date:
with ReservationAdjusted as (
select *,
lag(LastDay) over(partition by HouseID order by InitialDay, LastDay) as PreviousLast
from Reservation
)
select HouseId,
sum(case when PreviousLast>LastDay then 0 -- fully contained in the previous reservation
when PreviousLast>=InitialDay then datediff(day,PreviousLast,LastDay) -- overlap
else datediff(day,InitialDay,LastDay)+1 -- no overlap
end) as Days
from ReservationAdjusted
group by HouseId
The cases are:
The reservation is fully included in the previous reservation: we only need to compare end dates because the previous row is obtained ordering by InitialDay, LastDay, so the previous start date is always minor or equal than the current start date.
The current reservation overlaps with the previous: in this case we adjust the start and don't add 1 (the initial day is already counted), this case include when the previous end is equal to the current start (is a one day overlap).
There is no overlap: we just calculate the difference and add 1 to count also the initial day.
Note that we don't need extra condition for the reservation of a HouseID because by default the LAG() function returns NULL when there isn't a previous row, and comparisons with null always are false.
Sample input and output:
| HouseId | InitialDay | LastDay |
|---------|------------|------------|
| 1 | 2017-09-18 | 2017-09-20 |
| 1 | 2017-09-18 | 2017-09-22 |
| 1 | 2017-09-21 | 2017-09-22 |
| 19 | 2017-09-18 | 2017-09-27 |
| 19 | 2017-09-24 | 2017-09-26 |
| 19 | 2017-09-29 | 2017-09-30 |
| 20 | 2017-09-19 | 2017-09-22 |
| 20 | 2017-09-22 | 2017-09-26 |
| 20 | 2017-09-24 | 2017-09-27 |
| HouseId | Days |
|---------|------|
| 1 | 5 |
| 19 | 12 |
| 20 | 9 |
select house_id,min(initialDay),max(LastDay)
group by houseId
If I understood correctly!
Try out and let me know how it works out for you.
Ted.
While thinking through your question I came across the wonder that is the idea of a Calendar table. You'd use this code to create one, with whatever range of dates your want for your calendar. Code is from http://blog.jontav.com/post/9380766884/calendar-tables-are-incredibly-useful-in-sql
declare #start_dt as date = '1/1/2010';
declare #end_dt as date = '1/1/2020';
declare #dates as table (
date_id date primary key,
date_year smallint,
date_month tinyint,
date_day tinyint,
weekday_id tinyint,
weekday_nm varchar(10),
month_nm varchar(10),
day_of_year smallint,
quarter_id tinyint,
first_day_of_month date,
last_day_of_month date,
start_dts datetime,
end_dts datetime
)
while #start_dt < #end_dt
begin
insert into #dates(
date_id, date_year, date_month, date_day,
weekday_id, weekday_nm, month_nm, day_of_year, quarter_id,
first_day_of_month, last_day_of_month,
start_dts, end_dts
)
values(
#start_dt, year(#start_dt), month(#start_dt), day(#start_dt),
datepart(weekday, #start_dt), datename(weekday, #start_dt), datename(month, #start_dt), datepart(dayofyear, #start_dt), datepart(quarter, #start_dt),
dateadd(day,-(day(#start_dt)-1),#start_dt), dateadd(day,-(day(dateadd(month,1,#start_dt))),dateadd(month,1,#start_dt)),
cast(#start_dt as datetime), dateadd(second,-1,cast(dateadd(day, 1, #start_dt) as datetime))
)
set #start_dt = dateadd(day, 1, #start_dt)
end
select *
into Calendar
from #dates
Once you have a calendar table your query is as simple as:
select distinct t.House_id, c.date_id
from Reservation as r
inner join Calendar as c
on
c.date_id >= r.InitialDay
and c.date_id <= r.LastDay
Which gives you a row for each unique day each room was occupied. If you need a sum of how many days each room was occupied it becomes:
select a.House_id, count(a.House_id) as Days_occupied
from
(select distinct t.House_id, c.date_id
from so_test as t
inner join Calendar as c
on
c.date_id >= t.InitialDay
and c.date_id <= t.LastDay) as a
group by a.House_id
Create a table of all the possible dates and then join it to the Reservations table so that you have a list of all days between InitialDay and LastDay. Like this:
DECLARE #i date
DECLARE #last date
CREATE TABLE #temp (Date date)
SELECT #i = MIN(Date) FROM Reservations
SELECT #last = MAX(Date) FROM Reservations
WHILE #i <= #last
BEGIN
INSERT INTO #temp VALUES(#i)
SET #i = DATEADD(day, 1, #i)
END
SELECT HouseID, COUNT(*) FROM
(
SELECT DISTINCT HouseID, Date FROM Reservation
LEFT JOIN #temp
ON Reservation.InitialDay <= #temp.Date
AND Reservation.LastDay >= #temp.Date
) AS a
GROUP BY HouseID
DROP TABLE #temp

SQL query min startdate max enddate across multiple rows

SQL Server 2008 R2
I have data like this:
+---------+--------+-------------+-------------+
| SchedId | AdId | StartDate | EndDate |
+---------+--------+-------------+-------------+
| 335779 | 179911 | 2017-01-04 | 2017-01-04 |
| 335780 | 179911 | 2017-01-05 | 2017-01-05 |
| ... | | | |
| 335802 | 179911 | 2017-01-31 | 2017-01-31 |
+---------+--------+-------------+-------------+
Across all the records that have an AdId of 179911, I need the minimum StartDate (2017-01-04)and the maximum EndDate (2017-01-31) for a specific date of the month.
I would like to place the data I need for a specific date into a temp table. I tried this:
declare #dt datetime
set #dt = '2017-01-19'
SELECT
MIN(dbo.aoadrundates.StartDate) AS MinStartDate,
MAX(dbo.aoadrundates.EndDate) AS MaxEndDate,
dbo.aoadrundates.AdId AS T_AdId
FROM
dbo.aoadrundates
WHERE
StartDate >= #dt AND EndDate <= #dt
GROUP BY
dbo.AoAdRunDates.AdId,
dbo.aoadrundates.StartDate,
dbo.aoadrundates.EndDate
but I only get the single record with the date I'm selecting, e.g.,
MinStartDate MaxEndDate T_AdId
2017-01-19 2017-01-19 179911
I tried a few examples I found that use cast, min, max as well as a subquery with an inner join, but these have not worked (I think) because I need both min from 1 row and max from another.
Please, if you don't really want to help me, that's fine. If you can help, I would be most grateful. Thank you
So the problem is that you're grouping by the fields you want to aggregate by - dbo.aoadrundates.StartDate and dbo.aoadrundates.EndDate. Assuming you're wanting to group by AdId you can use:
declare #dt datetime
set #dt = '2017-01-19'
SELECT min(dbo.aoadrundates.StartDate) as MinStartDate,
max(dbo.aoadrundates.EndDate) as MaxEndDate,
dbo.aoadrundates.AdId
from
dbo.aoadrundates
where StartDate >= #dt and EndDate <= #dt
group by
dbo.AoAdRunDates.AdId
From the comment "Across all the records" though you might want it repeated for every record with that AdId, in which case you're going to have to use a CTE or similar (in 2012+ there are better ways...):
declare #dt datetime
set #dt = '2017-01-19'
;with cte_MinAndMax as (
SELECT min(dbo.aoadrundates.StartDate) as MinStartDate,
max(dbo.aoadrundates.EndDate) as MaxEndDate,
dbo.aoadrundates.AdId
from
dbo.aoadrundates
where StartDate >= #dt and EndDate <= #dt
group by
dbo.AoAdRunDates.AdId
)
select
dbo.aoadrundates.AdId as T_AdId,
cte_MinAndMax.MinStartDate,
cte_MinAndMax.MaxEndDate,
dbo.aoadrundates.whatever_else_you_want_to_select
from dbo.aoadrundates
left join cte_MinAndMax on dbo.aoadrundates.AdId = cte_MinAndMax.AdId

Sql Server - Comparing time only in 24 hour format?

I want to compare time with a time-range and if that particular time lies with-in that time-range, then it should return a record.
For example, Table 'A' has :
+-----------+------+-------------+-----------+
| User_Code | Name | Shift_Start | Shift_End |
+-----------+------+-------------+-----------+
| 1 | ABC | 04:01:00 | 11:00:00 |
| 2 | DEF | 11:01:00 | 20:00:00 |
| 3 | XYZ | 20:01:00 | 04:00:00 |
+-----------+------+-------------+-----------+
Now I want to check whose shift was it at this specific datetime : 2016-09-26 02:51:59. The SQL query should return the User_Code = 3. Shift_Start and Shift_End are of type time.
I have tried converting 2016-09-26 02:51:59 to time and then comparing with shift_start and shift_end using between and using logical operator but I am unable to get the desired result.
This has got me baffled. Its been an hour since I have tried coming up with a solution but I am unable to do so. Tried googling but got nothing there too. Any help will be appreciated. Using SQL Server 2012.
You need a more complicated where condition, because the shift times can be in either order.
So:
where ((shift_start < shift_end) and
cast(#datetime as time) between shift_start and shift_end)
) or
((shift_start > shift_end) and
cast(#datetime as time) not between shift_end and shift_start)
)
declare #t table(
User_Code int,
Name varchar(20),
Shift_Start time,
Shift_End time
);
insert #t(User_Code,Name,Shift_Start,Shift_End)
values
(1,'ABC','04:01:00','11:00:00')
,(2,'DEF','11:01:00','20:00:00')
,(3,'XYZ','20:01:00','04:00:00');
Try
declare #d datetime = '2016-09-26 02:51:59';
select *
from #t
cross apply ( -- intermediate vars: rounded param
select dt = cast(cast(#d as date) as datetime)
) r
where #d between dateadd(d, case when Shift_Start<Shift_End then 0 else -1 end, dt) + cast(Shift_start as datetime)
and dt+cast(Shift_end as datetime);
Approach from the other angle and add the date part of your search criteria to the Shift Start and End Times:
Note the case statement, this handles the situation where the shift end crosses midnight.
DECLARE #dateTime DATETIME = '2016-09-26 02:51:59'
-- Remove the time portion of above
DECLARE #datePortion DATETIME = DATEADD(dd, 0, DATEDIFF(dd, 0, #dateTime))
SELECT * FROM TableA
WHERE #dateTime BETWEEN #datePortion + cast(Shift_Start as datetime)
AND CASE WHEN ShiftEnd < ShiftStart
THEN DATEADD(day, 1, #datePortion)
ELSE #datePortion
END + cast(Shift_End as datetime)

What is the most efficient way to convert a split-up date to a datetime value in SQL Server 2005?

I'm trying to tie together two SQL Server 2005 databases using a view. The source database splits the date across three int fields.
RecordId | RecordYear | RecordMonth | RecordDay
-----------------------------------------------
000001 | 2001 | 1 | 26
000002 | 2002 | 3 | 10
My goal is to create an easier-to-work-with view with a single datetime field for the date, something like below.
RecordId | RecordDate
---------------------
000001 | 2001/01/26
000002 | 2002/03/10
What is the most efficient way to get this done?
Right now, I'm casting each column as a varchar, concatenate them with slash separators, then casting the full varchar as a datetime. I have to feel like there's a more efficient way.
cast(
cast(RecordYear as varchar) + '/' +
cast(RecordMonth as varchar) + '/' +
cast(RecordDay as varchar)
as datetime
) as RecordDate
No, don't cast to string, and definitely not to varchar without length.
Try:
DECLARE #x TABLE
(
RecordId CHAR(6) PRIMARY KEY,
RecordYear INT,
RecordMonth INT,
RecordDay INT
);
INSERT #x VALUES('000001',2001,1,26);
INSERT #x VALUES('000002',2002,3,10);
SELECT
RecordId,
RecordDate = DATEADD(DAY, RecordDay-1,
DATEADD(MONTH, RecordMonth-1,
DATEADD(YEAR, RecordYear-1900, '19000101'
)))
FROM #x
ORDER BY RecordId;
Results:
RecordId RecordDate
-------- ----------
000001 2001-01-26
000002 2002-03-10