SQL query min startdate max enddate across multiple rows - sql

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

Related

Create work pattern SQL table

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

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

MSSQL Check If From, To Date will fit between time range

I need a function in MSSQL Server which as parameters will get #startTime #endTime and will check table reservation (which also has startTime and endTime) if asked date range will fit between time range within table and Return 1 If Yes and 0 If no. A little bit confusing I will explain better on table (At least I hope so)
I have table:
|:-----------------------|----------------------:|
| startDate | endDate |
|:-----------------------|----------------------:|
| 2017-01-25 00:00:00.000|2017-01-25 12:00:00.000|
| 2017-01-25 13:00:00.000|2017-01-25 14:00:00.000|
|:-----------------------|----------------------:|
Need to check If reservation will be avaible for i.e:
#startTime = 2017-01-25 13:30:00.000
#endTime = 2017-01-25 15:00:00.000
And should return 0 because there is reservation in this period of time.
I've tried do this by #startTime > startDate and #endTime < endDate but condition is check for every row and I need check whole table.
Kind Regards
You can do something like this:
select (case when exists (select 1
from reservations r
where r.startDate <= #endTime and
r.endDate >= #startTime
)
then 0 else 1
end) as available;
The logic is simple. Two time periods overlap if the first starts before the second ends and the first ends after the second starts.
create table info(startdate datetime, enddate datetime);
insert into info values
('2017-01-01', '2017-01-05'),
('2017-01-03', '2017-01-06'),
('2017-01-01', '2017-01-15'),
('2017-01-02', '2017-01-13'),
('2017-01-12', '2017-01-18');
declare #StartDate datetime = '2017-01-03';
declare #EndDate datetime = '2017-01-04'
select *
from info
where startdate <= #StartDate and enddate >= #EndDate;
+----+---------------------+---------------------+
| | startdate | enddate |
+----+---------------------+---------------------+
| 1 | 01.01.2017 00:00:00 | 05.01.2017 00:00:00 |
+----+---------------------+---------------------+
| 2 | 03.01.2017 00:00:00 | 06.01.2017 00:00:00 |
+----+---------------------+---------------------+
| 3 | 01.01.2017 00:00:00 | 15.01.2017 00:00:00 |
+----+---------------------+---------------------+
| 4 | 02.01.2017 00:00:00 | 13.01.2017 00:00:00 |
+----+---------------------+---------------------+

Select record by given date from and date till.

I having the records as below
StartDate | EndDate | ID
---------------------------------
25-12-2016 30-12-2016 0
01-01-2017 05-01-2017 1
10-01-2017 12-01-2017 2
01-02-2017 05-02-2017 3
By given selecting the Date Range from 02-01-2017 till 11-01-2017 , How do we select the record Startdate n EndDate that is fall on between the Date Range given as expected?
Would like to expect table result as below
StartDate | EndDate | ID
------------------------------
01-01-2017 05-01-2017 1
10-01-2017 12-01-2017 2
So, basically you are asking how to check if two date ranges overlap.
The way to do this is to check that one starts before the other ends, while the other starts before one ends. You can see a visualization in the overlap tag wiki.
Your query should be something like this:
SELECT StartDate, EndDate, ID
FROM YourTable
WHERE StartDate <= '11-01-2017'
AND EndDate >= '02-01-2017'
Try the below query,
DECLARE #V_START_DATE DATETIME = '2017-01-02'
,#V_END_DATE DATETIME = '2017-01-11'
SELECT *
FROM #TABLE
WHERE StartDate BETWEEN #V_START_DATE AND #V_END_DATE
OR EndDate BETWEEN #V_START_DATE AND #V_END_DATE
Try the below Query
SELECT * FROM DateRanges
WHERE StartDate BETWEEN '02-01-2017' and '11-01-2017'
OR ENDdate BETWEEN '02-01-2017' and '11-01-2017'

SQL WHERE query on date range

In my table I have 118 records detailing projects. The 2 fields I am concerned with here are startdate and enddate.
I need to produce a report from this view which shows which projects were 'active' between the following date ranges:
01/01/2011 - 01/12/2011
I have tried the following WHERE clase:
WHERE startdate BETWEEN '01/04/2011' AND '01/12/2011'
OR enddate BETWEEN '01/04/2011' AND '01/12/2011'
OR startdate <= '01/04/2011' AND enddate >= '01/12/2011'
What comes through does not seem correct, there are only a few records displayed and many which I know for a fact should be displayed are not, such as one project with a start date of 20/07/2011 and enddate of 21/11/2011 dissapears when the WHERE query is run.
Can anyone see a fault with this WHERE query
WHERE
startdate <= '2011-12-01'
AND enddate >= '2011-01-01'
(Assuming the value in enddate is the last date the project is active)
Examples using numbers, searching for anything that overlaps 100 to 200...
Start | End | Start <= 200 | End >= 100
000 | 099 | Yes | No
101 | 199 | Yes | Yes (HIT!)
201 | 299 | No | Yes
000 | 150 | Yes | Yes (HIT!)
150 | 300 | Yes | Yes (HIT!)
000 | 300 | Yes | Yes (HIT!)
This absolutely needs an AND in the logic :)
In terms of your query...
Your query with parenthesis, looks like this...
WHERE
(
startdate BETWEEN '01/04/2011' AND '01/12/2011'
OR enddate BETWEEN '01/04/2011' AND '01/12/2011'
OR startdate <= '01/04/2011'
)
AND enddate >= '01/12/2011'
But your example never meets the last AND condition. Try adding parenthesis to be more explicit...
WHERE
(startdate BETWEEN '01/04/2011' AND '01/12/2011')
OR (enddate BETWEEN '01/04/2011' AND '01/12/2011')
OR (startdate <= '01/04/2011' AND enddate >= '01/12/2011')
Before the query add
set dateformat dmy
Also maybe add some brackets
WHERE
(startdate BETWEEN '01/01/2011' AND '01/12/2011')
OR
(enddate BETWEEN '01/01/2011' AND '01/12/2011')
OR
(startdate <= '01/01/2011' AND enddate >= '01/12/2011')
Assuming startdate and enddate are date fields,
Try this:
WITH Dates AS (
SELECT [Date] = #StartDate
UNION ALL SELECT [Date] = DATEADD(DAY, 1, [Date])
FROM Dates WHERE [Date] <= DATEADD(DAY, -1, #EndDate)
)
-- YOUR SELECT STATEMENT
-- YOUR FROM STATEMENT
CROSS APPLY Dates
WHERE [Date] BETWEEN startdate AND enddate
-- The rest of your where statement here
OPTION(MAXRECURSION 0);
Declaring your start date as 01/01/2011 and your end date as 01/12/2011
Everyone's looking at this the wrong way round comparing startdate and enddate to a string when you can compare the string to the columns; the following is the simplest way of ascertaining what you want:
where ( '01/04/2011' between startdate and enddate
or '01/12/2011' between startdate and enddate
)
My original query was working, the database I was connecting to however had different date formats to my query.