I have a transaction based table in SQL from which I am creating an SSRS report. I have a filter based on transaction dates, like below:
Label 7-13 value 1
Label 14-28 value 2
Label 7-28 value 3
If the user selects 1 then I need to show all the transactions which happen between 7-13 days from today. And same for others. Please advise me how I do this at the query level.
Here's an approach that includes sample data:
declare #value int
select #value = 1
;with cte as
(
select cast(getdate() as date) dt
union all
select dateadd(day, -1, dt)
from cte
where dt > getdate() - 30
)
select *
from cte
where (#value = 1 and dt between DATEADD(day, -13, cast(getdate() as date)) and DATEADD(day, -7, cast(getdate() as date)))
or (#value = 2 and dt between DATEADD(day, -28, cast(getdate() as date)) and DATEADD(day, -14, cast(getdate() as date)))
or (#value = 3 and dt between DATEADD(day, -28, cast(getdate() as date)) and DATEADD(day, -7, cast(getdate() as date)))
The key is in the where clause:
WHERE (#value = 1 AND [do this if #value = 1])
OR (#value = 2 AND [do this if #value = 2])
etc.
Related
When I call this with these parameters(#MediaSellerID = 138, #StartDate = N'9/1/2020', #EndDate = N'10/7/2020') it returns data successfully.
But when I passed these parameters(#MediaSellerID = 213,#StartDate = '8/4/2020',#EndDate = N'11/25/2020')
this error occurs.
Msg 530, Level 16, State 1, Procedure GetAvailableDatesforCampaign, Line 81 [Batch Start Line 2]
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
If anyone has this solution (OPTION(MAXRECURSION 0)) then please also mention this statement place. thanks
ALTER Procedure [dbo].[GetAvailableDatesforCampaign]
-- exec GetAvailableDatesforCampaign 2,getdate(),getdate()
#MediaSellerID int,
#StartDate datetime,
#EndDate datetime
AS
BEGIN
SET FMTONLY OFF
Declare #Frequency as varchar(200)
Declare #Weeks as varchar(200)
Declare #Days as varchar(200)
Declare #CopyDeadlineDays as int
Declare #DayItem as varchar(20)
Declare #WeekItem as varchar(200)
Declare #DayNumber as int
Delete from tblCampaignDates
select
#Frequency=MediaFrequency,
#Weeks=BroadcastDays,
#Days=BroadcastDaysWeeks,
#CopyDeadlineDays=Copydeadline
from
tblMediaSeller where MediaSeller_ID=#MediaSellerID
CREATE TABLE
#TempAvailableDates
(
AvailableDate datetime default GetDate()
)
CREATE TABLE
#TempAllDates
(
AllDate datetime,
DayNames varchar(20),
Weeknumber int
)
IF(#Frequency='Bi-Weekly' or #Frequency='Weekly' or #Frequency='Monthly')
BEGIN
DECLARE MY_CURSOR2 CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT DISTINCT
Item
FROM
dbo.SplitString(#Days)
OPTION (MAXRECURSION 0)
OPEN MY_CURSOR2
FETCH NEXT FROM MY_CURSOR2 INTO #DayItem
WHILE ##FETCH_STATUS = 0
BEGIN
-- Select All dates of those days in tblMediaSeller (Adv Publishing day) from start date to End dates
Set #DayNumber= CASE WHEN #DayItem='Monday' THEN 1
WHEN #DayItem='Tuesday' THEN 2
WHEN #DayItem='Wednesday' THEN 3
WHEN #DayItem='Thursday' THEN 4
WHEN #DayItem='Friday' THEN 5
WHEN #DayItem='Saturday' THEN 6
WHEN #DayItem='Sunday' THEN 7
ELSE 0 END
IF(#DayNumber<>0)
BEGIN
SET DATEFIRST #DayNumber; -- First day of the week is set to monday
WITH CTE(dt)
AS
(
SELECT #StartDate
UNION ALL
SELECT DATEADD(d, 1, dt)
FROM CTE
WHERE dt <#EndDate
)
INSERT INTO
#TempAllDates
SELECT
CONVERT(date,dt),
datename(dw, dt),
DATEDIFF(WEEK, DATEADD(MONTH, DATEDIFF(MONTH, 0, dt), 0), dt)
FROM
CTE
WHERE
datepart ("dw", dt) = 1;
END
FETCH NEXT FROM MY_CURSOR2 INTO #DayItem
END
CLOSE MY_CURSOR2
DEALLOCATE MY_CURSOR2
Set #WeekItem= REPLACE(#Weeks, 'Week', '');
DECLARE #ActualStartDate as Datetime
Set #ActualStartDate = (Select top 1 AllDate from #TempAllDates)
IF(#CopyDeadlineDays is null)
BEGIN
IF(#ActualStartDate>= DATEADD(day,5,getdate()))
BEGIN
Insert into
#TempAvailableDates
Select
AllDate
From
#TempAllDates
WHERE
Weeknumber in (select cast(item as int) from dbo.SplitString(#WeekItem))
END
ELSE
BEGIN
Insert into
#TempAvailableDates
Select
AllDate
From
#TempAllDates
WHERE
Weeknumber in (select cast(item as int) from dbo.SplitString(#WeekItem))
AND AllDate>=DATEADD(day,5,getdate())
END
END
ELSE
BEGIN
IF(#ActualStartDate>= DATEADD(day,#CopyDeadlineDays,getdate()))
BEGIN
Insert into
#TempAvailableDates
Select
AllDate
From
#TempAllDates
WHERE
Weeknumber in (select cast(item as int) from dbo.SplitString(#WeekItem))
END
ELSE
BEGIN
Insert into
#TempAvailableDates
Select
AllDate
From
#TempAllDates
WHERE
Weeknumber in (select cast(item as int) from dbo.SplitString(#WeekItem))
AND AllDate>=DATEADD(day,#CopyDeadlineDays,getdate())
END
END
END
ELSE
BEGIN
IF(#CopyDeadlineDays is null)
begin
IF(#StartDate>= DATEADD(day,5,getdate()))
BEGIN
Insert into
#TempAvailableDates
SELECT TOP
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b;
END
ELSE
BEGIN
SET #StartDate=#StartDate+5
Insert into
#TempAvailableDates
SELECT TOP
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b;
END
END
ELSE
BEGIN
IF(#StartDate>= DATEADD(day,#CopyDeadlineDays,getdate()))
BEGIN
Insert into
#TempAvailableDates
SELECT TOP
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b;
END
ELSE
BEGIN
SET #StartDate=#StartDate+#CopyDeadlineDays
Insert into
#TempAvailableDates
SELECT TOP
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #StartDate)
FROM
sys.all_objects a
CROSS JOIN
sys.all_objects b;
END
END
END
Insert into
tblCampaignDates
Select
AvailableDate
from #TempAvailableDates
Select AvailableDate from tblCampaignDates
END
This should work:
WITH CTE(dt) AS (
SELECT #StartDate
UNION ALL
SELECT DATEADD(d, 1, dt)
FROM CTE
WHERE dt <#EndDate
)
INSERT INTO #TempAllDates
SELECT CONVERT(date,dt), datename(dw, dt), DATEDIFF(WEEK, DATEADD(MONTH, DATEDIFF(MONTH, 0, dt), 0), dt)
FROM CTE
WHERE datepart(dw, dt) = 1
OPTION (MAXRECURSION 0);
Here is a db<>fiddle with a much simpler example.
I'm trying to write a function to return the number of working days between 2 dates. I have a table with all the bank holidays in which has two fields: BHID and BHDate.
I've written the following SELECT query to test out my theory and it works nicely:
DECLARE #StartDate AS Date, #EndDate AS Date
SET #StartDate = '2015-01-01'
SET #EndDate = '2015-07-01'
;WITH CTE AS (
SELECT #StartDate AS StartDate, #EndDate AS EndDate, #StartDate AS DateCalc
UNION ALL
SELECT #StartDate AS StartDate, #EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
WHERE DATEADD(dd, 1, DateCalc) <= EndDate)
SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S'
option (Maxrecursion 0);
But when I try and put it in a function I get some errors around the SET line, it could be me just not being quite as good on functions as the rest of my knowledge or I was wondering whether it was to do with the CTE not having been used ahead of the SET line? Here's my attempt:
CREATE FUNCTION [dbo].[WorkingDays]
(#StartDate AS Date, #EndDate AS Date
)
RETURNS INT
AS
BEGIN
DECLARE #Days AS INT
;WITH CTE AS (
SELECT #StartDate AS StartDate, #EndDate AS EndDate, #StartDate AS DateCalc
UNION ALL
SELECT #StartDate AS StartDate, #EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
WHERE DATEADD(dd, 1, DateCalc) <= EndDate)
SET #Days =
(SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S'
option (Maxrecursion 0);)
RETURN #Days
END
I even tried inserting the result of the INT into a temporary table and then dropping this after setting #Days before then returning #Days but this then results in an error regarding not being allowed to produce temporary tables as part of a function.
Any help would be great, I'm sure it's a small thing but just escaping me currently.
Because a CTE can only be used in a query, rather than using SET to set the variable, use SELECT instead:
SELECT #Days =
(SELECT COUNT(*) AS Days
FROM CTE …
From MSDN:
A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE
statement that references some or all the CTE columns.
Worked it out with stuartd's help:
CREATE FUNCTION [dbo].[WorkingDays]
(#StartDate AS Date, #EndDate AS Date
)
RETURNS INT
AS
BEGIN
DECLARE #Days AS INT
;WITH CTE AS (
SELECT #StartDate AS StartDate, #EndDate AS EndDate, #StartDate AS DateCalc
UNION ALL
SELECT #StartDate AS StartDate, #EndDate AS EndDate, DATEADD(dd, 1, DateCalc) AS DateCalc FROM CTE
WHERE DATEADD(dd, 1, DateCalc) <= EndDate)
SELECT #Days =
(SELECT COUNT(*) AS Days
FROM CTE
LEFT JOIN psd.dbo.LUBankHolidays BH
ON CTE.DateCalc = BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, DateCalc) ,1) <> 'S')
option (Maxrecursion 0);
RETURN #Days
END
Thanks All
Use this function
CREATE FUNCTION [dbo].[WorkingDays]
(#Start_Date AS Date, #end_Date AS Date
)
RETURNS INT
AS
BEGIN
DECLARE #Days AS INT
WITH AllDays
AS ( SELECT #start_date AS [Date], 1 AS [level]
UNION ALL
SELECT DATEADD(DAY, 1, [Date]), [level] + 1
FROM AllDays
WHERE [Date] < #end_date )
SELECT #Days =COUNT(*) AS Days
FROM AllDays
LEFT JOIN psd.dbo.LUBankHolidays BH
ON AllDays.Date= BH.BHDate
WHERE BHID IS NULL
AND LEFT(DATENAME(dw, AllDays.Date) ,1) <> 'S'
RETURN #Days
END
I am trying to get the date difference in a given date excluding the week days.
Here is what I have:
SELECT DATEADD (w, -4, GETDATE())
This returns 2013-05-04 19:01:53.170, which means that it also counts weekends.
Same for
SELECT DATEADD (dw, -4, GETDATE())
Any help will be appreciated.
Thanks in advance.
I'm using these functions that return the non-weekend seconds between two dates:
CREATE FUNCTION [dbo].[DateDiff_NoWeekends](
#date1 DATETIME,
#date2 DATETIME
)
RETURNS INT AS BEGIN
DECLARE #retValue INT
SET #date1 = dbo.__CorrectDate(#date1, 1)
SET #date2 = dbo.__CorrectDate(#date2, 0)
IF (#date1 >= #date2)
SET #retValue = 0
ELSE BEGIN
DECLARE #days INT, #weekday INT
SET #days = DATEDIFF(d, #date1, #date2)
SET #weekday = DATEPART(dw, #date1) - 1
SET #retValue = DATEDIFF(s, #date1, #date2) - 2 * 24 * 3600 * ((#days + #weekday) / 7)
END
RETURN #retValue
END
GO
CREATE FUNCTION [dbo].[__CorrectDate](
#date DATETIME,
#forward INT
)
RETURNS DATETIME AS BEGIN
IF (DATEPART(dw, #date) > 5) BEGIN
IF (#forward = 1) BEGIN
SET #date = #date + (8 - DATEPART(dw, #date))
SET #date = DateAdd(Hour, (8 - DatePart(Hour, #date)), #date)
END ELSE BEGIN
SET #date = #date - (DATEPART(dw, #date)- 5)
SET #date = DateAdd(Hour, (18 - DatePart(Hour, #date)), #date)
END
SET #date = DateAdd(Minute, -DatePart(Minute, #date), #date)
SET #date = DateAdd(Second, -DatePart(Second, #date), #date)
END
RETURN #date
END
Here's a sql-fiddle demo for all non-weekend days in april (22).
SELECT [no weekend days in april] =
(dbo.DateDiff_NoWeekends('2013-04-01','2013-05-01')
/ 3600 / 24)
The query below gives the difference for week days alone , Ie counts the no od days between two days and subtracts the no of weekend days ,
DECLARE #StartDate DATETIME,
#EndDate DATETIME
SELECT #StartDate = '01-July-2008',
#EndDate = '30-July-2008'
;WITH DATE (Date1)
AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, '19000101', #StartDate), '19000101')
UNION ALL
SELECT DATEADD(DAY, 1, Date1)
FROM DATE
WHERE Date1 < #EndDate
)
SELECT count(*) -
(
SELECT count(*)
--CONVERT(VARCHAR(15),d1.DATE1 ,110) as [Working Date],
--DATENAME(weekday, d1.Date1) [Working Day]
from DATE d1 where (DATENAME(weekday, d1.Date1)) in ('Saturday','Sunday')
)
--CONVERT(VARCHAR(15),d1.DATE1 ,110) as [Working Date],
--DATENAME(weekday, d1.Date1) [Working Day]
from DATE d1 where (DATENAME(weekday, d1.Date1)) not in ('Saturday','Sunday')
please let me know for any clarifications
Maybe I am still missing some full testing, but this works for me too: take the difference in days and then subtract 2 days for each weekend
DateDiff(d, d1, d2) - 2*DateDiff(wk, d1, d2)
Could be put in a function as well
Referring back to this SO post
If there is a Grouping category "Category" which, for simplicity's sake, can be either X or Y - is it a trivial matter amending this script so that it will add in the missing dates for each of the categories ?
I assume the category will need adding into the CTE?
In other words if I have the following initial table:
...how do I get to the following:
Will upload my attempt shortly
I've called the initial table #x. I'm hoping to adapt a recursive CTE query like the following to include the field Category:
DECLARE #MinDate DATETIME;
SET #MinDate = (SELECT Min(DATE) FROM #x)
DECLARE #MaxDate DATETIME;
SET #MaxDate = (SELECT Max(DATE) FROM #x)
;WITH times AS
(
SELECT #MinDate dt , 1 depth
UNION ALL
SELECT
DATEADD(d, depth, #MinDate) dt
, 1 + depth as depth
FROM times
WHERE DATEADD(d, depth, #MinDate) <= #MaxDate
)
SELECT
*
FROM
TIMES t
LEFT OUTER JOIN #X x
ON
t.dt = x.Date
Ok - I've tied including a CROSS JOIN but it expands things incorrectly:
SELECT DISTINCT Category INTO #Cat FROM #x
DECLARE #MinDate DATETIME;
SET #MinDate = (SELECT Min(DATE) FROM #x)
DECLARE #MaxDate DATETIME;
SET #MaxDate = (SELECT Max(DATE) FROM #x)
;WITH times AS
(
SELECT
Category
, #MinDate dt
, 1 depth
FROM #Cat
UNION ALL
SELECT
c.Category
, DATEADD(d, depth, #MinDate) dt
, 1 + depth as depth
FROM
times t
CROSS JOIN #Cat c
--ON c.Category IS NOT NULL
WHERE DATEADD(d, depth, #MinDate) <= #MaxDate
)
SELECT
*
FROM
TIMES
This seems to have worked ok:
SELECT DISTINCT Category INTO #Cat FROM #x
DECLARE #MinDate DATETIME;
SET #MinDate = (SELECT Min(DATE) FROM #x)
DECLARE #MaxDate DATETIME;
SET #MaxDate = (SELECT Max(DATE) FROM #x)
;WITH times AS
(
SELECT
Category
, #MinDate dt
, 1 depth
FROM #Cat
UNION ALL
SELECT
Category
, DATEADD(d, depth, #MinDate) dt
, 1 + depth as depth
FROM
times t
WHERE DATEADD(d, depth, #MinDate) <= #MaxDate
)
SELECT
*
FROM
TIMES
Here is a solution without a calendar table (which is a must in production). You might have date range in variables, or you might go for min() and max() from the_table.
EDIT: shorter version incorporating categories into date range generation
declare #startdate datetime = '2012-1-1'
declare #enddate datetime = '2012-1-5'
; with dates([date], category) as (
select distinct #startdate, category
from the_table
union all
select dateadd (day, 1, [date]), category
from dates
where [date] < #enddate
)
select dates.date,
dates.category,
isnull(the_table.amount, 0) Amount
from dates
left join the_table
on dates.date = the_table.date
and dates.category = the_table.category
order by dates.category, dates.date
option (maxrecursion 0)
There is live test # Sql Fiddle.
New Sql Fiddle.
Something like this should do the trick:
declare #curDate datetime, #maxDate datetime
declare #count tinyint
select #curDate = convert(datetime, '20120101', 112), #maxDate = getdate()
select #count = 0
while #curDate < #maxDate
begin
select #count = count(1) from tablename where Category = 'X' and convert(varchar(8), Date, 112) = convert(varchar(8), #curDate, 112)
if #count > 0
begin
insert into tablename
select 'X', #curDate, 0
end
select #curDate = dateadd(dd, 1, #curDate)
end
I have from date and to date in my table, I want to know total number of days between two dates without sunday, in SQL Server 2008.
give me a query..
Accept my question...
OK, so work out the total number of days, subtract the total number of weeks, and a fiddle factor for the case where the from date is a Sunday:
SELECT
DATEDIFF(dd, FromDate, ToDate)
-DATEDIFF(wk, FromDate, ToDate)
-(CASE WHEN DATEPART(dw, FromDate) = 1 THEN 1 ELSE 0 END)
try to use this as an example and work it..
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #StartDate = '2008/10/01'
SET #EndDate = '2008/10/31'
SELECT
(DATEDIFF(dd, #StartDate, #EndDate) + 1)
-(DATEDIFF(wk, #StartDate, #EndDate) * 2)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, #EndDate) = 'Saturday' THEN 1 ELSE 0 END)
You could do this with a CTE, and this couuld easily be turned into a scalar function:
DECLARE #startDate DATETIME = '2011-09-01'
DECLARE #endDate DATETIME = '2011-09-23'
;WITH DateRange (date) AS
(
SELECT #startDate
UNION ALL
SELECT Date+1
FROM DateRange
WHERE date<#endDate
)
SELECT COUNT(*) FROM DateRange WHERE DATENAME(dw,Date) != 'Sunday'
Returns 20 which is the number of days this month so far which are not sundays.
Here's an equivalent function which can be used:
CREATE FUNCTION dbo.NumberOfDaysExcludingSunday(
#startDate DATETIME,
#endDate DATETIME
) RETURNS INT AS
BEGIN
DECLARE #rtn INT
;WITH DateRange (date) AS
(
SELECT #startDate
UNION ALL
SELECT Date+1
FROM DateRange
WHERE date<#endDate
)
SELECT #rtn = COUNT(*) FROM DateRange WHERE DATENAME(dw,Date) != 'Sunday'
RETURN #rtn
END
Usage:
SELECT dbo.NumberOfDaysExcludingSunday(startDate,endDate)
FROM myTable