SQL WHERE query on date range - sql

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.

Related

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 |
+----+---------------------+---------------------+

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

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'

Query grouped by calendar week / Issue: Date expands over more than one year

I have an SQL Query which calculates a quote of two sums. This quote shall be calculated on a weekly basis. Therefore is used Datepart('ww', Date, 2, 2) to get the calendar week.
This query worked fine during 2014, but now I am facing a problem.
The users choses with date pickers in a form from and to date which is then used in the where clause to only select relevant records.
If they chose a from date from past year (e.g. 2014) and a to date from this year my query shows irrelvant data, because it does not cosinder the year just the calendar week.
So, how group by calendar week AND only chose records from the correct year.
SELECT DatePart('ww',Date,2,2) AS WEEK,
Year(Workload_Date) AS [YEAR],
(SUM(Value1)+SUM(Value2))AS [Total1],
(SUM(Value3)+SUM(Value4)) AS [Total2],
((SUM(Value1)+SUM(Value2))/(SUM(Value3)+SUM(Value4))) AS [Quote]
FROM tbl
WHERE DatePart('ww',Date,2,2) Between DatePart('ww',FromDatePickerField,2,2) And DatePart('ww',ToDatePickerField,2,2)
GROUP BY DatePart('ww',Date,2,2), Year(Date)
ORDER BY Year(Date), DatePart('ww',Date,2,2);
The table contains one record per day.
Date | Value1 | Value2 | Value3 | Value4 |
01.01.2014 | 4 | 3 | 2 | 3 |
02.01.2014 | 4 | 3 | 9 | 3 |
03.01.2014 | 4 | 3 | 4 | 1 |
04.01.2014 | 4 | 3 | 1 | 3 |
...
01.01.2015 | 4 | 3 | 6 | 3 |
02.01.2015 | 4 | 3 | 3 | 7 |
You could base your logic on Week_Ending_date instead of the week number and year, that way you could aggregate all you data on a weekly basis and let SQL handle the week/year detection logic.
Incase, you have a date range that spans 2 years, even then the calculations will be based on the week_ending_date and should work out correctly.
Something like...
SELECT DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE) AS WEEK_ENDING_DATE
,Year(DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE)) AS [YEAR]
,(SUM(Value1) + SUM(Value2)) AS [Total1]
,(SUM(Value3) + SUM(Value4)) AS [Total2]
,((SUM(Value1) + SUM(Value2)) / (SUM(Value3) + SUM(Value4))) AS [Quote]
FROM tbl
WHERE DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE) BETWEEN DATEADD(dd, 7-(DATEPART(dw, FromDatePickerField)), FromDatePickerField)
AND DATEADD(dd, 7-(DATEPART(dw, ToDatePickerField)), ToDatePickerField)
and date >= FromDatePickerField
and date <= ToDatePickerField
GROUP BY DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE)
ORDER BY DATEADD(dd, 7-(DATEPART(dw, DATE)), DATE)
I see 2 possible solutions.
1) You stop the user while using the datepicker on the form whenever the years of the two datepickers are different. Maybe a MsgBox or something,
2) You change the to-DatePicker to the from-Year.
DECLARE #MinDate DATE = '01.11.2014'
DECLARE #tmpDate DATE = '12.02.2015'
DECLARE #MaxDate DATE = (CASE WHEN Year(#MinDate) != Year(#tmpDate) THEN Convert(DATE, '31.12.' + Convert(VARCHAR(4), Year(#MinDate))) ELSE #tmpDate END)
SELECT #MinDate, #tmpDate, #MaxDate
Result :
minDate tmpDate maxDate
01.11.2014 12.02.2015 31.12.2014

Generate more rows if there is a difference between two columns in SQL Server

I'm currently working on some reports from MS Project Server and found this oddity:
For some obscure reason, whenever you appoint to the same task with the same amount of time in consecutive days, instead of creating an entry for each appointment, the application updates the start date and the finish date fields on database, leaving only one entry for that task, but with a range between the dates.
If the amount of time appointed to the task in consecutive days are different, then there will be created one entry per appointment.
(Yes, I know, it's kind of confusing. I don't even know how to explain this better).
I want to know if it is somehow possible to generate more rows within SQL statement whenever there is a difference between the start and the finish date, one for each day in the range.
This is the query I have right now, I already can tell which rows have this date difference, but I don't know what I can do next.
select
r.WRES_ID, r.RES_NAME, PROJ_NAME, p.WPROJ_ID, TASK_NAME, WWORK_VALUE, WWORK_START, WWORK_FINISH,
datediff(d, WWORK_START, WWORK_FINISH) + 1 AS work_days
from MSP_WEB_RESOURCES r
join
MSP_WEB_ASSIGNMENTS a on a.WRES_ID = r.WRES_ID
join
MSP_WEB_PROJECTS p on p.WPROJ_ID = a.WPROJ_ID
join
MSP_WEB_WORK w on w.WASSN_ID = a.WASSN_ID
where RES_NAME = 'HenriqueBarcelos'
and WWORK_TYPE = 1
and WWORK_VALUE > 0
and WWORK_FINISH between '2014-01-27' and '2014-01-31'
order by WWORK_FINISH DESC
I know I could do this at the application level, but I was wondering if I could just do it within the database itself.
Thank's in advance.
Edit:
These are my current results:
WRES_ID | RES_NAME | TASK_NAME | WWORK_VALUE | WWORK_START | WWORK_FINISH | work_days
--------+------------------+-------------------------+---------------+---------------------+---------------------+----------
382 | HenriqueBarcelos | Outsourcing Initiatives | 60000.000000 | 2014-01-30 00:00:00 | 2014-01-30 00:00:00 | 1
382 | HenriqueBarcelos | Internal Training | 289800.000000 | 2014-01-29 00:00:00 | 2014-01-29 00:00:00 | 1
382 | HenriqueBarcelos | Outsourcing Initiatives | 120000.000000 | 2014-01-29 00:00:00 | 2014-01-29 00:00:00 | 1
382 | HenriqueBarcelos | Outsourcing Initiatives | 60000.000000 | 2014-01-27 00:00:00 | 2014-01-28 00:00:00 | 2
382 | HenriqueBarcelos | Infrastructure (TI) | 120000.000000 | 2014-01-27 00:00:00 | 2014-01-27 00:00:00 | 1
Notice that the second last register has a range of 2 days. In deed, there are 2 appointments, one on Jan 27th and other on 28th.
What I want to do is expand this and return one entry per day in this case.
It can be done, but it's not very elegant. First you need a function that will expand the date range into sequence of dates:
CREATE FUNCTION ufn_Expand(#start DATE, #end DATE)
RETURNS TABLE
AS
RETURN
WITH cte AS
(
SELECT #start AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt) FROM cte WHERE dt < #end
)
SELECT dt FROM cte
Then use that in your query with CROSS APPLY:
SELECT /* your columns */, x.dt
FROM /* your joins */
CROSS APPLY ufn_Expand(WWORK_START, WWORK_FINISH) x
I'd use a numbers table (nice and set-based, yum!)
SELECT start_date
, end_date
, DateDiff(dd, start_date, end_date) + 1 As number_of_days --rows to display
FROM your_table
INNER
JOIN dbo.numbers
ON numbers.number BETWEEN 1 AND DateDiff(dd, start_date, end_date) + 1
Use your favourite search engine to find a numbers table script. Here's one I made earlier.
As an aside: if you remove the +1s you just modify the join to be between zero and the DateDiff() - I added the +1s as I thought it might be clearer!
You can see this from another perspective. You don't really want a row per each worked day. What you really need it's the number of worked days, multiplied by the reported worked time. Something like this:
(dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
however, this creates an issue. Let's say you want a given period. If you use the WWORK_START and WWORK_FINISH dates for your report, you need to be careful to include all the work with only some days inside the period. Something like this will do it:
DECLARE #InitDate DATETIME;
DECLARE #EndDate DATETIME;
SET #InitDate = '2016/06/01';
SET #EndDate = '2016/07/01';
--Full list of tasks
SELECT dbo.MSP_WEB_RESOURCES.RES_NAME AS Name, dbo.MSP_WEB_PROJECTS.PROJ_NAME AS Project,
dbo.MSP_WEB_WORK.WWORK_VALUE / 60000 AS ReportedWork,
CASE
WHEN WWORK_START < #InitDate THEN DATEDIFF(day, #InitDate, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1 --If the task started before the start of the period
WHEN WWORK_FINISH > DATEDIFF(day,-1,#EndDate) THEN DATEDIFF(day, WWORK_START, DATEDIFF(day,-1,#EndDate)) + 1 --if the task ended after the end of the period
ELSE DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1 --All tasks with start and end date inside the period
END AS RepeatedDays,
CASE
WHEN WWORK_START < #InitDate THEN (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, #InitDate, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
WHEN WWORK_FINISH > DATEDIFF(day,-1,#EndDate) THEN (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, WWORK_START, DATEDIFF(day,-1,#EndDate)) + 1)
ELSE (dbo.MSP_WEB_WORK.WWORK_VALUE / 60000) * (DATEDIFF(day, dbo.MSP_WEB_WORK.WWORK_START, dbo.MSP_WEB_WORK.WWORK_FINISH) + 1)
END AS ActualWork,
dbo.MSP_WEB_WORK.WWORK_START,
dbo.MSP_WEB_WORK.WWORK_FINISH
FROM dbo.MSP_WEB_RESOURCES INNER JOIN
dbo.MSP_WEB_ASSIGNMENTS INNER JOIN
dbo.MSP_WEB_PROJECTS ON dbo.MSP_WEB_ASSIGNMENTS.WPROJ_ID = dbo.MSP_WEB_PROJECTS.WPROJ_ID INNER JOIN
dbo.MSP_WEB_WORK ON dbo.MSP_WEB_ASSIGNMENTS.WASSN_ID = dbo.MSP_WEB_WORK.WASSN_ID ON
dbo.MSP_WEB_RESOURCES.WRES_ID = dbo.MSP_WEB_ASSIGNMENTS.WRES_ID
WHERE (dbo.MSP_WEB_WORK.WWORK_TYPE = 1) AND
(
#InitDate BETWEEN dbo.MSP_WEB_WORK.WWORK_START and dbo.MSP_WEB_WORK.WWORK_FINISH OR
DATEADD(day,-1,#EndDate) BETWEEN dbo.MSP_WEB_WORK.WWORK_START and dbo.MSP_WEB_WORK.WWORK_FINISH OR
(dbo.MSP_WEB_WORK.WWORK_START >= #InitDate) AND
(dbo.MSP_WEB_WORK.WWORK_FINISH < #EndDate)
)
ORDER BY dbo.MSP_WEB_WORK.WWORK_START;