I am trying to use a predefined INT value giving 1 or 0 as being a business day or not to count the rolling business days sequence. I have tried a lot of different code and checked out different posts here but none are specific enough to work with mine.
The predefined INT value is "business_day_flag_int". This comes from converting the calculations giving me "day_business_day_flag" which is a bit value. Currently Saturday/Sunday and Banking holidays trigger a 0 for "day_business_day_flag" and Mon-Fri being non holiday give it a 1 value.
How can I get this to work in an Update table that I can add to the rest of my table generation file? If more information is needed let me know.
I've tried a bunch of different alterations and variations of what I have here. If I remove date values it gives me an int value for all rows in the column of roughly 12600 business days. If I use with the date range it gives me
DECLARE #StartDate DATETIME = '01/01/2000' --Starting value of Date Range
DECLARE #EndDate DATETIME = '01/01/2050' --End Value of Date Range
DECLARE
#DayOfWeekInMonth INT,
#DayOfWeekInYear INT,
#DayOfQuarter INT,
#WeekOfMonth INT,
#CurrentYear INT,
#CurrentMonth INT,
#CurrentQuarter INT
DECLARE #CurrentDate AS DATETIME = #startDate
SET #CurrentMonth = DATEPART(MM, #CurrentDate)
SET #CurrentYear = DATEPART(YY, #CurrentDate)
SET #CurrentQuarter = DATEPART(QQ, #CurrentDate)
UPDATE [EDW_MDM].[dbo].[CALENDAR_DIM] SET
business_day_flag_int = Convert(INT, day_business_day_flag)
UPDATE [EDW_MDM].[dbo].[CALENDAR_DIM] SET
rolling_business_day_sequence = (SELECT count(business_day_flag_int) FROM [EDW_MDM].[dbo].[CALENDAR_DIM]
WHERE business_day_flag_int = 1 and
day_date between #StartDate and #CurrentDate)
I want the column "rolling_business_day_sequence" to count sequentially business days past. For example row 1 = 1, row 2 = 2, etc. Until the end of my calendar.
Update 1: Edited the line of code to
UPDATE [EDW_MDM].[dbo].[FCFCU_CALENDAR_DIM] SET
rolling_business_day_sequence = datediff(day,#StartDate,day_date) WHERE day_business_day_flag = 1
This gave me counting days and set rolling_business_day_sequence row values to null where not having day_busienss_day_flag = 1 and still counting them instead of not counting them. How can I make it not add the day?
Consider a window function sum on your WHERE conditions to cumulatively count all instances of business_day_flag_int = 1 within specified date range. However, to use window functions in UPDATE, a CTE or subquery is required.
WITH CTE AS
(
SELECT ID, SUM(CASE WHEN day_date BETWEEN #StartDate AND #CurrentDate
AND business_day_flag_int = 1
THEN 1
ELSE 0
END) OVER (ORDER BY day_date) AS running_sequence
FROM [EDW_MDM].[dbo].[CALENDAR_DIM]
)
UPDATE t
FROM [EDW_MDM].[dbo].[CALENDAR_DIM] t
JOIN CTE ON t.ID = CTE.ID
SET t.rolling_business_day_sequence = CTE.running_sequence
Related
This question already has answers here:
Calculate a Running Total in SQL Server
(15 answers)
Closed 1 year ago.
I have the below SQL query that works but it's very slow. it takes about 1 min to run the query. This would be made into a stored procedure. which is not the problem. but that store procedure would be called for every item of which there are about 600 items. The estimated run time probably would end up taking about 10 hours. Does anyone have any suggestions of a better way of doing it?
What I'm trying to do is get a running stock level for every day for the past year for each item.
If you need any more information. Please let me know.
DECLARE #StartDate AS DATETIME
DECLARE #EndDate AS DATETIME
DECLARE #CurrentDate AS DATETIME
DECLARE #ItemName As Varchar(450)
DECLARE #QOH DECIMAL(19,4)
SET #QOH = 0
SET #ItemName = 'TUR001-02'
SET #StartDate = '2020-04-01'
SET #EndDate = GETDATE()
SET #CurrentDate = #StartDate
CREATE TABLE #TempTable
(
Date datetime,
ItemName char(450),
QOH DECIMAL(19,4)
);
WHILE (#CurrentDate < #EndDate)
BEGIN
DECLARE #daySales DECIMAL(19,4)
SELECT #daySales = SUM(Quantity)
FROM qbInvoiceLineDetail
WHERE TxnDate = #CurrentDate AND FullName = #ItemName;
SET #QOH = #QOH - #daySales
INSERT INTO #TempTable (Date, ItemName, QOH)
SELECT #CurrentDate, #ItemName, #QOH;
SET #CurrentDate = DATEADD(DAY, 1, #CurrentDate);
END
SELECT * FROM #TempTable
DROP TABLE #TempTable
You can use a tally table to generate your dates between start and end, then insert all your data in one hit into your table.
Caveat - this is untested as I have nothing to check it against, assumes the dates are dates only, if they include time then will need to use convert - hopefully will be what you are looking for:
/*first, create a tally table - this should be a permanent feature */
select top 1000 N=Identity(int, 0, 1)
into dbo.Digits
from master.dbo.syscolumns a cross join master.dbo.syscolumns
declare #StartDate datetime='20200401', #EndDate datetime=GetDate()
select DateAdd(day,N,#startDate) currentDate, FullName ItemName, Sum(Quantity) over(order by d.N) QOH
from Digits d
left join qbInvoiceLineDetail q on q.TxnDate=DateAdd(day,N,#startDate)
where DateAdd(day,N,#startDate)<=#EndDate
group by TxnDate, ItemName
I have a table of dates which includes every day as a separate row between a #startDate and an #endDate.
I need to remove all dates apart from the ones which match a #dayOfMonth, say the 16th as an example.
But I don't need to keep every row which contains a 16th of the month. I have to factor in another variable, #everyNMonths. So if #everyNMonths was set to 2, and my #startDate was '2016-10-13' and #endDate was '2017-03-20' I would want to keep:
2016-10-16
2016-12-16
2017-02-16
Not sure how I can achieve this. Thanks for any help.
This should work:
DECLARE #StartTime DATE = '2015-11-01'
DECLARE #EndTime DATE = '2016-10-01'
DECLARE #Interval INT = 1
DECLARE #dayOfMonth INT = 16
--CREATE Table #TempTimes
SELECT StartTime
FROM
(
SELECT StartTime,ROW_NUMBER() OVER (ORDER BY StartTime) As Num
FROM
(
SELECT DISTINCT StartTime
FROM #TempTimes
WHERE DATEPART(DD,StartTime) = #dayOfMonth
AND StartTime BETWEEN #StartTime AND #EndTime
)t
)s
WHERE (Num - 1)%#Interval = 0
I just want to ask how can I achieve getting the records in a database by week in a month? using sql
What I really mean is when I input 1 it will get the records in 1st week of a month
I've done a lot of research today but it seems I can't find a good solution
Heres the code:
DECLARE #MONTH int
DECLARE #YEAR int
DECLARE #WEEK int
SET #MONTH = 9
SET #YEAR = 2013
SET #WEEK = 1
SELECT RH.RepairOrderNumber FROM dbo.RepairOrderHeader RH
WHERE MONTH(RH.DateReleased) = #MONTH AND YEAR(RH.DateReleased) = #YEAR AND WEEK(RH.DateReleased) = #WEEK
I just want to fetched the records according to month,year, and by week is there any way and precise code on how to do this?
DATEPART(WEEK,) gives you number of a week in an YEAR so you just need to calculate number of week in the month calculating difference between current date week number and the week number of the first day of the month:
WHERE MONTH(RH.DateReleased) = #MONTH
AND YEAR(RH.DateReleased) = #YEAR
AND DATEPART(WEEK,RH.DateReleased)
-DATEPART(WEEK,DATEADD(DAY,-DAY(RH.DateReleased)+1,RH.DateReleased))+1
= #WEEK
WEEK returns the week of the year, starting on a constant day of the week (i.e. each Sunday). You seem to want the week of the month, which I guess would always start on the 1st of each month.
so:
SELECT RH.RepairOrderNumber FROM dbo.RepairOrderHeader RH
WHERE MONTH(RH.DateReleased) = #MONTH AND
YEAR(RH.DateReleased) = #YEAR AND
DAY(RH.DateReleased) / 7 + 1 = #WEEK
Where possible avoid using functions on columns in the WHERE clause. You immediately lose the ability to take advantage of indices on these columns. The below solution gives the same results as the select answer, but performs much better:
DECLARE #MONTH INT = 9
DECLARE #YEAR INT = 2013
DECLARE #WEEK INT = 2
--1ST OF THE MONTH DECLARED
DECLARE #Date DATE = CAST(CAST(#Year AS VARCHAR(4)) +
RIGHT('0' + CAST(#Month AS VARCHAR(2)), 2) + '01' AS DATE)
SET #Date = DATEADD(WEEK, #Week - 1, DATEADD(DAY, 1 - DATEPART(WEEKDAY, #Date), #Date));
SELECT RH.RepairOrderNumber
FROM dbo.RepairOrderHeader RH
WHERE RH.DateReleased >= #Date
AND RH.DateReleased < DATEADD(WEEK, 1, #Date);
All this does is get the first of the month in question, then find the first day of the week that the first of the month falls in. Then adds the declared number of weeks to this date.
With an example table with 6 years of data in and 10 rows for each day (20,480 records, so not a lot) and an index on your date column the execution plans of this and the accepted answer are as follows (Accepted on top, using dates on the bottom):
Testing DDL and queries on SQL-Fiddle
This shows that when you use WEEK(DateColumn) = #Week that the index is not used at all, and the query cost is much higher. Depending on the distribution of data in your table this could make a massive difference, even in the small set of data in my example it is 800% more efficient.
I consider '2013-08-26' - '2013-09-01' the first week in september
DECLARE #MONTH int = 9
DECLARE #YEAR int = 2013
DECLARE #WEEK int = 1
DECLARE #from date= dateadd(day, datediff(day, -(#WEEK - 1)*7,
(dateadd(month, (#year-1900) * 12 + #month - 1 , 0)))/7*7 , 0)
SELECT RH.RepairOrderNumber
FROM dbo.RepairOrderHeader RH
WHERE RH.DateReleased >= #from
and RH.DateReleased < dateadd(week, 1, #from)
I am here again for some help. This is what I am trying to do. I have a function or sp that takes 3 parameters. startdate, enddate, and dayofweek (m, t, w, etc), need to count how many weeks are in between the start and end date.
Example, if I pass startday=2011-05-02, enddate=2011-05-10, and dayofweek as Tuesday, it should count 2, if I pass dayofweek as wednesday then it should count 1.
I am able to work using this code, is there any better way to do this? The ClosingStartDate is being adjusted to the correct start date based on the dayofweek used. So basically, the code is adding 1 week to the runningdate and running date is adjusted according to the week is used.
Any other way to do it without the while..loop.???
declare #NoofPeriods int
declare #runningdate datetime
set #runningdate = #ClosingStartDate
set #NoofPeriods=0
while (#runningdate <= #NextStatementClosingdate)
begin
set #NoofPeriods=#NoofPeriods+1
set #runningdate = Dateadd(day,7,#runningdate)
end
It is already built in to SQL 2005.
SELECT DATEDIFF(week, #startdate, #enddate)
I'm probably not dead on with the SQL Server syntax, but logic like this should work. Basically use modulo-7 arithmetic to determine how many "odd" days you have at the start of the period, and whether these include one of your chosen weekdays (as determined by your dayofweek) or not.
#diffInDays = SELECT DATEDIFF(day, #startdate, #enddate)
#startDateDay = SELECT DATEPART(weekday, #startdate)
set #diffInWholeWeeks = #diffInDays / 7
set #diffRemainderDays = #diffInDays % 7
if #diffRemainderDays >= ( ( #dayofweek - #startDateDay ) % 7 )
begin
set #extraWeek = 1
end
else
begin
set #extraWeek = 0
end
set #answer = #diffInWholeWeeks + #extraWeek
#slothrop's answer is almost dead on, so I am going to base this on that code and upvote that answer.
Okay this comment was wrong (please ignore):
Because days are from 1-7 instead of 0-6, we need to account for it. I do this by subtracting one after our second modulous. I haven't tested all scenarios, but it seems to work.
What was messing me up was the shift of the week. Add 7 to the dayofweek to make sure the mod works.
This should work,
--Setup
DECLARE #dayofweek int = 4
DECLARE #startdate datetime = '5/2/2011'
DECLARE #enddate datetime = '5/10/2011'
--Solution
DECLARE #diffInDays int = DATEDIFF(day, #startdate, #enddate);
DECLARE #startDateDay int = DATEPART(weekday, #startdate);
DECLARE #extraWeek int = 0;
DECLARE #diffInWholeWeeks int = #diffInDays / 7
DECLARE #diffRemainderDays int = #diffInDays % 7
if #diffRemainderDays >= ( ( #dayofweek + 7 - #startDateDay ) % 7 )
set #extraWeek = 1
DECLARE #answer int = #diffInWholeWeeks + #extraWeek
SELECT #answer
Note: I'm not sure the inline setting of declarations is supported in 2005. If so, just use a SET or SELECT statement to set the values.
I'm attempting to group contiguous date ranges to show the minimum and maximum date for each range. So far I've used a solution similar to this one: http://www.sqlservercentral.com/articles/T-SQL/71550/ however I'm on SQL 2000 so I had to make some changes. This is my procedure so far:
create table #tmp
(
date smalldatetime,
rownum int identity
)
insert into #tmp
select distinct date from testDates order by date
select
min(date) as dateRangeStart,
max(date) as dateRangeEnd,
count(*) as dates,
dateadd(dd,-1*rownum, date) as GroupID
from #tmp
group by dateadd(dd,-1*rownum, date)
drop table #tmp
It works exactly how I want except for one issue: weekends. My data sets have no records for weekend dates, which means any group found is at most 5 days. For instance, in the results below, I would like the last 3 groups to show up as a single record, with a dateRangeStart of 10/6 and a dateRangeEnd of 10/20:
Is there some way I can set this up to ignore a break in the date range if that break is just a weekend?
Thanks for the help.
EDITED
I didn't like my previous idea very much. Here's a better one, I think:
Based on the first and the last dates from the set of those to be grouped, prepare the list of all the intermediate weekend dates.
Insert the working dates together with weekend dates, ordered, so they would all be assigned rownum values according to their normal order.
Use your method of finding contiguous ranges with the following modifications:
1) when calculating dateRangeStart, if it's a weekend date, pick the nearest following weekday;
2) accordingly for dateRangeEnd, if it's a weekend date, pick the nearest preceding weekday;
3) when counting dates for the group, pick only weekdays.
Select from the resulting set only those rows where dates > 0, thus eliminating the groups formed only of the weekends.
And here's an implementation of the method, where it is assumed, that a week starts on Sunday (DATEPART returns 1) and weekend days are Sunday and Saturday:
DECLARE #tmp TABLE (date smalldatetime, rownum int IDENTITY);
DECLARE #weekends TABLE (date smalldatetime);
DECLARE #minDate smalldatetime, #maxDate smalldatetime, #date smalldatetime;
/* #1 */
SELECT #minDate = MIN(date), #maxDate = MAX(date)
FROM testDates;
SET #date = #minDate - DATEPART(dw, #minDate) + 7;
WHILE #date < #maxDate BEGIN
INSERT INTO #weekends
SELECT #date UNION ALL
SELECT #date + 1;
SET #date = #date + 7;
END;
/* #2 */
INSERT INTO #tmp
SELECT date FROM testDates
UNION
SELECT date FROM #weekends
ORDER BY date;
/* #3 & #4 */
SELECT *
FROM (
SELECT
MIN(date + CASE DATEPART(dw, date) WHEN 1 THEN 1 WHEN 7 THEN 2 ELSE 0 END)
AS dateRangeStart,
MAX(date - CASE DATEPART(dw, date) WHEN 1 THEN 2 WHEN 7 THEN 1 ELSE 0 END)
AS dateRangeEnd,
COUNT(CASE WHEN DATEPART(dw, date) NOT IN (1, 7) THEN date END) AS dates,
DATEADD(d, -rownum, date) AS GroupID
FROM #tmp
GROUP BY DATEADD(d, -rownum, date)
) s
WHERE dates > 0;