How to select a record that is between two dateranges and two input parameters StartDate and EndDate in SQL server? - sql

In my database I have a group which can be active in a week with opening hours.
Group A Startdate: 22-10-2012 EndDate: 28-10-2012 (Startdate is always beginning of the week)
Group A Startdate: 29-10-2012 EndDate: 04-11-2012 (Always end of the week)
Group A has two different opening hours in this two weeks.
Now I have a stored procedure that returns the active group by looking the input parameters StartDate and EndDate. If this two parameters are between the startdate and end date of ONE week then I'm getting the right opening hours.
But when the input parameters are about TWO weeks (Begindate: 22-10-2012 and EndDate: 30-10-2012) then I get the opening hours of two weeks.
declare #Begindate datetime
set #Begindate = '2012-10-22'
declare #Enddate datetime
set #Enddate = '2012-11-02'
SELECT Id, Date, ..., ...
FROM Table1 t1
INNER JOIN Combinations c ON t1.Id=c.Table1Id
INNER JOIN Group g ON c.GroupId=g.Id
WHERE t1.Date<= #Enddate) AND (t1.Date>= #Begindate (gets dates I need)
AND g.BeginDate <=#Enddate and g.Enddate >= #Begindate (Gets active groups)
Some table data:
Table1:
Id Date GroupId
1 2012-10-23 10
1 2012-10-29 10
Combinations: (holds the relation between Table1 and Group)
ID Table1Id GroupId
1 10 1
Group Table:
ID Name StartDate EndDate MondayOpen MondayClose ...
1 Group A 2012-10-22 2012-10-28 08:00 18:00
1 Group A 2012-10-29 2012-11-04 13:00 18:00
With this query I am getting a group two times because both the inputparametes are over two weeks.
How can I get for the dates (from t1) the correct opening dates looking the group Begin and End date?

You are missing a condition between table1 and [group], which is the last line in the query below.
SELECT *
FROM Table1 t1
INNER JOIN Combinations c ON t1.Id=c.Table1Id
INNER JOIN [Group] g ON c.GroupId=g.Id
WHERE t1.Date<= #Enddate AND t1.Date>= #Begindate
AND g.BeginDate <=#Enddate and g.Enddate >= #Begindate
AND t1.Date between g.BeginDate and g.EndDate
SQLFiddle

Related

Get list of dates that falls in End Of each Month?

I need to get end of each month for the past 3 yrs from the current date excluding statutory holidays and weekends using table1 and table2. Table1 has all the dates ranging from year 2025-2017. Table2 has all the statutory holidays for the years ranging from 2025-2017.
How to create SQL script for to attain this result? Any suggestions could help. Expected result would be list of date last 3yrs of endofmonth excluding statutory holidays and weekends.
Table 1 has 2 columns, DateId and FullDate column
DateID Fulldate
1010392 2019-12-1
1010393 2019-12-2
1010394 2019-12-3
1010395 2019-12-4
.
.
101086 2019-12-31
Table 2 has 2 columns, DateId and Statutory_Holidays
Date ID Stat_Holidays
101085 2019-12-25
101086 2019-12-26
And the returned results should look like
WeekDay_FullDate_Past3yrs
2019-12-31
2020-1-31
2020-2-28
2020-3-31
Tried the below:
select * from
( select a.Date from Table1 a where a.Date <=
'20221215' and a.Date >= DATEADD (YEAR, -3, getdate()) ) as t1
join
( select EOMONTH(a.Date) as Date from Table1 a where a.Date <= '20221215' and a.Date >= DATEADD (YEAR, -3, getdate()) ) as t2 on t1.Date = t2.Date
tried the solution from the below link it dosen't solve my issue. I'm looking to get list of last workday of a month(excluding weekends and holiday) for the past 3yrs
SQL Server - Get Last Business Data excluding holidays and Weekends
You can group by month and year and take the max date (excluding holidays and weekends):
SET DATEFIRST 1;
DECLARE #CurrentDate DATE = '20221215';
WITH cte
AS
(
SELECT MAX(Date ) as EOMDate
FROM Table1
WHERE DATEPART(weekday,Date) NOT IN (6,7)
AND Date NOT IN (SELECT Date FROM Table2)
GROUP BY YEAR(Date),MONTH(Date)
)
SELECT *
FROM cte
WHERE cte.EOMDate BETWEEN DATEADD(YEAR,-3,#CurrentDate) AND #CurrentDate;
This should work and give you the last working day for each month in your main table. Just filter by the desired time period:
SELECT TOP 1 WITH TIES FullDate
FROM Table1
WHERE FullDate NOT IN (SELECT Stat_Holidays FROM Table2) -- not holiday
AND DATEPART(weekday, FullDate) NOT IN (7, 1) -- not saturday and sunday
ORDER BY DENSE_RANK() OVER(PARTITION BY YEAR(FullDate), MONTH(FullDate) ORDER BY FullDate DESC)
Check this with your table name and column names.
select year(dates) _Year ,month(dates) _Month,EOMONTH(dates) endofMOnth from tabledate1 where DATENAME(DW, dates) not in ('Saturday','Sunday')
and EOMONTH(dates) not in (select holidaydate from tableholidays)
Group by year(dates),month(dates),EOMONTH(dates)
order by year(dates) ,month(dates)

How can I contstruct this T-SQL query involving missing date ranges?

I'll try to keep the specific details of my problem out of this question and focus only on the pertinent issues.
Lets say I have an Assets table with a primary key of AssetID.
I have another table called ProcessedDates with primary key PID and with additional columns AssetID, StartDate, EndDate.
I want to run a process for a list of assets between a start date and end date. Before I can run this process, I need to know which assets and which date ranges have already been processed.
For example, there are 2 entries in ProcessedDates:
AssetID StartDate EndDate
--------------------------
Asset1 Day4 day7
Asset1 Day10 Day12
I want to process Asset1 between day2 and day11. I don't need to waste time by processing on days that have already been done so in this example, I will only process asset1 from day2 to day3 and from day8 to day 9.
So what I need is a query that returns the gaps in the date ranges. In this case, the result set will be 2 lines:
AssetID StartDate EndDate
--------------------------
Asset1 day2 day3
Asset1 day8 day9
In my actual requirement I have many assetIDs. The ProcessedDates table may have multiple entries for each asset or none at all and each asset does not necessarily have the same processed dates as any other asset.
declare #StartDate date, #EndDate date (assume these are given)
--get distinct assets
select distinct AssetIDs from (some query) into #Assets
--get the already processed date ranges
select p.AssetID, p.StartDate, p.EndDate
from ProcessedDates p inner join #Assets a on p.AssetID = a.AssetID
where p.StartDate between #StartDate and #EndDate
or p.EndDate between #StartDate and #EndDate
From here I have no clue how to proceed. How do I get it to return AssetID, StartDate, EndDate for all the gaps in between?
Something like this:
declare #StartDate date = '2015-01-01', #EndDate date = '2015-05-05'
declare #Assets table (AssetID varchar(50), StartDate date, EndDate date)
declare #AssetTypes table (AssetID varchar(50))
insert into #AssetTypes values
('Asset1'),
('Asset2')
insert into #Assets values
('Asset1', '2014-12-10', '2014-12-31'), -- Ignored
('Asset1', '2015-02-02', '2015-03-02'),
('Asset1', '2015-03-05', '2015-05-01'),
('Asset1', '2015-06-01', '2015-06-06') -- Ignored
;WITH Base AS (
SELECT AT.AssetID
, CASE WHEN A.AssetID IS NULL THEN 1 ELSE 0 END EmptyAsset
, A.StartDate
, A.EndDate
, ROW_NUMBER() OVER (PARTITION BY AT.AssetID ORDER BY StartDate) RN
FROM #AssetTypes AT
LEFT JOIN #Assets A ON A.AssetID = AT.AssetID
WHERE A.AssetID IS NULL -- case of totally missing asset
OR (StartDate <= #EndDate AND EndDate >= #StartDate)
)
-- first missing range, before the first row
SELECT AssetID, #StartDate StartDate, DATEADD(dd, -1, StartDate) EndDate
FROM Base
WHERE RN = 1 AND StartDate > #StartDate
UNION ALL
-- each row joined with the next one
SELECT B1.AssetID, DATEADD(dd, 1, B1.EndDate), ISNULL(DATEADD(dd, -1, B2.StartDate), #EndDate)
FROM Base B1
LEFT JOIN Base B2 ON B2.AssetID = B1.AssetID AND B2.RN = B1.RN + 1
WHERE B1.EmptyAsset = 0
AND (B2.AssetID IS NULL -- Last row case
OR DATEADD(dd, 1, B1.EndDate) < B2.StartDate) -- Other rows case
AND B1.EndDate < #EndDate -- If the range ends after #EndDate, nothing to do
UNION ALL
-- case of totally missing asset
SELECT AssetID, #StartDate, #EndDate
FROM Base
WHERE EmptyAsset = 1
The main idea is that each row is joined with the next one. A new range is generated (if necessary) between the EndDate + 1 and the StartDate - 1. There is a special handling for the last row (B2.AssetID IS NULL and ISNULL(... #EndDate)). The first SELECT generated a row before the first range, and the last select is for the special case of no ranges present for an asset.
As I've written in the comments, it gets ugly quite quickly.
Here's an simple version to get the result you want. I use integer as date, and assume the min date is 0 and the max date is 999.
--DDL
create table Assets (AssetID integer, StartDate integer, EndDate integer);
insert into Assets values
(1,4,7),
(1,10,12),
(1,15,17),
(2,5,7),
(2,9,10);
with temp as(
select a1.AssetId,
a1.enddate+1 as StartDate,
coalesce(min(a2.startdate) - 1,999) as EndDate
from Assets a1
left join Assets a2
on a1.assetid = a2.assetid
and a1.enddate < a2.startdate
group by a1.assetid,a1.enddate
union all
select a.assetid,0,min(startdate) -1
from Assets a
group by a.assetid
)
select AssetId,
case when StartDate<2 then 2 else StartDate end as StartDate,
case when EndDate>11 then 11 else EndDate end as EndDate
from temp
where StartDate<=11 and EndDate>=2
order by AssetId,StartDate
The temp table can get the missing ranges. Then filter the match ranges between Day2 and Day11, will get the result that you want.
AssetId StartDate EndDate
1 2 3
1 8 9
2 2 4
2 8 8
2 11 11
Here's the SqlFiddle Demo

display list of dates by giving start and end date and get data against those dates

I have a sql table having three columns
id, balance, datetime
i want to get data from the table by giving time duration. suppose i want to get data between 1/1/2013 to 1/15/2013. data is given table is shown as:
#id Datetime Balance #
1 1/1/2013 1500
2 1/2/2013 2000
3 1/4/2013 1500
4 1/5/2013 2500
now I want the output as
#id Datetime Balance #
1 1/1/2013 1500
2 1/2/2013 2000
3 1/3/2013 0
4 1/4/2013 1500
5 1/5/2013 2500
i want to display all the dates and if there is no balance against the date. it shows O or null value
I would get rid of ID column as it is useless when you are adding additional rows and do something like this:
set dateformat mdy
declare #tmpTable table (dates date)
declare #startDate date = '1/1/2013'
declare #endDate date = '1/15/2013'
while #startDate <= #endDate
begin
insert into #tmpTable (dates) values (#startDate)
set #startDate = DATEADD(DAY, 1, #startDate)
end
select tmp.dates, yourtable.balance
from #tmpTable as tmp
left outer join yourTable on yourTable.[Datetime] = tmp.dates
where yourtable.[Datetime] between #startDate and #endDate
I'm not sure what flavor of SQL you're using but you can a table of all dates and to do an outer join to it. For example, if you have a table of all dates called 'Dates', then you could do:
select id, dateTime, balance from table1 t RIGHT JOIN Dates d on t.dateTime = d.dateTime
where t.dateTime BETWEEN '1/1/2013' AND '1/5/2013'
You can do it by creating a cte with the dates between your daterange and outer joining it with your table.
with cte as
(
select start_date dt
union all
select dateadd(dd,1,dt) from cte where dt < end_date
)
select id, cte.dt, balance from cte left outer join yourtable b on cte.dt = b.date;

Total number of Events on every date

I am stuck developing a query.
I have a table, structured like this:
[EventId] [Description] [EventName] [ValidFrom] [ValidTo] [Approved]
1 Sample1 1stEvent 2013-01-27 2013-05-10 1
2 Sample2 2stEvent 2013-04-07 2013-06-15 1
3 Sample3 3stEvent 2013-04-07 2013-06-15 1
4 Sample4 4stEvent 2013-03-02 2013-05-29 1
5 Sample5 5stEvent 2013-05-17 2013-07-10 1
6 Sample6 6stEvent 2013-03-20 2013-05-11 1
What i want is the total number of events for every date within a date range, inclusive.
Select distinct
Convert(varchar,ValidFrom,101)as [Date],
case
when count(EventID)>1 then Convert(nvarchar, count(EventID)) +' Events'
else Convert(nvarchar, count(EventID)) + ' Event'
end as CountOf,
Row_Number()
over (Order By Convert(varchar,ValidFrom,101)) as RowNumber
from [Table]
where Approved=1
group by Convert(varchar,ValidFrom,101)
This is the query I have come up with until now, but this shows the total number of events on a particular date without including the events which were continued as per the dates between valid from and valid to dates.
This code sample is not complete - you need to enter the fields you need to display and aggregate upon. It sounds like you're looking for a result that's between two dates, and you don't have that in your query. I'm not sure I comletely understand your question.
DECLARE #pStartDate DATE
DECLARE #pEndDate DATE
SET #pStartDate = [enter your start date for the date range]
SET #pEndDate = [enter your end date for the date range]
SELECT
COUNT(EventId),
ValidFrom,
ValidTo
FROM [Table]
WHERE
ValidFrom >= #pStartDate
AND ValidTo <= #pEndDate
AND Approved = 1
GROUP BY
ValidFrom,
ValidTo
This will do:
declare #dateFrom date
declare #dateTo date
SET #dateFrom = '20130101'
SET #dateTo = '20130501'
;with cte as(Select #dateFrom AS EveryDay
UNION ALL
Select dateadd(dd, 1, EveryDay) FROM cte WHERE EveryDay < #dateTo)
SELECT
EveryDay,
COUNT(DISTINCT [EventName]) AS NoEvents
from cte LEFT JOIN Table1 ON ValidFrom <= EveryDay AND ValidTo >= EveryDay
GROUP BY EveryDay
OPTION (MAXRECURSION 0)
SQL Fiddle
With the above Query i have mentioned gives the result like this :-
Date CountOf RowNumber
01/27/2013 2 Events 1
03/02/2013 1 Event 2
04/07/2013 2 Events 3
05/17/2013 1 Event 4
As you can see i am getting total events only on the base of particular date which is not desired.
i want to show the result as like in valid from =01/27/2013 in valid to 2013-05-10 which means in the case of Date =2013-03-02 the events should be incremented by one or the number of events which are active on that date,so the result for 2013-03-02 should be 6 events, i wish i can present a better picture but because of the complexity it made me blank :(
SELECT t1.EventId,t1.Description,t1.EventName,t1.ValidFrom,t1.ValidTo,t1.Approved,SUM(TotalEvent) FROM
(SELECT EventId,Description,EventName,ValidFrom,ValidTo,Approved,COUNT(EventId) as Total
FROM TABLE
WHERE Approved='1' AND ValidFrom >=(DATE) AND ValidTO<=(DATE)
GROUP BY EventId,Description,EventName,ValidFrom,ValidTo,Approved) AS t1
LEFT JOIN
(SELECT EventId, COUNT(EventId) as TotalEvent
FROM TABLE
WHERE ValidFrom >=(DATE) AND ValidTO<=(DATE)) AS t2
ON t1.EventId=t2.EventId
Group by t1.EventId,t1.Description,t1.EventName,t1.ValidFrom,t1.ValidTo,t1.Approved
Hope this help you.

MSSQL select count where condition is met across a date range

I have a table containing date, employeeID(int) , and ShiftWorked (can be night/day/weekend or evening) . There is a row for each employee and date combination
I would like to construct a query that gives me a count of how many people have worked a night shift in the week before and after each date in the roster period.
--------------------------------------------------------------------------
Date (yyyy-MM-dd) | CountOfNightshifts(for 1 week either side of date)
--------------------------------------------------------------------------
2012-1-1 | 8
2012-1-2 | 12
2012-1-3 | 11
2012-1-4 | 6
etc | etc
I hope this is clear. I have spent days trying to get this to work but I am not getting anywhere.
For example:
SELECT COUNT(id), [date]
FROM ROSTER
WHERE Shift = night AND [date] BETWEEN DATEADD(D,-7,[date]) AND DATEADD(d,7,[date])
GROUP by [date]
group by [date]
This will give me a list of dates and a count of nights on that particular day - not all night shifts in the 7 days before and after the date.
The following query will return two columns: the reference (roster) date and the number of (distinct) people that have worked on the night sift seven days before to seven days after the reference date.
SELECT tmain.date,
(
SELECT COUNT(DISTINCT taux.employeeId)
FROM roster taux
WHERE taux.shiftWorked = 'night'
AND taux.date >= DATEADD(DAY, -7, tmain.date)
AND taux.date <= DATEADD(DAY, 7, tmain.date)
) AS [number_of_distinct_people_with_night_shift]
FROM roster tmain
ORDER BY tmain.date;
Note 1: Usually I prefer joins over sub-queries, but I guess this solution is easier to read.
Note 2: I am assuming the time component of date values are irrelevant and all dates have the same time (i.e. '00:00:00.00'); if it is not the case, there are more adjustments to be done on the date comparison.
how about this?
SELECT
[date]
,count(*)
FROM
Shifts as s
WHERE
s.Date > DATEADD(day,-7,GETDATE())
AND ShiftWorked = 'Night'
GROUP BY
date
http://sqlfiddle.com/#!3/e88cc/1
a bit more data:
http://sqlfiddle.com/#!3/b7793/2
If you are only interested in a specific date then you could use:
DECLARE #target datetime
SET #target = GETDATE()
SELECT
count(*) as NightShifts
FROM
Shifts as s
WHERE
ShiftWorked = 'Night'
AND s.Date > DATEADD(day,-7,#target)
AND s.Date < DATEADD(day,7,#target)
http://sqlfiddle.com/#!3/b7793/20
but if you have another table that actually has the periods in it (e.g. billing or payroll dates):
DECLARE #target datetime
SET #target = GETDATE()
SELECT
p.periodDate
,count(*)
FROM
Shifts as s
INNER JOIN periods as p
ON s.date > dateadd(day,-7,p.periodDate)
AND s.date < dateadd(day,7,p.periodDate)
WHERE
ShiftWorked = 'Night'
GROUP BY p.periodDate
http://sqlfiddle.com/#!3/fc54d/2
OR to get ) when no night shift was worked:
SELECT
p.periodDate
,ISNULL(t.num,0) as nightShifts
FROM
periods as p
LEFT OUTER JOIN (
SELECT
p.periodDate
,count(*) as num
FROM
Shifts as s
INNER JOIN periods as P
ON s.date > dateadd(day,-7,p.periodDate)
AND s.date < dateadd(day,7,p.periodDate)
WHERE
ShiftWorked = 'Night'
GROUP BY p.periodDate
) as t
ON p.periodDate = t.periodDate
http://sqlfiddle.com/#!3/fc54d/11
You can pull it off by joining the ROSTER table to itself, thereby creating several result rows per employee and day. Otherwise your GROUP BY clause will group the resulting rows from the period you are after into the dates of the original table.
SELECT
r.[date],
COUNT(period.id)
FROM ROSTER r
JOIN ROSTER period
ON period.employeeID=r.employeeID
AND period.shift = night
AND r.[date] BETWEEN DATEADD(d,-7,period.[date]) and DATEADD(d,7,period.[date])
WHERE
r.shift = night
GROUP BY r.[date]