Showing dates by day wise, and having default date based on condition - sql

am attaching a query to bring dates to a field,
if system time is before 01:00 pm then it should bring today date+1 else if system date is after or equal 01:00 pm it should bring today date +2
further , user can see the query result that will show him a date range along side with day names , and select one of them manually if the above cases wasn't what the user is looking for .
queries are avilable and working fine but separately,
one query bring date range , and one query set the date based on a condition , i need to make them one query , that bring the date ranges and set default date to be based on the conditions;
here are the queries :
Note : Q No 2 , I tried to make both queries as one query but its not bring the correct result , it keep bring today date +1 no matter what system time is.
1)
If DATEPART(Hour,Getdate())<12
SELECT GetDate()+1
If DATEPART(Hour,Getdate())>=12
SELECT GetDate()+2
2)
If DATEPART(Hour,Getdate())<12
DECLARE #Date1 DATE, #Date2 DATE
SET #Date1 = GetDate()+0
SET #Date2 = GetDate()+365
SELECT DATEADD(DAY,number+1,#Date1) [Date], DateName(Weekday,DATEADD(DAY,number+1,#Date1)) as dayname
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2
If DATEPART(Hour,Getdate())>=12
DECLARE #Date3 DATE, #Date4 DATE
SET #Date3 = GetDate()+2
SET #Date4 = GetDate()+365
SELECT DATEADD(DAY,number+1,#Date3) [Date], DateName(Weekday,DATEADD(DAY,number+1,#Date3)) as dayname
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date3) < #Date4

I've used a CTE to calculate the start date. The CTE uses a CASE statement to tell if it is being run before or after 12.
WITH Base AS
(
/* Returns the start date based on the current time.
* Before midday the start date is today.
* After midday it is the day after tomorrow.
*/
SELECT
CASE
-- Use current time to calculate start date.
WHEN DATEPART(HOUR, GETDATE()) < 12 THEN DATEADD(DAY, 0, GETDATE())
ELSE DATEADD(DAY, 2, GETDATE())
END AS StartDate
)
SELECT
DATEADD(DAY, sv.Number + 1, b.StartDate) AS [Date],
DATENAME(WEEKDAY, DATEADD(DAY, sv.Number + 1, b.StartDate)) AS [DateName]
FROM
master..spt_values AS sv
CROSS JOIN Base AS b
WHERE
sv.[type] = 'P'
AND DATEADD(DAY, Number + 1, b.StartDate) < DATEADD(YEAR, 1, b.StartDate)
;

Related

Get first date of each month SQL Server 2008 R2

I need to generate first date of each month starting 01/01/2000 to 12/31/2099.
Issue: Below query works in Management Studio. However, it doesn't work in SSRS. Unfortunately, I cannot create a function or a view because I do not have access to do so.
Is there an alternative way to write this query that would work with SSRS? SSRS does not like use of "TOP" in my query.
Error related to "TOP":
A TOP or FETCH clause contains an invalid value.
declare #pStartDate date = '01/01/2000'
declare #pEndDate date = '12/31/2099'
;With months(DATE, MONTH, YEAR)
as(
SELECT TOP (DATEDIFF(MONTH, #pStartDate, #pEndDate)+1)
TheDate = DATEADD(MONTH, number, #pStartDate),
TheMonth = MONTH(DATEADD(MONTH, number, #pStartDate)),
TheYear = YEAR(DATEADD(MONTH, number, #pStartDate))
FROM [master].dbo.spt_values
WHERE [type] = N'P' ORDER BY number
)
select cast(DATE as Date) as Date from months
I just rearranged your code in a simpler and efficient way, without TOP clause:
declare #pStartDate date = '01/01/2000'
declare #pEndDate date = '12/31/2099'
SELECT DATEADD(MONTH, number, #pStartDate) as Date
FROM [master].dbo.spt_values
WHERE [type] = N'P'
AND number < DATEDIFF(MONTH, #pStartDate, #pEndDate)+1
ORDER BY number
When you add one month to a date it keeps the day. Day 30 and 31 are special cases, try it yourself.
Try this simple, recursive CTE to get what you want:
declare #pStartDate date = '01/01/2000'
declare #pEndDate date = '12/31/2099'
;with FirstDayOfMonth as(
select #pStartDate as [firstDayOfMonth]
union all
select DATEADD(month, 1, [firstDayOfMonth]) from FirstDayOfMonth
where DATEADD(month, 1, [firstDayOfMonth]) < #pEndDate
)
select * from FirstDayOfMonth
option (maxrecursion 0)

How to query 2 different date ranges depending on the day it is run

I am making a program that auto-runs using windows scheduler. What I'd like to do is set the program to run on the 1st and the 16th of every month. If the program run's on the 1st. I'd like to have the query run for last month... For example if today was the first of august I would want it to run 7/1/12 - 7/31/12. If I run the program on the 16th I want it to run the query for the current month to the 15th. For example if it were 8/16, I would want the program to run the query for 8/1/12 - 8/15/12. What is the best way to accomplish this? Do I go with 2 seperate programs with the query attaching it to the correct date range? One scheduled to run on the first of every month, and one on the 16th? How would I go about getting the date range and the year as it will depend on which month/year it is run... My query is:
SELECT Store_Number, Invoice_Number, Invoice_Date, Extended_Price, Warranty_Amount, Quantity_Sold, Invoice_Detail_Code
FROM Invoice_Detail_Tb
WHERE (Warranty_Amount > 0) AND (Invoice_Date BETWEEN CONVERT(DATETIME, '2012-08-01 00:00:00', 102) AND CONVERT(DATETIME, '2012-08-05 00:00:00', 102))
ORDER BY Store_Number, Invoice_Date
Try 8/1/2012 and 8/16/2012 as the date. It returns the values you want to see:
declare #date datetime = '8/16/2012', #start datetime, #end datetime
if datepart(dd, #date) = 1
begin
set #start = dateadd(mm, -1, #date)
set #end = dateadd(dd, -1, #date)
end
else
begin
set #start = dateadd(dd, -15, #date)
set #end = dateadd(dd, -1, #date)
end
select #start, #end
It would be fairly easy to adapt this so that it would dynamically calculate the correct start and end dates based on any input date -- so you could run it anytime during the month.
This should be simple, let me throw some examples for you.
I truly think this should be one scheduled task, not multiple ones.
It is easier at the end of the day to point and look at one scheduled task (one procedure)
then go digging up multiple procedures just to see what might have wen't wrong.
The task can be scheduled using the SQL Server Agent (under the jobs section). The job can point to one single stored procedure.
In the procedure you can do a simple if else if logic.
IF DAY(GetDate()) = 1
--code here
ELSE IF DAY(GETDATE()) = 16
--code here
DAY(date_expression) returns the day in a datetime column. Ironically there is a MONTH and YEAR function if you for some reason need those. The rest is simple, if you are on the first date of the month then perform the monthly query from months first date till next months first day - 1, this becomes:
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
Otherwise if it hits on the 16th, you can run on the first day until half of the month.
If you have your query in a view, you might use this:
where
Invoice_Date between
(
case
when datepart(dd, getdate()) = 1 then dateadd(mm, -1, getdate())
else dateadd(dd, -15, getdate())
end
)
and
(
case
when datepart(dd, getdate()) = 1 then dateadd(dd, -1, getdate())
else dateadd(dd, -1, getdate())
end
)
UPDATE: Ignoring the time
(I know it looks ugly.)
where
Invoice_Date between
(
case
when datepart(dd, dateadd(dd, datediff(dd, 0, getdate()), 0)) = 1 then dateadd(mm, -1, dateadd(dd, datediff(dd, 0, getdate()), 0))
else dateadd(dd, -15, dateadd(dd, datediff(dd, 0, getdate()), 0))
end
)
and
(
case
when datepart(dd, dateadd(dd, datediff(dd, 0, getdate()), 0)) = 1 then dateadd(dd, -1, dateadd(dd, datediff(dd, 0, getdate()), 0))
else dateadd(dd, -1, dateadd(dd, datediff(dd, 0, getdate()), 0))
end
)
This is how I usually do something like that. Your stored procedure should look something like this:
declare
#today datetime ,
#dtFrom datetime ,
#dtThru datetime
------------------------------------------------------
-- get the current date, discarding the time component
------------------------------------------------------
set #today = convert(datetime,convert(varchar,current_timestamp,112),112) -- get todays date, discarding the time component
---------------------------------------------------------------------------------------------------------------------------------------------------
-- determine the start/end dates of the query period.
--
-- if the query date (#today) is in the 1st half of the month (1st - 15th), the query range is the entire preceding month
-- if the query date (#today) is in the last half of the month (16 - 31st), the query range is the 1st of the current month up to the current date
---------------------------------------------------------------------------------------------------------------------------------------------------
if ( datepart(day) < 16 )
begin
set #dtThru = dateadd(day, - datepart(day, #today ) , #today ) -- set the end date to the last day of the previous month
set #dtFrom = dateadd(day, 1 - datepart(day, #dtThru ) , #dtThru ) -- set the start date to the first day of the previous month
end
else
begin
set #dtfrom = dateadd(day, 1 - datepart(day, #today) , #today ) -- set the start date to the first day of the current month
set #dtThru = #today
end
----------------------------------------------------------------------------------------------------------------------
-- finally, adjust the start/end times to cover the entire gamut of date/time values for the month
--
-- We don't have to modify #dtFrom at all: we know its time component is 00:00:00.000 already. However, we want
-- #dtThru to have a time component of 23:59:59.997, due to SQL Server's broken way of counting time -- any time value
-- higher than that (e.g., '23:59.59.999') is 'rounded up' to start-of-day (00:00.00.000), the next day. Brilliant!
--
----------------------------------------------------------------------------------------------------------------------
set #dtThru = dateadd(ms, -3 , dateadd(day,1,#dtThru) )
--------------------------------
-- return the data to the caller
--------------------------------
SELECT Store_Number ,
Invoice_Number ,
Invoice_Date ,
Extended_Price ,
Warranty_Amount ,
Quantity_Sold ,
Invoice_Detail_Code
FROM Invoice_Detail_Tb id
WHERE Warranty_Amount > 0
AND Invoice_Date BETWEEN #dtFrom AND #dtThru
ORDER BY Store_Number ,
Invoice_Date
If you aren't using a stored procedure, you can accomplish the same thing with a parameterized query. Compute the two DateTime values needed. Put placeholders in your select statement ('#dtFrom' and '#dtThru'). When you execute the query, pass in your two DateTime values as SqlParameter objects with names matching the placeholders.

Group days by week

Is there is a way to group dates by week of month in SQL Server?
For example
Week 2: 05/07/2012 - 05/13/2012
Week 3: 05/14/2012 - 05/20/2012
but with Sql server statement
I tried
SELECT SOMETHING,
datediff(wk, convert(varchar(6), getdate(), 112) + '01', getdate()) + 1 AS TIME_
FROM STATISTICS_
GROUP BY something, TIME_
ORDER BY TIME_
but it returns the week number of month. (means 3)
How to get the pair of days for current week ?
For example, now we are in third (3rd) week and I want to show 05/14/2012 - 05/20/2012
I solved somehow:
SELECT DATEADD(ww, DATEDIFF(ww,0,<my_column_name>), 0)
select DATEADD(ww, DATEDIFF(ww,0,<my_column_name>), 0)+6
Then I will get two days and I will concatenate them later.
All right, bear with me here. We're going to build a temporary calendar table that represents this month, including the days from before and after the month that fall into your definition of a week (Monday - Sunday). I do this in a lot of steps to try to make the process clear, but I probably haven't excelled at that in this case.
We can then generate the ranges for the different weeks, and you can join against your other tables using that.
SET DATEFIRST 7;
SET NOCOUNT ON;
DECLARE #today SMALLDATETIME, #fd SMALLDATETIME, #rc INT;
SELECT #today = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0), -- today
#fd = DATEADD(DAY, 1-DAY(#today), #today), -- first day of this month
#rc = DATEPART(DAY, DATEADD(DAY, -1, DATEADD(MONTH, 1, #fd)));-- days in month
DECLARE #thismonth TABLE (
[date] SMALLDATETIME,
[weekday] TINYINT,
[weeknumber] TINYINT
);
;WITH n(d) AS (
SELECT TOP (#rc+12) DATEADD(DAY, ROW_NUMBER() OVER
(ORDER BY [object_id]) - 7, #fd) FROM sys.all_objects
)
INSERT #thismonth([date], [weekday]) SELECT d, DATEPART(WEEKDAY, d) FROM n;
DELETE #thismonth WHERE [date] < (SELECT MIN([date]) FROM #thismonth WHERE [weekday] = 2)
OR [date] > (SELECT MAX([date]) FROM #thismonth WHERE [weekday] = 1);
;WITH x AS ( SELECT [date], weeknumber, rn = ((ROW_NUMBER() OVER
(ORDER BY [date])-1) / 7) + 1 FROM #thismonth ) UPDATE x SET weeknumber = rn;
-- now, the final query given all that (I've only broken this up to get rid of the vertical scrollbars):
;WITH ranges(w,s,e) AS (
SELECT weeknumber, MIN([date]), MAX([date]) FROM #thismonth GROUP BY weeknumber
)
SELECT [week] = CONVERT(CHAR(10), r.s, 120) + ' - ' + CONVERT(CHAR(10), r.e, 120)
--, SOMETHING , other columns from STATISTICS_?
FROM ranges AS r
-- LEFT OUTER JOIN dbo.STATISTICS_ AS s
-- ON s.TIME_ >= r.s AND s.TIME_ < DATEADD(DAY, 1, r.e)
-- comment this out if you want all the weeks from this month:
WHERE w = (SELECT weeknumber FROM #thismonth WHERE [date] = #today)
GROUP BY r.s, r.e --, SOMETHING
ORDER BY [week];
Results with WHERE clause:
week
-----------------------
2012-05-14 - 2012-05-20
Results without WHERE clause:
week
-----------------------
2012-04-30 - 2012-05-06
2012-05-07 - 2012-05-13
2012-05-14 - 2012-05-20
2012-05-21 - 2012-05-27
2012-05-28 - 2012-06-03
Note that I chose YYYY-MM-DD on purpose. You should avoid regional formatting like M/D/Y especially for input but also for display. No matter how targeted you think your audience is, you're always going to have someone who thinks 05/07/2012 is July 5th, not May 7th. With YYYY-MM-DD there is no ambiguity whatsoever.
Create a calendar table, then you can query week numbers, first/last days of specific weeks and months etc. You can also join on it queries to get a date range etc.
How about a case statement?
case when datepart(day, mydatetime) between 1 and 7 then 1
when datepart(day, mydatetime) between 8 and 14 then 2
...
You'll also have to include the year & month unless you want all the week 1s in the same group.
It's not clear of you want to "group dates by week of month", or alternately "select data from a given week"
If you mean "group" this little snippet should get you 'week of month':
SELECT <stuff>
FROM CP_STATISTICS
WHERE Month(<YOUR DATE COL>) = 5 --april
GROUP BY Year(<YOUR DATE COL>),
Month(<YOUR DATE COL>),
DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, <YOUR DATE COL>), 0)
, <YOUR DATE COL>) +1
Alternately, if you want "sales for week 1 of April, ordered by date" You could do something like..
DECLARE #targetDate datetime2 = '5/3/2012'
DECLARE #targetWeek int = DATEDIFF(week, DATEADD(MONTH,
DATEDIFF(MONTH, 0, #targetDate), 0), #targetDate) +1
SELECT <stuff>
FROM CP_STATISTICS
WHERE MONTH(#targetDate) = Month(myDateCol) AND
YEAR(#targetDate) = Year (myDateCol) AND
#targetWeek = DATEDIFF(week, DATEADD(MONTH,
DATEDIFF(MONTH, 0, myDateCol), 0), myDateCol) +1
ORDER BY myDateCol
Note, things would get more complicated if you use non-standard weeks, or want to reach a few days into an earlier month for weeks that straddle a month boundary.
EDIT 2
From looking at your 'solved now' section. I think your question is "how do I get data out of a table for a given week?"
Your solution appears to be:
DECLARE #targetDate datetime2 = '5/1/2012'
DECLARE #startDate datetime2 = DATEADD(ww, DATEDIFF(ww,0,targetDate), 0)
DECLARE #endDate datetime2 = DATEADD(ww, DATEDIFF(ww,0,#now), 0)+6
SELECT <stuff>
FROM STATISTICS_
WHERE dateStamp >= #startDate AND dateStamp <= #endDate
Notice how if the date is 5/1 this solution results in a start date of '4/30/2012'. I point this out because your solution crosses month boundaries. This may or may not be desirable.

What is a good way to find gaps in a set of datespans?

What is a way to find gaps in a set of date spans?
For example, I have these date spans:
1/ 1/11 - 1/10/11
1/13/11 - 1/15/11
1/20/11 - 1/30/11
Then I have a start and end date of 1/7/11 and 1/14/11.
I want to be able to tell that between 1/10/11 and 1/13/11 there is a gap so the start and end date is not possible. Or I want to return only the datespans up to the first gap encountered.
If this can be done in SQL server that would be good.
I was thinking to go through each date to find out if it lands in a datespan... if it does not then there's a gap on that day.
Jump to 2nd last code block for: *I want to be able to tell that
between 1/10/11 and 1/13/11 there is
a gap so the start and end date is*
not possible.
Jump to last code block for: *I want to return only
the datespans up to the first gap
encountered.*
First of all, here's a virtual table to discuss
create table spans (date1 datetime, date2 datetime);
insert into spans select '20110101', '20110110';
insert into spans select '20110113', '20110115';
insert into spans select '20110120', '20110130';
This is a query that will list, individually, all the dates in the calendar
declare #startdate datetime, #enddate datetime
select #startdate = '20110107', #enddate = '20110114'
select distinct a.date1+v.number
from spans A
inner join master..spt_values v
on v.type='P' and v.number between 0 and datediff(d, a.date1, a.date2)
-- we don't care about spans that don't intersect with our range
where A.date1 <= #enddate
and #startdate <= A.date2
Armed with this query, we can now test to see if there are any gaps, by
counting the days in the calendar against the expected number of days
declare #startdate datetime, #enddate datetime
select #startdate = '20110107', #enddate = '20110114'
select case when count(distinct a.date1+v.number)
= datediff(d,#startdate, #enddate) + 1
then 'No gaps' else 'Gap' end
from spans A
inner join master..spt_values v
on v.type='P' and v.number between 0 and datediff(d, a.date1, a.date2)
-- we don't care about spans that don't intersect with our range
where A.date1 <= #enddate
and #startdate <= A.date2
-- count only those dates within our range
and a.date1 + v.number between #startdate and #enddate
Another way to do this is to just build the calendar from #start
to #end up front and look to see if there is a span with this date
declare #startdate datetime, #enddate datetime
select #startdate = '20110107', #enddate = '20110114'
-- startdate+v.number is a day on the calendar
select #startdate + v.number
from master..spt_values v
where v.type='P' and v.number between 0
and datediff(d, #startdate, #enddate)
-- run the part above this line alone to see the calendar
-- the condition checks for dates that are not in any span (gap)
and not exists (
select *
from spans
where #startdate + v.number between date1 and date2)
The query returns ALL dates that are gaps in the date range #start - #end
A TOP 1 can be added to just see if there are gaps
To return all records that are before the gap, use the query as a
derived table in a larger query
declare #startdate datetime, #enddate datetime
select #startdate = '20110107', #enddate = '20110114'
select *
from spans
where date1 <= #enddate and #startdate <= date2 -- overlaps
and date2 < ( -- before the gap
select top 1 #startdate + v.number
from master..spt_values v
where v.type='P' and v.number between 0
and datediff(d, #startdate, #enddate)
and not exists (
select *
from spans
where #startdate + v.number between date1 and date2)
order by 1 ASC
)
Assuming MySQL, something like this would work:
select #olddate := null;
select start_date, end_date, datediff(end_date, #olddate) as diff, #olddate:=enddate
from table
order by start_date asc, end_date asc
having diff > 1;
Basically: cache the previous row's end_date in the #olddate variable, and then do a diff on that "old" value with the currel enddate. THe having clause will return only the records where the difference between two rows is greater than a day.
disclaimer: Haven't tested this, but the basic query construct should work.
I want to be able to tell that between
1/10/11 and 1/13/11 there is a gap so
the start and end date is not
possible.
I think you're asking this question: does the data in your table have a gap between the start date and the end date?
I created a one-column table, date_span, and inserted your date spans into it.
You can identify a gap by counting the number of days between start date and end date, and comparing that the the number of rows in date_span for the same range.
select
date '2011-01-14' - date '2011-01-07' + 1 as elapsed_days,
count(*) from date_span
where cal_date between '2011-01-07' and '2011-01-14';
returns
elapsed_days count
-- --
8 6
Since they're not equal, there's a gap in the table "date_span" between 2011-01-07 and 2011-01-14. I'll stop there for now, because I'm really not certain what you're trying to do.

Calculating in SQL the first working day of a given month

I have to calculate all the invoices which have been paid in the first 'N' days of a month. I have two tables
. INVOICE: it has the invoice information. The only field which does matter is called 'datePayment'
. HOLYDAYS: It is a one column table. Entries at this table are of the form "2009-01-01",
2009-05-01" and so on.
I should consider also Saturdays and Sundays
(this might be not a problem because I could insert those days at the Hollidays table in order to consider them as hollidays if neccesary)
The problem is to calculate which is the 'payment limit'.
select count(*) from invoice
where datePayment < PAYMENTLIMIT
My question is how to calculate this PAYMENTLIMIT. Where PAYMENTLIMIT is 'the fifth working day of every month'.
The query should be run under Mysql and Oracle therefore standard SQL should be used.
Any hint?
EDIT
In order to be consistent with the title of the question the pseudo-query should the read as follows:
select count(*) from invoice
where datePayment < FIRST_WORKING_DAY + N
then the question can be reduced to calculate the FIRST_WORKING_DAY of every month.
You could look for the first date in a month, where the date is not in the holiday table and the date is not a weekend:
select min(datePayment), datepart(mm, datePayment)
from invoices
where datepart(dw, datePayment) not in (1,7) --day of week
and not exists (select holiday from holidays where holiday = datePayment)
group by datepart(mm, datePayment) --monthnr
Something like this might work:
create function dbo.GetFirstWorkdayOfMonth(#Year INT, #Month INT)
returns DATETIME
as begin
declare #firstOfMonth VARCHAR(20)
SET #firstOfMonth = CAST(#Year AS VARCHAR(4)) + '-' + CAST(#Month AS VARCHAR) + '-01'
declare #currDate DATETIME
set #currDate = CAST(#firstOfMonth as DATETIME)
declare #weekday INT
set #weekday = DATEPART(weekday, #currdate)
-- 7 = saturday, 1 = sunday
while #weekday = 1 OR #weekday = 7
begin
set #currDate = DATEADD(DAY, 1, #currDate)
set #weekday = DATEPART(weekday, #currdate)
end
return #currdate
end
I'm not 100% sure about whether the "weekday" numbers are fixed or might depend on your locale on your SQL Server. Check it out!
Marc
Rather than a Holidays table of days to exclude, we use the calendar table approach: one row for every day the application will ever need (thirty years spans a modest 11K rows). So not only does it have an is_weekday column, it has other things relevant to the enterprise e.g. julianized_date. This way, every possible date would have a ready-prepared value for first_working_day_this_month and finding it involves a simple lookup (which SQL products tend to be optimized for!) rather than 'calculating' it each time on the fly.
We have dates table in our application (filled with all dates and date parts for some tens of years), what allows various "missing" date manipulations, like (in pseudo-sql):
select min(ourdates.datevalue)
from ourdates
where ourdates.year=<given year> and ourdates.month=<given month>
and ourdates.isworkday
and not exists (
select * from holidays
where holidays.datevalue=ourdates.datevalue
)
Ok, at a first stab, you could put the following code into a UDF and pass in the Year and Month as variables. It can then return TestDate which is the first working day of the month.
DECLARE #Month INT
DECLARE #Year INT
SELECT #Month = 5
SELECT #Year = 2009
DECLARE #FirstDate DATETIME
SELECT #FirstDate = CONVERT(varchar(4), #Year) + '-' + CONVERT(varchar(2), #Month) + '-' + '01 00:00:00.000'
DROP TABLE #HOLIDAYS
CREATE TABLE #HOLIDAYS (HOLIDAY DateTime)
INSERT INTO #HOLIDAYS VALUES('2009-01-01 00:00:00.000')
INSERT INTO #HOLIDAYS VALUES('2009-05-01 00:00:00.000')
DECLARE #DateFound BIT
SELECT #DateFound = 0
WHILE(#DateFound = 0)
BEGIN
IF(
DATEPART(dw, #FirstDate) = 1
OR
DATEPART(dw, #FirstDate) = 1
OR
EXISTS(SELECT * FROM #HOLIDAYS WHERE HOLIDAY = #FirstDate)
)
BEGIN
SET #FirstDate = DATEADD(dd, 1, #FirstDate)
END
ELSE
BEGIN
SET #DateFound = 1
END
END
SELECT #FirstDate
The things I don`t like with this solution though are, if your holidays table contains all days of the month there will be an infinite loop. (You could check the loop is still looking at the right month) It relies upon the dates being equal, eg all at time 00:00:00. Finally, the way I calculate the 1st of the month past in using string concatenation was a short cut. There are much better ways of finding the actual first day of the month.
Gets the first N working days of each month of year 2009:
select * from invoices as x
where
datePayment between '2009-01-01' and '2009-12-31'
and exists
(
select
1
from invoices
where
-- exclude holidays and sunday saturday...
(
datepart(dw, datePayment) not in (1,7) -- day of week
/*
-- Postgresql and Oracle have programmer-friendly IN clause
and
(datepart(yyyy,datePayment), datepart(mm,datePayment))
not in (select hyear, hday from holidays)
*/
-- this is the MSSQL equivalent of programmer-friendly IN
and
not exists
(
select * from holidays
where
hyear = datepart(yyyy,datePayment)
and hmonth = datepart(mm, datePayment)
)
)
-- ...exclude holidays and sunday saturday
-- get the month of x datePayment
and
(datepart(yyyy, datePayment) = datepart(yyyy, x.datePayment)
and datepart(mm, datePayment) = datepart(mm, x.datePayment))
group by
datepart(yyyy, datePayment), datepart(mm, datePayment)
having
x.datePayment < MIN(datePayment) + #N -- up to N working days
)
Returns the first Monday of the current month
SELECT DATEADD(
WEEK,
DATEDIFF( --x weeks between 1900-01-01 (Monday) and inner result
WEEK,
0, --1900-01-01
DATEADD( --inner result
DAY,
6 - DATEPART(DAY, GETDATE()),
GETDATE()
)
),
0 --1900-01-01 (Monday)
)
SELECT DATEADD(day, DATEDIFF (day, 0, DATEADD (month, DATEDIFF (month, 0, GETDATE()), 0) -1)/7*7 + 7, 0);
select if(weekday('yyyy-mm-01') < 5,'yyyy-mm-01',if(weekday('yyyy-mm-02') < 5,'yyyy-mm-02','yyyy-mm-03'))
Saturdays and Sundays are 5, 6 so you only need two checks to get the first working day