Related
In SQL, I am going to write a query which insert all data between 2 dates and also I want to bring then in a 1000 batch but since the number of data between those days are more than my limitation I was going to write a loop which makes the smaller period to bring the data.
here is my code:
DECLARE #StartDate DATETIME = CAST('2021-06-02 01:00:00.000' AS DATETIME)
DECLARE #EndDate DATETIME = CAST('2021-06-23 01:00:00.000' AS DATETIME)
DECLARE #RealRowCount INT = (SELECT DISTINCT SUM(##ROWCOUNT) OVER() FROM GetReport (
#StartDate, #EndDate))
DECLARE #TransactionCount INT = (SELECT DISTINCT TransactionCount FROM GetReport (
#StartDate, #EndDate))
WHILE #RealRowCount < #TransactionCount
BEGIN
DECLARE #DiffDate INT = (SELECT DATEDIFF(DAY, #StartDate, #EndDate))
SET #EndDate = DATEADD(DAY, #DiffDate/2 ,#StartDate)
SELECT *,#StartDate, #EndDate FROM GetReport (#StartDate, #EndDate)
END
PS: I was thinking about find the middle of the period of date and then change them into the new EneDate and StartDate but there is problem here!
Your question is not very clear. Suppose you have 10,000 records between two dates and you do not want to retrieve more than a thousand records at a time. In this case, you can use pagination. Both in the program code and in SQL.
DECLARE #StartDate DATETIME = CAST('2021-06-02 01:00:00.000' AS DATETIME)
DECLARE #EndDate DATETIME = CAST('2021-06-23 01:00:00.000' AS DATETIME)
DECLARE #RealRowCount INT = (SELECT DISTINCT COUNT(*) FROM Products WHERE InsertDate BETWEEN #StartDate AND #EndDate)
DECLARE #Counter INT = 0
WHILE #Counter <= #RealRowCount
BEGIN
SELECT *
FROM Products
WHERE InsertDate BETWEEN #StartDate AND #EndDate
ORDER BY InsertDate
OFFSET #Counter ROWS -- skip #Counter rows
FETCH NEXT 1000 ROWS ONLY -- take 1000 rows
SET #Counter = #Counter + 1000
END
Or you can get the time difference between the two dates and add the start date each time in a specific step and retrieve the data of that date.
For example, the date difference is 20 days. Increase the start date by 5 steps each time to the start date with the end date
I create another table to put Dates and if this table has any rows I can get 'EndDate', but if it has not any records I simply just use the date that I specified.
AccSync is the table that I insert details of my records and AccTransformation is the table wich I want to insert all of my records.
DECLARE #Count INT = (SELECT COUNT(*) FROM [AccTransaction])
DECLARE #Flag BIT = (SELECT IIF(#Count > 1, 1, 0))
DECLARE #End DATETIME = GETDATE();
DECLARE #Start DATETIME
IF(#Flag = 0)
BEGIN
SET #Start = CAST('2021-03-08' AS DATETIME2);
SET #Flag = 1
END
ELSE IF(#Flag = 1)
BEGIN
SET #Start = (SELECT TOP 1 EndDate FROM (SELECT EndDate FROM [AccSync] ORDER BY ActionDate DESC OFFSET 0 ROW) AS TT);
END
DECLARE #RealRowCount INT = (SELECT DISTINCT SUM(##ROWCOUNT) FROM [GetReport] (#Start, #End));
DECLARE #TransactionCount INT = (SELECT DISTINCT TransactionCount FROM [GetReport] (#Start, #End));
----------------------------------------------------------------------------------------------
WHILE (#RealRowCount <> #TransactionCount)
BEGIN
DECLARE #DiffDate INT = (SELECT DATEDIFF(SECOND, #Start, #End))
SET #End = DATEADD(SECOND, (#DiffDate/2), #Start)
SET #RealRowCount = (SELECT DISTINCT SUM(##ROWCOUNT) FROM [GetReport] (#Start, #End))
SET #TransactionCount = (SELECT DISTINCT TransactionCount FROM [GetReport] (#Start, #End))
END
----------------------------------------------------------------------------------------------
INSERT INTO [AccTransaction]
SELECT *
FROM [GetReport](#Start, #End)
----------------------------------------------------------------------------------------------
INSERT INTO [AccSync]
VALUES(NEWID(), GETDATE(), #Start, #End, ISNULL(#TransactionCount,0), DATEDIFF(SECOND, #Start, #End))
I have seen many example adding working date (business days) to date in SQL. But I would like to add hour.
For example; I would like to add 36 hour to date not in Sunday , Saturday
Could one help me about it ?
CREATE FUNCTION AddWorkDays
(
#WorkingDays As Int,
#StartDate AS DateTime
)
RETURNS DateTime
AS
BEGIN
DECLARE #Count AS Int
DECLARE #i As Int
DECLARE #NewDate As DateTime
SET #Count = 0
SET #i = 0
WHILE (#i < #WorkingDays) --runs through the number of days to add
BEGIN
-- increments the count variable
SELECT #Count = #Count + 1
-- increments the i variable
SELECT #i = #i + 1
-- adds the count on to the StartDate and checks if this new date is a Saturday or Sunday
-- if it is a Saturday or Sunday it enters the nested while loop and increments the count variable
WHILE DATEPART(weekday,DATEADD(d, #Count, #StartDate)) IN (1,7)
BEGIN
SELECT #Count = #Count + 1
END
END
-- adds the eventual count on to the Start Date and returns the new date
SELECT #NewDate = DATEADD(d,#Count,#StartDate)
RETURN #NewDate
END
GO
The following will add N hours to a date (excluding Saturday and Sundays).
Example
Declare #Date1 datetime = '2017-04-28'
Declare #Hours int = 36
Select D=max(D)
From (
Select D,HN=-1+Row_Number() over (Order by D)
From (Select D=DateAdd(HOUR,-1+Row_Number() Over (Order By (select null)),#Date1) From master..spt_values n1 ) D
Where DateName(WEEKDAY,D) not in ('Saturday','Sunday')
) D1
Where HN=#Hours
Returns
2017-05-01 12:00:00.000
is it what you are looking for?
declare #num_hours int;
set #num_hours = 1;
select dateadd(HOUR, #num_hours, getdate()) as time_with_hour;
declare #num_hours int;
set #num_hours = 5;
select dateadd(HOUR, #num_hours, getdate()) as time_added,
getdate() as curr_date
This question is already answered Here
I know that there are other posts with code that solve my problem but I don't want to take another's code so I'm trying to do it by myself and I'm stuck with the month not increasing problem, so if anyone can help me with that mistake it will be awesome.
The problem is:
I have to populate the table Time from year 1990 to 2016 with all the months and days, I have already achieved that the code works and it populates correctly the years and the days but months increases to January (1) and then is not increasing so the table is filled with all months being January (LOL)
Here's my code:
create table Time
(
Year int,
Month int,
Day int
)
create procedure pTime
as
declare #year int, #month int, #day int;
set #year = 1990;
set #month = 12;
set #day = 10;
while(#year<=2016)
Begin
If(#day = 29)
Begin
set #month = #month + 1;
If(#month = 13)
Begin
set #month = 1;
set #day = 1;
set #year = #year + 1;
insert into Time values (#year, #month, #day);
End
End
else
Begin
If(#day = 29)
Begin
set #month = #month + 1;
set #day = 1;
insert into Time values (#year, #month, #day);
End
Else
Begin
insert into Time values (#year, #month, #day);
set #day = #day + 1;
End
End
End
Any idea where is my mistake or any suggestion?
I didn't look very closely for your mistake because SQL Server has some helpful date arithmetic functions. Here's simplified version of your stored procedure:
create procedure pTime
as
declare #theDate date = '12/10/1990', #days int = 0
while #theDate < '1/1/2016'
begin
insert into Time (Year, Month, Day) values (datepart(year, #theDate), datepart(month, #theDate), datepart(day, #theDate));
set #theDate = dateadd(day, 1, #theDate)
end
Another faster approach would be to use a tally table. Note the code below:
WITH
E(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1),
iTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1 FROM E a,E b,E c,E d,E e),
dates(dt) AS
(
SELECT TOP(datediff(DAY,'19900101','20160101')) DATEADD(day,N,'19900101')
FROM iTally
)
--INSERT [time] --uncomment for the insert, leave commented to see what will be inserted
SELECT YEAR(dt), MONTH(dt), DAY(dt)
FROM dates;
Why do you need If(#year = 29) condition? In your code this block never will be executed. try this:
create procedure pTime
as
declare #year int, #month int, #day int;
set #year = 1990;
set #month = 12;
set #day = 10;
while(#year<=2016)
Begin
If(#day = 29)
Begin
set #month = #month + 1;
set #day = 1;
If(#month = 13)
Begin
set #month = 1;
set #year = #year + 1;
insert into Time values (#year, #month, #day);
End
End
else
Begin
If(#day = 29)
Begin
set #month = #month + 1;
set #day = 1;
insert into Time values (#year, #month, #day);
End
Else
Begin
insert into Time values (#year, #month, #day);
set #day = #day + 1;
End
End
End
I think first assignment set #day = 1; wasn't in right place. After increasing #month value you should set also #day to 1;
Can you please help:
I have an arrival date column that needs 5000 rows adding to it with random dates from the year 2010.
For example: 01/01/2010, 25/8/2010, 03/05/2010, 31/12/2010, etc
I think it would be an INSERT statement but I am unsure.
Thanks
Wayne
USE Occupancy
CREATE TABLE Book
(BookNumber varchar(30),
ArrivalDate int)
DECLARE #BookNumber varchar(30)
DECLARE #ArrivalDate int
SET #BookNumber = 1
SET #ArrivalDate = 0
WHILE #BookNumber <= 5000
WHILE #ArrivalDate <= 5000
BEGIN
INSERT INTO Book (BookNumber,ArrivalDate)
SELECT 'B' + Right ('00000000' + CAST(#BookNumber AS varchar(30)),8),
DATEADD(day, CAST(RAND() * 365 as int), '2010-1-1')
SET #BookNumber = #BookNumber + 1
SET #ArrivalDate = #ArrivalDate + 1
END
DECLARE #counter int
SET #counter = 0
WHILE #counter < 5000
BEGIN
INSERT INTO myTable (arrivalDate)
SELECT DATEADD(day, CAST(RAND() * 365 as int), '2010-1-1')
SET #counter = #counter + 1
END
And if you have a numbers table or generator, you can avoid the loop:
INSERT INTO myTable (arrivalDate)
SELECT DATEADD(day, ABS(CHECKSUM(NEWID()) % 365), '2010-1-1')
FROM ( GenerateRowNumbers(5000, 1) ) t
I am using this query to get time taken.
SELECT DATEDIFF(dd, ActualStartDate, ActualCompletionDate) AS TimeTaken
FROM TableName
Now I want to exclude weekends and only include Mon-Fri as days counted.
Example query below, here are some details on how I solved it.
Using DATEDIFF(WK, ...) will give us the number of weeks between the 2 dates. SQL Server evaluates this as a difference between week numbers rather than based on the number of days. This is perfect, since we can use this to determine how many weekends passed between the dates.
So we can multiple that value by 2 to get the number of weekend days that occurred and subtract that from the DATEDIFF(dd, ...) to get the number of weekdays.
This doesn't behave 100% correctly when the start or end date falls on Sunday, though. So I added in some case logic at the end of the calculation to handle those instances.
You may also want to consider whether or not the DATEDIFF should be fully inclusive. e.g. Is the difference between 9/10 and 9/11 1 day or 2 days? If the latter, you'll want to add 1 to the final product.
declare #d1 datetime, #d2 datetime
select #d1 = '9/9/2011', #d2 = '9/18/2011'
select datediff(dd, #d1, #d2) - (datediff(wk, #d1, #d2) * 2) -
case when datepart(dw, #d1) = 1 then 1 else 0 end +
case when datepart(dw, #d2) = 1 then 1 else 0 end
I found when i used this there was a problem when d1 fell on saturday. Below is what i used to correct this.
declare #d1 datetime, #d2 datetime
select #d1 = '11/19/2011' , #d2 = '11/28/2011'
select datediff(dd, #d1, #d2) +case when datepart(dw, #d1) = 7 then 1 else 0 end - (datediff(wk, #d1, #d2) * 2) -
case when datepart(dw, #d1) = 1 then 1 else 0 end +
case when datepart(dw, #d2) = 1 then 1 else 0 end
BEGIN
DECLARE #totaldays INT;
DECLARE #weekenddays INT;
SET #totaldays = DATEDIFF(DAY, #startDate, #endDate)
SET #weekenddays = ((DATEDIFF(WEEK, #startDate, #endDate) * 2) + -- get the number of weekend days in between
CASE WHEN DATEPART(WEEKDAY, #startDate) = 1 THEN 1 ELSE 0 END + -- if selection was Sunday, won't add to weekends
CASE WHEN DATEPART(WEEKDAY, #endDate) = 6 THEN 1 ELSE 0 END) -- if selection was Saturday, won't add to weekends
Return (#totaldays - #weekenddays)
END
This is on SQL Server 2014
declare #d1 datetime, #d2 datetime
select #d1 = '4/19/2017', #d2 = '5/7/2017'
DECLARE #Counter int = datediff(DAY,#d1 ,#d2 )
DECLARE #C int = 0
DECLARE #SUM int = 0
WHILE #Counter > 0
begin
SET #SUM = #SUM + IIF(DATENAME(dw,
DATEADD(day,#c,#d1))IN('Sunday','Monday','Tuesday','Wednesday','Thursday')
,1,0)
SET #Counter = #Counter - 1
set #c = #c +1
end
select #Sum
If you hate CASE statements as much as I do, and want to be able to use the solution inline in your queries, just get the difference of days and subtract the count of weekend days and you'll have the desired result:
declare #d1 datetime, #d2 datetime, #days int
select #d1 = '2018/10/01', #d2 = '2018/11/01'
SET #days = DateDiff(dd, #d1, #d2) - DateDiff(ww, #d1, #d2)*2
print #days
(The only caveat--or at least point to keep in mind--is that this calculation is not inclusive of the last date, so you might need to add one day to the end date to achieve an inclusive result)
I just want to share the code I created that might help you.
DECLARE #MyCounter int = 0, #TempDate datetime, #EndDate datetime;
SET #TempDate = DATEADD(d,1,'2017-5-27')
SET #EndDate = '2017-6-3'
WHILE #TempDate <= #EndDate
BEGIN
IF DATENAME(DW,#TempDate) = 'Sunday' OR DATENAME(DW,#TempDate) = 'Saturday'
SET #MyCounter = #MyCounter
ELSE IF #TempDate not in ('2017-1-1', '2017-1-16', '2017-2-20', '2017-5-29', '2017-7-4', '2017-9-4', '2017-10-9', '2017-11-11', '2017-12-25')
SET #MyCounter = #MyCounter + 1
SET #TempDate = DATEADD(d,1,#TempDate)
CONTINUE
END
PRINT #MyCounter
PRINT #TempDate
If you do have a holiday table, you can also use that so that you don't have to list all the holidays in the ELSE IF section of the code. You can also create a function for this code and use the function whenever you need it in your query.
I hope this might help too.
Using https://stackoverflow.com/a/1804095 and JeffFisher30's answer above (https://stackoverflow.com/a/14572370/6147425) and my own need to have fractional days, I wrote this:
DateDiff(second,Start_Time,End_Time)/86400.0
-2*DateDiff(week, Start_Time, End_Time)
-Case When (DatePart(weekday, Start_Time)+##DateFirst)%7 = 1 Then 1 Else 0 End
+Case When (DatePart(weekday, End_Time)+##DateFirst)%7 = 1 Then 1 Else 0 End
Use this function to calculate the number of business days excluding Saturday and Sunday. Also it will exclude start date and it will include end date.
-- Select [dbo].[GetBussinessDays] ('02/18/2021', '03/06/2021') -- 11 days
CREATE or ALTER FUNCTION [dbo].[GetBussinessDays] (
#StartDate DATETIME,
#EndDate DATETIME
)
returns INT AS
BEGIN
DECLARE #tempStartDate DATETIME= #StartDate;
DECLARE #tempEndDate DATETIME = #EndDate;
IF(#tempStartDate IS NULL
OR
#tempEndDate IS NULL)
BEGIN
RETURN NULL;
END
--To avoid negative values reverse the date if StartDate is grater than EndDate
IF(#StartDate > #EndDate)
BEGIN
SET #StartDate = #tempEndDate;
SET #EndDate = #tempStartDate;
END
DECLARE #Counter INT = Datediff(day,#StartDate ,#EndDate);
DECLARE #TempCounter INT = 0;
DECLARE #TotalBusinessDays INT = 0;
WHILE #Counter >= 0
BEGIN
IF(#TempCounter > 0 OR #Counter = 1) -- To ignore first day's calculation
Begin
SET #TotalBusinessDays = #TotalBusinessDays + Iif(Datename(dw, Dateadd(day,#TempCounter,#StartDate)) IN('Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday'),1,0)
END
SET #Counter = #Counter - 1
SET #TempCounter = #TempCounter +1
END
RETURN #TotalBusinessDays;
END
Using #Derek Kromm answer (Current Marked Answer)
I have modified so it IS tolerant of any localisations that may be on the target server.
DECLARE #d1 DATETIME, #d2 DATETIME
SELECT #d1 = '10/01/2022', #d2 = '10/28/2022'
SELECT (datediff(dd, #d1, #EndQuery)+1) - (datediff(wk, #d1, dateadd(dd,1,#d2)) * 2)
- CASE WHEN DateName(WEEKDAY, #d1) = 'Sunday' THEN 1 ELSE 0 END -- This includes for start date edge case
+ CASE WHEN DateName(WEEKDAY, #d2) = 'Saturday' THEN 1 ELSE 0 END -- This includes for end date edge case.
This is with the end date being innclusive.
/*
EXAMPLE:
/MONDAY/
SET DATEFIRST 1
SELECT dbo.FUNC_GETDATEDIFFERENCE_WO_WEEKEND('2019-02-01','2019-02-12')
*/
CREATE FUNCTION FUNC_GETDATEDIFFERENCE_WO_WEEKEND
(
#pdtmaLastLoanPayDate DATETIME,
#pdtmaDisbursedDate DATETIME
)
RETURNS BIGINT
BEGIN
DECLARE
#mintDaysDifference BIGINT
SET #mintDaysDifference = 0
WHILE CONVERT(NCHAR(10),#pdtmaLastLoanPayDate,121) <= CONVERT(NCHAR(10),#pdtmaDisbursedDate,121)
BEGIN
IF DATEPART(WEEKDAY,#pdtmaLastLoanPayDate) NOT IN (6,7)
BEGIN
SET #mintDaysDifference = #mintDaysDifference + 1
END
SET #pdtmaLastLoanPayDate = DATEADD(DAY,1,#pdtmaLastLoanPayDate)
END
RETURN ISNULL(#mintDaysDifference,0)
END