we are making a date range picker for employee vacations
for example i will user January 2016 as an example and our system goes from sunday through saturday
if an employee takes a vacation from January 14-20 they should be in the date range 10-16 and also 17-23
I just cant think of how to write an sql query that will limit it to those 2 dates.
What i have is:
DECLARE #WeekRangeStart DATETIME ='2016/01/10';
DECLARE #WeekRangeEnd DATETIME = '2016/01/16';
SELECT [ID],
[EmpName],
[EmpType]
FROM Vacations
WHERE VacationStartDate >= #WeekRangeStart OR VacationEndDate >= #WeekRangeStart
--OUTPUT
--ALL DAYS BEFORE THIS WOULD BE TRUE...
--1/14/2016 >= 1/10/2016 TRUE
--1/20/2016 >= 1/10/2016 TRUE
-- NEXT WEEK
--1/14/2016 >= 1/17/2016 FALSE
--1/20/2016 >= 1/17/2016 TRUE
-- NEXT WEEK
--1/14/2016 >= 1/24/2016 FALSE
--1/20/2016 >= 1/24/2016 FALSE
--ALL DAYS AFTER THIS DAY WOULD BE FALSE...
but this only works for for a things that have passed, But if i were to book a day in march i would always show on the schedules because my startdate would greater than today. How should i go about limiting it to that range only?
DECLARE #WeekRangeStart DATETIME ='2016/01/10';
DECLARE #WeekRangeEnd DATETIME = '2016/01/16';
SELECT [ID],
[EmpName],
[EmpType]
FROM Vacations
WHERE VacationStartDate between #WeekRangeStart and #WeekRangeEnd
or VacationEndDate Between #WeekRangeStart and #WeekRangeEnd
This might be useful. Using a recursive CTE I got all the dates the vacations spans. Then return the weeks it spans based on the week ranges provided.
DECLARE #WeekRangeStart DATETIME ='2016/01/10';
DECLARE #WeekRangeEnd DATETIME = '2016/01/16';
DECLARE #VacationStartDate DATETIME = '2016-01-14'
DECLARE #VacationEndDate DATETIME = '2016-01-20'
;WITH cte AS
(
SELECT #VacationStartDate AS Dates
UNION ALL
SELECT DATEADD(dd, 1, dates)
FROM cte
WHERE dates < #VacationEndDate
)
SELECT DISTINCT
DATEADD(dd, -(DATEPART(dw, dates)-1), dates) AS WeekStartDate,
DATEADD(dd, 7-(DATEPART(dw, dates)), dates) AS WeekEndDate
FROM cte
WHERE dates BETWEEN #WeekRangeStart AND #WeekRangeEnd
Related
I have a query that converts week and year to date.
But it returns the exact date.
But what i want is, I need the date to be such a day, that is same as the first day of the year.
dateadd (week, PromisedWeek-1, dateadd (year, PromisedYear-1900, 0)) - 4 -
datepart(dw, dateadd (week, PromisedWeek-1, dateadd (year, PromisedYear-1900, 0)) - 4) + 1
Hypothetical example.
My current query does is:
If week is 4 and year 2017, returns 26-Sun-2017
My need:
If week is 4 and year 2017 and January 1st was a Wednesday, its should return 29-Wednesday-2017.
Hoping that you guys get what I am trying to explain.
I need the query to return such a date which has the same day as that of the current year's 1st day.
Unless I'm reading this wrong, you're making things too hard on yourself. Just do this:
declare #year int; set #year = 2017;
declare #week int; set #week = 4;
select dateadd(week, #week, dateadd(year, #year - 1900, 0))
Result:
2017-01-29 00:00:00.000
The inner dateadd() gives you the first day of the requested year (Jan 1), regardless of weekday. If it's a Wednesday, you'll get a Wednesday. This year, it's Sunday. The outer dateadd() then adds full weeks to that, so you end up with the same day of the week, just like you asked.
If that's not what you want, please explain how it needs to be more complicated.
This is pretty easy if you have a calendar table. A calendar table is a table that contains every date and its parts for a given range. I created mine for 2010 to 2020 with this code:
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 you can query for the correct weekday in the PromisedWeek
declare #PromisedYear as numeric
declare #PromisedWeek as int
set #PromisedYear = 2017
set #PromisedWeek = 4
select concat(date_day, '-', weekday_nm, '-', #PromisedYear)
from Calendar as c
where
weekday_id =
(select weekday_ID
from Calendar
where date_year = #PromisedYear
and day_of_year = 1
)
and datepart(week, date_id) = #PromisedWeek
and date_year = #PromisedYear
Turns out that January 1st 2017 was a Sunday and the 4th Sunday in 2017 was January 22nd, so the return is 22-Sunday-2017
I have a query for calculating first and last date in the week, according to given date. It is enough to set #dDate and the query will calculate first (monday) and last date (sunday) for that week.
Problem is, that is calculating wrong and I don't understand why.
Example:
#dDate = 2019-10-03 (year-month-day).
Result:
W_START W_END
2019-09-25 2019-10-01
But it should be:
2019-09-30 2019-10-06
Why is that?
Query:
set datefirst 1
declare #dDate date = cast('2019-10-16' as date)
select #dDAte
declare #year int = (select DATEPART(year, #dDAte))
select #year
declare #StartingDate date = cast(('' + cast(#year as nvarchar(4)) + '-01-01') as date)
select #StartingDate
declare #dateWeekEnd date = (select DATEADD(week, (datepart(week, cast(#dDate as date)) - 1), #StartingDate))
declare #dateWeekStart date = dateadd(day, -6, #dateWeekEnd)
select #dateWeekStart W_START, #dateWeekEnd W_END
Days of the week are so complicated. I find it easier to remember that 2001-01-01 fell on a Monday.
Then, the following date arithmetic does what you want:
select dateadd(day,
7 * (datediff(day, '2001-01-01', #dDate) / 7),
'2001-01-01' -- 2001-01-01 fell on a Monday
)
I admit this is something of a cop-out/hack. But SQL Server -- and other databases -- make such date arithmetic so cumbersome that simple tricks like this are handy to keep in mind.
I have some reports which run showing data MTD
Here is the code that is not working how I would like
StartDate = select dateadd(s,0,dateadd(mm, datediff(m,0,getdate()),0))
EndDate = getdate()
Our data replication happens at the end of each day.
So on the First day of each month I don't want a blank report to run.
what I would like to happen.
Only If its the first day of the month then the StartDate must be beginning of last month and EndDate to be end of last month. Else use
StartDate = select dateadd(s,0,dateadd(mm, datediff(m,0,getdate()),0)) and
EndDate = getdate()
Not exactly sure about your main query. This is how you could get first and last day of last month depending on the given date being 1st of current month.
Please note, else part of each case expression setting the current date for both first and last date. You can set them as null if needed.
DECLARE #Today DATETIME = GETDATE()
DECLARE #FirsDay DATETIME = CASE WHEN DATEPART(DAY, #Today) = 1
THEN DATEADD(MONTH, -1, #Today) --first day of last month
ELSE #Today END --current date for other dates
DECLARE #LastDay DATETIME = CASE WHEN DATEPART(DAY, #Today) = 1
THEN DATEADD(DAY, -1, #Today) --last date of last month
ELSE #Today END --current date for other dates
Thanks all.
so the rabbit hole got a bit deeper
Example : What about when the 1st falls on a Saturday ?
I need it to run using the last trading day if its the first day of the new month.
what I ended up using was a function that uses our working hours table
ALTER FUNCTION [data].[Last_Trade_Day] (#Date date)
returns date as
begin
declare #OrigDate date = isnull(#Date, getdate())
return (
select max(convert(date, wh_starttime))
from Embrace.fact.Working_Hours
where convert(date, wh_starttime) < #OrigDate
)
end
The code in the report now look's like :
declare #EndDate date = Embrace.data.Last_Trade_Day(isnull(#Date, getdate()))
declare #StartDate date = dateadd(mm, 0, dateadd(mm, datediff(m, 0, #EndDate), 0))
You can try to use something like this:
-- Create demo data
CREATE TABLE #dates(get_date_simulation datetime)
INSERT INTO #dates(get_date_simulation)
VALUES (N'2015-07-01 13:46:47.063'), -- fallback to 2015-06-01
(N'2015-07-02 13:46:47.063') -- use this date normal
-- Your part
SELECT get_date_simulation,
CASE
WHEN DATEPART(day,get_date_simulation) = 1
THEN DATEADD(month,-1,DATEADD(day,(DATEPART(day,get_date_simulation)-1)*-1,get_date_simulation))
ELSE DATEADD(day,(DATEPART(day,get_date_simulation)-1)*-1, get_date_simulation)
END as start_date,
CASE
WHEN DATEPART(day,get_date_simulation) = 1
THEN DATEADD(second,-1,CONVERT(datetime,CONVERT(date,get_date_simulation)))
ELSE get_date_simulation
END as end_date
FROM #dates
-- Cleanup
DROP TABLE #dates
Which results into this:
get_date_simulation start_date end_date
----------------------- ----------------------- -----------------------
2015-07-01 13:46:47.063 2015-06-01 13:46:47.063 2015-06-30 23:59:59.000
2015-07-02 13:46:47.063 2015-07-01 13:46:47.063 2015-07-02 13:46:47.063
To me it seems as simple as subtracting 1 day from the current date to get the start date
SELECT StartDate =
DATEADD(s,0,DATEADD(mm,DATEDIFF(m,0,GETDATE() - 1),0))
Then you just get the end date using the current date without the time
SELECT EndDate =
CONVERT(DATE, GETDATE()
Then your query is WHERE date >= StartDate and < EndDate
I am trying to get data from my Database of those who have upcoming birth days in next few days(declared earlier)
it's working fine for days but this query will not work if i add 24 days to current date cause than it will need change in month.
i wonder how can i do it
declare #date int=10,
#month int=0
select * from STUDENT_INFO where DATEPART(DD,STDNT_DOB) between
DATEPART(DD,GETDATE()) and DATEPART(DD,DATEADD(DD,#date,GETDATE()))
and
DATEPART(MM,STDNT_DOB) = DATEPART(MM,DATEADD(MM,#month,GETDATE()))
This query works fine but it only checks date between 8 & 18
but if i use it like this
declare #date int=30,
#month int=0
select * from STUDENT_INFO where DATEPART(DD,STDNT_DOB) between
DATEPART(DD,GETDATE()) and DATEPART(DD,DATEADD(DD,#date,GETDATE()))
and
DATEPART(MM,STDNT_DOB) = DATEPART(MM,DATEADD(MM,#month,GETDATE()))
it will return nothing since it require addition in month as well
If I Use it like this
declare #date int=40,
#month int=0
select * from STUDENT_INFO where DATEPART(DD,STDNT_DOB) between
DATEPART(DD,GETDATE()) and DATEADD(DD,#date,GETDATE())
and
DATEPART(MM,STDNT_DOB) = DATEPART(MM,DATEADD(MM,#month,GETDATE()))
than it will return results till the last of this month but will not show till 18/12 which was required
Here is a simple way to solve it:
DECLARE #date int = 10
;WITH cte as
(
SELECT cast(getdate() as date) fromdate,
cast(getdate() as date) todate,
1 loop
UNION ALL
SELECT cast(dateadd(year, -loop, getdate()) as date),
cast(dateadd(year, -loop, getdate())+#date as date),
loop+1
FROM cte
WHERE loop < 200 -- go back 200 years
-- (should be enough unless you are a turtle)
)
SELECT dob, name, datediff(year, dob, getdate()) will_turn
FROM cte
JOIN (values(cast('1968-11-11' as date), 'Jack'),
(cast('1984-11-12' as date), 'Jill'),
(cast('1984-11-13' as date), 'Hans'),
(cast('1984-11-21' as date), 'Gretchen'),
(cast('1884-11-22' as date), 'Snowwhite')) x(dob, name)
ON dob BETWEEN fromdate and todate
OPTION (maxrecursion 300)
Returns:
dob name name will_turn
1984-11-12 Jill 29
1984-11-13 Hans 29
1984-11-21 Gretchen 29
1968-11-11 Jack 45
avoiding udf:-
create table #dob (
name varchar(50),
cakeday date
)
go
insert into #dob values
('yesterday',dateadd(yy,-50,getdate()-1)),
('today',dateadd(yy,-51,getdate())),
('ten days',dateadd(yy,-52,getdate()+10)),
('eleven days',dateadd(yy,-53,getdate()+11))
go
select *
from #dob d
where datepart(dayofyear,d.cakeday)
between datepart(dayofyear,getdate()) and datepart(dayofyear,getdate())+10
returns (on 2013-11-09):-
name cakeday
today 1962-11-09
ten days 1961-11-19
Would this work for you?
declare #date int = 10
select * from STUDENT_INFO
where DATEDIFF(DD, GETDATE(),
DATEFROMPARTS(DATEPART(YYYY, GETDATE()), -- this years birthday
DATEPART(MM, STDNT_DOB),
DATEPART(DD, STDNT_DOB))) between 0 and #date
or DATEDIFF(DD, GETDATE(),
DATEFROMPARTS(DATEPART(YYYY, GETDATE()) + 1, -- next years birthday
DATEPART(MM, STDNT_DOB),
DATEPART(DD, STDNT_DOB))) between 0 and #date
The trick here is to get the students birthday this year (and next year) and get the number of days until that date. I did that by creating a calculated field using the students day and month of birth, and included todays year.
The check against next years birthday (i.e., the second part of the OR clause) is required to get those students whose upcoming birthday is early next year.
Edit: the query will fail if anyone on the table was born on feb/29 (on a leap year, of course). Thanks to t-clausen.dk for poiting it out.
I have a table like this:
Month Value
2012-08-01 0.345
2012-09-01 0.543
2012-10-01 0.321
2012-11-01 0.234
2012-12-01 0.234
User inputs week range from '2012-09-29' to '2012-10-13'
Output should show results for all weeks in requested range and average values for each week with the following logic:
- if all weekdays are entirely in one month, just use monthly value for that month
- if weekdays are spread out over two months, calculate weekly value as average between those two months giving preference to the month that contains the most days of that week.
If someone can give me an idea how to do something like this in T-SQL that would be highly appreciated.
The last query is the example. The Calendar table is built on-request, but every database can do with a persisted Calendar table, on which you would filter the date range instead.
declare #tbl table (
Month datetime,
Value decimal(10,3));
insert #tbl select
'2012-08-01', 0.345 union all select
'2012-09-01', 0.543 union all select
'2012-10-01', 0.321 union all select
'2012-11-01', 0.234 union all select
'2012-12-01', 0.234;
declare #start datetime, #end datetime;
select #start = '2012-09-29', #end ='2012-10-13';
;with Calendar(TheDate,StartOfWeek,StartOfMonth) as(
select #start, #start+1-Datepart(dw,#start), #start-Day(#start)+1
union all
select TheDate+1, TheDate+1+1-Datepart(dw,TheDate+1),
TheDate+1-Day(TheDate+1)+1
from Calendar
where TheDate < #end
)
select case when #start > v.StartOfWeek
then #start else v.StartOfWeek end RangeStart,
case when #end < v.StartOfWeek+6
then #end else v.StartOfWeek+6 end RangeEnd,
cast(avg(m.value) as decimal(10,3)) [Average]
from Calendar v
join #tbl m on v.StartOfMonth = m.Month
group by v.StartOfWeek;
Output
RANGESTART RANGEEND Average
September, 29 2012 September, 29 2012 0.543
September, 30 2012 October, 06 2012 0.353
October, 07 2012 October, 13 2012 0.321
Your query would be something like this. The idea is find the first day of the next month DATEADD(mm, DATEDIFF(mm, 0, Month) + 1, 0. Calculate the number of days, and then you can get the monthly total, and calculate the average based on the difference between the first day of the current month and next month. (SQL syntax may need some clean up).
declare #startdate datetime
declare #enddate datetime
set #startdate = '2012-09-05'
set #enddate ='2012-10-13'
Select Monthtotal/DateDiff(d,Month,NextMonth)
FROM
(Select
Month, DATEADD(mm, DATEDIFF(mm, 0, Month) + 1, 0) NextMonth,
DateDiff(d,Month, DATEADD(mm, DATEDIFF(mm, 0, Month) + 1, 0) * Value as Monthtotal
FROM DatesTable
WHERE
#startdate >= Month and
#enddate <= DATEADD(mm, DATEDIFF(mm, 0, Month) + 1, 0))