CTE : looping data input every 15 days| SQL, SQL SERVER - sql

I have this test code from the guides here that I modified for test practice.
What I'm trying to achieve in this code is for it to loop the input based on a certain number, and it will loop with a 15 day gap.
Scenario: I will set the looping into 5 times. And the start date of the loop is 08/15/2019
So, the data that should be inputted is 08/15/2019, 08/30/2019, 09/15/2019, 09/30/2019, 10/15/2019 (Assuming that all dates are in 30 days.)
DECLARE #MYTESTTABLE TABLE ([Month] date, [Data 1] INT, [Data 2] INT, [Data 3] INT);
WITH MYLOOP AS (
SELECT CAST('2019-01-01' AS DATE) AS [date], 100 as [Data 3]
UNION ALL
SELECT DATEADD(MONTH, 1, [date]), [Data 3]=[Data 3]+1
FROM MYLOOP
WHERE DATEADD(MONTH, 1, [date]) < CAST('2022-01-01' AS DATE) )
INSERT INTO #MYTESTTABLE ([Month], [Data 1], [Data 2], [Data 3])
SELECT LEFT([date],10) as [Month], 100 AS [Data 1], 100 AS [Data 2], [Data 3]
FROM MYLOOP
SELECT * FROM #MYTESTTABLE
This is the guide that I got based on that scenario.
My problem now is that I'm having a hard time implementing the 2 SQL Queries into 1.
DECLARE #YourTable table (YourDate int, valuex int,tests int)
insert into #YourTable VALUES ('100',5,11)
;WITH AllNumbers AS
(SELECT 1 AS Number, 100 AS [value]
UNION ALL
SELECT Number+1, [value]=[value]+1
FROM AllNumbers
WHERE Number<=15-1 )
SELECT
YourDate, valuex, tests
FROM #YourTable y
INNER JOIN AllNumbers a ON 1=1
This is what I got so far.
DECLARE #MYTESTTABLE TABLE ([Month] date, [Data 1] INT, [Data 2] INT, [Data 3] INT);
WITH MYLOOP AS (
SELECT CAST('2019-01-01' AS DATE) AS [date], 1 as [Data 2], 100 as [Data 3]
UNION ALL
SELECT DATEADD(MONTH, 1, [date]), [Data 2]+1, [Data 3]=[Data 3]+1
FROM MYLOOP
WHERE [Data 2] < 7 )
INSERT INTO #MYTESTTABLE ([Month], [Data 1], [Data 2], [Data 3])
SELECT LEFT([date],10) as [Month], 100 AS [Data 1], [Data 2], [Data 3]
FROM MYLOOP
SELECT * FROM #MYTESTTABLE
How do is set to to trigger, or add every 15 days, and not every 1 month(30 days)?
May I ask for your assistance on this matter?

If you are looking for 5 dates only from your input date, this following script will work-
Input date must need to be 15 or 30 of a month
DECLARE #st_dt DATE = '08/15/2019'
DECLARE #prev_dt DATE
DECLARE #LC INT =0
DECLARE #MYTESTTABLE TABLE ([Month] date)
WHILE #LC < 5
BEGIN
IF #LC = 0
BEGIN
INSERT INTO #MYTESTTABLE ([Month]) VALUES(#st_dt)
SET #prev_dt = #st_dt
END
ELSE
BEGIN
IF DATEPART(DD,#prev_dt) = 15
BEGIN
INSERT INTO #MYTESTTABLE ([Month]) VALUES(DATEADD(DD,(CASE WHEN DATEPART(MM,#prev_dt) = 2 THEN 13 ELSE 15 END) ,#prev_dt))
SET #prev_dt = DATEADD(DD,(CASE WHEN DATEPART(MM,#prev_dt) = 2 THEN 13 ELSE 15 END) ,#prev_dt)
END
ELSE
BEGIN
INSERT INTO #MYTESTTABLE ([Month])
VALUES(DATEADD(DD,(15-DATEPART(DD,DATEADD(MM,1,#prev_dt))),DATEADD(MM,1,#prev_dt)))
SET #prev_dt = DATEADD(DD,(15-DATEPART(DD,DATEADD(MM,1,#prev_dt))),DATEADD(MM,1,#prev_dt))
END
END
SET #LC = #LC + 1
END
SELECT * FROM #MYTESTTABLE
Output is-
Month
2019-08-15
2019-08-30
2019-09-15
2019-09-30
2019-10-15
Now, If only adding +15 days with previous date can satisfy your requirement, this is easy and this following script will work for you-
DECLARE #st_dt DATE = '8/01/2019'
DECLARE #prev_dt DATE
DECLARE #LC INT =0
DECLARE #MYTESTTABLE TABLE ([Month] date)
WHILE #LC < 5
BEGIN
IF #LC = 0
BEGIN
INSERT INTO #MYTESTTABLE ([Month]) VALUES(#st_dt)
SET #prev_dt = #st_dt
END
ELSE
BEGIN
INSERT INTO #MYTESTTABLE ([Month]) VALUES(DATEADD(DD,15,#prev_dt))
SET #prev_dt = DATEADD(DD,15,#prev_dt)
END
SET #LC = #LC + 1
END
SELECT * FROM #MYTESTTABLE

This is the CTE version
-- straight 15 days addition
declare #startdate date = '2019-08-15'
declare #loop smallint = 15
;with cte_count as (
select 0 ctr
union all
select ctr + 1 from cte_count
where ctr < #loop-1
)
select dateadd(day,ctr*15,x.newdate) [Date] from cte_count
cross join
(select #startdate newdate) x
--- below shows 15th day and end of month
declare #startdate date = '2019-08-15'
declare #loop smallint = 50
;with cte_count as (
select 0 ctr
union all
select ctr + 1 from cte_count
where ctr < #loop-1
)
select
distinct
cast(iif(day(dateadd(day,ctr*15,newdate))<=15,
dateadd(day,14,DATEADD(m, DATEDIFF(m, 0, eomonth(dateadd(day,ctr*15,newdate))), 0))
,eomonth(dateadd(day,ctr*15,newdate))) as date)
from cte_count
cross join
(select #startdate newdate) x

Related

Count each days of week between two dates without loop

I can do it with loop, but if many day is slow. So I need do without loop.
Here is my code:
DECLARE
#FRDT date = '01-SEP-2019'
,#TODT date = '30-SEP-2019'
,#N int
,#SUN int = 0
,#MON int = 0
,#TUE int = 0
,#WED int = 0
,#THU int = 0
,#FRI int = 0
,#SAT int = 0
WHILE #FRDT <= #TODT
BEGIN
SET #N = DATEPART(WEEKDAY, #FRDT)
IF #N = 1
SET #SUN = #SUN + 1
ELSE IF #N = 2
SET #MON = #MON + 1
ELSE IF #N = 3
SET #TUE = #TUE + 1
ELSE IF #N = 4
SET #WED = #WED + 1
ELSE IF #N = 5
SET #THU = #THU + 1
ELSE IF #N = 6
SET #FRI = #FRI + 1
ELSE IF #N = 7
SET #SAT = #SAT + 1
SET #FRDT = DATEADD(DAY, 1, #FRDT)
END
SELECT 1 AS [NO], 'Sunday' AS [DAYNAME], #SUN AS [NUMBEROFDAY]
UNION SELECT 2, 'Monday', #MON
UNION SELECT 3, 'Tuesday', #TUE
UNION SELECT 4, 'Wednesday', #WED
UNION SELECT 5, 'Thursday', #THU
UNION SELECT 6, 'Friday', #FRI
I want to result like code above, but not use loop for better performance.
The date range is 30 days, dividing by 7 gives quotient 4 and remainder 2.
So every day of the week gets 4 and two days need an additional one. These are the ones corresponding to #start_date and the following day in this case.
SQL to implement this approach is below (demo)
SELECT DATENAME(WEEKDAY,base_date),
quotient + IIF(Nums.N < remainder, 1, 0)
FROM (VALUES
(0),
(1),
(2),
(3),
(4),
(5),
(6)) Nums(N)
CROSS APPLY(SELECT 1 + DATEDIFF(DAY,#start_date,#end_date)) DC(day_count)
CROSS APPLY(SELECT DATEADD(DAY, Nums.N, #start_date), day_count/7, day_count% 7) D(base_date, quotient, remainder)
ORDER BY DATEPART(DW,base_date)
You can do it with using recursive CTE as below-
DECLARE #start_date DATE= '01-SEP-2019', #end_date DATE= '30-SEP-2019';
WITH cte
AS (
SELECT #start_date AS date_
UNION ALL
SELECT CAST(DATEADD(day, 1, date_) AS DATE)
FROM cte
WHERE date_ < #end_date
)
SELECT DATEPART(DW,date_) No,
DATENAME(DW,date_) Day_Name,
COUNT(*) Num_Day
FROM cte
GROUP BY DATEPART(DW,date_),DATENAME(DW,date_)
ORDER BY DATEPART(DW,date_)
OPTION(MAXRECURSION 0);
Output-
No Day_Name Num_Day
1 Sunday 5
2 Monday 5
3 Tuesday 4
4 Wednesday 4
5 Thursday 4
6 Friday 4
7 Saturday 4
For such situation you need to have a Number table or Date Table.
In my example I am using a Number table. You can create number table anyway you want and it will help in many situations.
Create Table tblNumber(Number int primary key)
insert into tblNumber (Number) values(1),(2)...... till thousands or millions
Edit: You could generate the numbers for this number table using:
INSERT INTO tblNumber
SELECT TOP 100000 ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS [N]
FROM dbo.syscolumns tb1,dbo.syscolumns tb2
Keep this table permanently as it is useful.
DECLARE #FromDT DATETIME= '2019-09-01';
DECLARE #ToDT DATETIME= '2019-09-30';
SELECT COUNT(*), wkday
FROM
(
SELECT DATEname(weekday, DATEADD(day, number, #FromDT)) wkday
FROM tblNumber
WHERE number BETWEEN DATEPART(day, #FromDT) AND DATEPART(day, #ToDT)
) tbl
GROUP BY wkday;
If you have a Date table then it is more efficient in this situation.

How to generate all dates for a date range extracted from two date columns in SQL

I have a table TEMP1 as :
Parameter Val From To
a 1 8/1/2018 8/5/2018
b 2 8/4/2018 8/8/2018
c 3 8/8/2018 8/13/2018
and i need the output like below:
Parameter Date Val
a 8/1/2018 1
a 8/2/2018 1
a 8/3/2018 1
a 8/4/2018 1
a 8/5/2018 1
b 8/4/2018 2
b 8/5/2018 2
b 8/6/2018 2
b 8/7/2018 2
b 8/8/2018 2
c 8/8/2018 3
c 8/9/2018 3
c 8/10/2018 3
c 8/11/2018 3
c 8/12/2018 3
c 8/13/2018 3
using SQL.
Use the select below to get the dates, join in your TEMP1 table to get the values you want.
CREATE TABLE #tmpDates( StartDate DATETIME, EndDate DATETIME )
INSERT INTO #tmpDates (StartDate, EndDate)VALUES( '2018-08-10', '2018-08-15')
GO
WITH myDates AS
(SELECT StartDate as aDate FROM #tmpDates AS td
UNION ALL
SELECT DATEADD(day, 1, aDate) AS aDate FROM myDates
INNER JOIN #tmpDates AS td ON myDates.aDate >= td.StartDate
WHERE DATEADD(day, 1, aDate) <= td.EndDate)
SELECT aDate, 1 as aValue
FROM myDates
INNER JOIN (SELECT StartDate, EndDate FROM #tmpDates AS td ) AS i ON 1=1
OPTION (MAXRECURSION 0);
DROP TABLE #tmpDates
DECLARE #StartDate DATE
DECLARE #EindDate DATE
SET #StartDate = '1990-01-01' -- << user input >> --
SET #EindDate = '2018-12-31' -- << user input >> --
IF OBJECT_ID ('TEMPDB..#Date') IS NOT NULL DROP TABLE #Date
IF OBJECT_ID ('TEMPDB..#Date') IS NULL CREATE TABLE #Date (Date_ DATE)
INSERT INTO #Date VALUES (#StartDate)
WHILE #StartDate < #EindDate
BEGIN
INSERT INTO #Date
SELECT DATEADD (DD, 1, #StartDate) AS Date
SET #StartDate = DATEADD (DD, 1, #StartDate)
END;
SELECT *
FROM #Date
CREATE TABLE #T (Parameter VARCHAR (10), VAL INT, [From] DATE, [To] Date)
INSERT INTO #T VALUES ('a', 1, '20180801', '20180805')
INSERT INTO #T VALUES ('b', 2, '20180804', '20180808')
INSERT INTO #T VALUES ('c', 3, '20180808', '20180813')
SELECT D.Date_, T.Parameter, T.VAL
FROM #Date AS D
INNER JOIN #T AS T ON D.Date_ >= T.[From] AND D.Date_ <= T.[To]
MySQL (prior to V8) does not have support for recursive queries. What you want to do is to join your table to a numbers table. You can create one on the fly if it not too big:
select t.parameter, (t.from + interval n - 1 day) day as date,
t.val
from temp1 t join
(select 1 as n union all
select 2 as n union all
select 3 as n union all
select 4 as n union all
select 5 as n
) n
on t.from + interval n - 1 day <= t.to;
I should note that you can also do this using a Calendar table.
You can make a cartesian join:
http://sqlfiddle.com/#!18/03a13/6
SELECT s.*
FROM temp1 s, (
select 1 'temp'
union all
select 2
union all
select 3
union all
select 4
union all
select 5
) ss
Try below query:
WITH cte AS
(SELECT Parameter,Val,From as dateval
UNION ALL
SELECT Parameter,Val,DATEADD(day, 1, dateval)
FROM cte WHERE DATEADD(day, 1, dateval) <= To
)
SELECT Parameter,Val,dateval
FROM cte
OPTION (MAXRECURSION 0);
You can use below SQL Query for to get your result.
This was using Cursor and column names and alias may need to change as per your requirements.
SET NOCOUNT ON
DECLARE #mindate date
DECLARE #maxdate date
DECLARE #parameter char(5)
DECLARE #value smallint
Declare #temp table( fromdate date, Parameter char(5),val smallint)
DECLARE cur_date CURSOR
STATIC FOR
SELECT [from],[to], [parameter],[val] from temp1
OPEN cur_date
IF ##CURSOR_ROWS > 0
BEGIN
FETCH NEXT FROM cur_date INTO #mindate,#maxdate,#parameter, #value
WHILE ##Fetch_status = 0
BEGIN
INSERT INTO #temp
SELECT TOP (DATEDIFF(DAY, #mindate, #maxdate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #mindate), #parameter as Parameter, #value as Val
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
FETCH NEXT FROM cur_date INTO #mindate,#maxdate,#parameter, #value
END
END
CLOSE cur_date
DEALLOCATE cur_date
SELECT * FROM #temp

Calculating time in a price list

I have a Price list table in SQL Server 2008R2 and want to calculate the Price for a Service according to the time where the Service has been made.
timefrom |timeto |Price
-------- |------ |-----
1900-01-01 00:00:00|1900-01-01 07:00:00|20.00
1900-01-01 07:00:00|1900-01-01 19:00:00|15.00
1900-01-01 19:00:00|1900-01-02 00:00:00|20.00
This pricelist Shows different Price during night time starting at 19:00 and Lasting until 07.00 AM and daytime from 07:00 to 19:00.
Minutes have to be rounded up and down to quarter hours.
There is some more to take care of, such as Minimum notice Periode (#Vorlaufzeit) and if weekday of Weekend. Both conditions are met and not the Problem. My Problem is the first and the last record, where I have to round up and/or round down, which are incorrect. Both lines have 1 in the loop and should be corrected correctly in the both updates, but does not.
So, for example a Service from 2016-11-04 10:50 (rounding down to 10:45 which is 0.25 hours) to 2016-11-04 19:25 (rounded up to 19:30 which is 0.5 hours) is 0.25+8+0.5= 8.75 hours and costs 8.25*15 + 0.5*20 = 133.75.
I tried with this code, but it does not bring me the correct result. It is only the first and the last record where I have to round up or down. It is only correct, when there are full hours.
DECLARE #Dauer int
DECLARE #X int --Loopcounter für Stunden
declare #Y int --Loopcounter für Tageszahler
declare #Anfangszeit datetime
declare #Anfangsstunde datetime
declare #Endzeit datetime
declare #Vorlaufzeit int --in Minuten
declare #ErsteZeitvon datetime
declare #SummeAnzStunden decimal(8,2)
declare #MinimumZeit int
declare #ZeitvonVolleStunde int -- aus 07:25 mach 7 Uhr
declare #ZeitbisVolleStunde int
declare #AnfangsDatumZeit as datetime
declare #EndDatumZeit as datetime
declare #AnfangsDatumZeitLoop as datetime
declare #AnfangsZeitLoop as datetime
declare #TagesZaehler int
set #AnfangsDatumZeit = #Datumvon+#Zeitvon
set #EndDatumZeit = #Datumbis+#Zeitbis
set #Tageszaehler=datediff(day,#AnfangsDatumZeit, #EndDatumZeit)
declare #t1 table ( PreisID int, AnzStunden decimal(5,2), Preis decimal(8,2), Anfangszeit datetime, Prüfzeit datetime, startzeit datetime, endezeit datetime, Vorlaufzeit int, Dauer int, PreisFT decimal(8,2), DatZeitvon datetime, DatZeitbis datetime, Tageszaehler int )
-- Insert statements for procedure here
set #ZeitvonVolleStunde=Datediff(hour, '00:00:00', #Zeitvon)
set #ZeitbisVolleStunde=Datediff(minute, '00:00:00', #Zeitbis)
set #Dauer=ceiling(Datediff(minute, #AnfangsDatumZeit, #EndDatumZeit)/60.00)
set #Vorlaufzeit=datediff(minute,#Bestelldatum, #AnfangsDatumZeit)
SET #X = 0
if Datediff(minute, #AnfangsDatumZeit, #EndDatumZeit) > 360
begin
WHILE (#X <=#Dauer) --z.b. 13
begin
--set #Y = datediff(day,#AnfangsDatumZeit,#AnfangsDatumZeitLoop)
set #Y = datediff(day,#AnfangsDatumZeit,dateadd(hour,#X, #AnfangsDatumZeit))
set #AnfangsDatumZeitLoop=dateadd(hour,#X, #AnfangsDatumZeit)
set #AnfangsZeitLoop=dateadd(hour,#X, #Zeitvon)
insert into #t1 ( PreisID, AnzStunden, Preis , Anfangszeit, Prüfzeit, DatZeitvon , DatZeitbis )
SELECT top 1 preisID, 1, Preis, #AnfangsZeitLoop, #AnfangsDatumZeitLoop, Zeitvon, Zeitbis
FROM dbo.Mypricetable
where SdlID=#Leistungsart --SdlID
and Wochentag=case when DATEPART(dw,#AnfangsDatumZeitLoop) < 6 then 'W' else 'S' end --Wochentag
and #Vorlaufzeit BETWEEN Vorlaufzeitvon and Vorlaufzeitbis --Vorlaufzeit in Minuten
AND #Dauer*60 BETWEEN Dauervon AND Dauerbis --DauerInMinuten
and #AnfangsZeitLoop between Zeitvon and Zeitbis --sucht die von/bis Zeitgruppe
order by zeitvon
SET #X = #X + 1
end
--check and udate of the first record in #t1 rounding down to 15 minutes
update #t1 set Anzstunden= Anzstunden + CONVERT(DECIMAL(6, 2), ROUND(((datediff(minute,[dbo].[sfRoundToHourParts](#AnfangsDatumZeit,1), [dbo].[sfRoundToHourParts](#AnfangsDatumZeit,4)) + 7) / 60.00) / 25, 2) * 25)
from #t1 c where c.Prüfzeit=(SELECT Top 1 Prüfzeit from #t1 order by Prüfzeit)
--check and udate of the last record in #t1 rounding up to 15 minutes
update #t1 set Anzstunden= round(convert(decimal(5,2),datepart(minute,#EndDatumZeit)+7)/60/25,2)*25
from #t1 c where c.Prüfzeit=(SELECT Top 1 Prüfzeit from #t1 order by Prüfzeit DESC)
end
select * from #t1 order by Prüfzeit
Thanks your help!
Michael
This code is a bit verbose, but wanted to share it with you as it produces the desired result of 133.75
DECLARE #x table (
timefrom datetime
, timeto datetime
, price decimal(14,4)
);
INSERT INTO #x (timefrom, timeto, price)
VALUES ('1900-01-01T00:00:00', '1900-01-01T07:00:00', 20.00)
, ('1900-01-01T07:00:00', '1900-01-01T19:00:00', 15.00)
, ('1900-01-01T19:00:00', '1900-01-02T00:00:00', 20.00)
;
-- You should have your own, physical tally table!
DECLARE #numbers table (
number tinyint
);
INSERT INTO #numbers (number)
SELECT DISTINCT
number
FROM master.dbo.spt_values
WHERE number BETWEEN 0 AND 255
;
DECLARE #start datetime = '2016-11-04T10:50:00'
, #end datetime = '2016-11-04T19:25:00'
;
-- first, let's do some rounding of our inputs
DECLARE #rounded_start_time time
, #rounded_end_time time
;
-- Illustrate the steps to round the time to quarters... this might not be the simplest method; but it works!
/*
SELECT #start AS start
, DateAdd(hh, DateDiff(hh, 0, #start), 0) AS truncate_hour
, Round(DatePart(mi, #start) / 15.0, 0) * 15 AS rounded_mins
, DateAdd(mi, Round(DatePart(mi, #start) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, #start), 0)) AS truncate_hour_then_add_mins
;
*/
SET #rounded_start_time = DateAdd(mi, Round(DatePart(mi, #start) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, #start), 0));
SET #rounded_end_time = DateAdd(mi, Round(DatePart(mi, #end ) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, #end ), 0));
PRINT 'Start: ' + Format(#rounded_start_time, 'HH:mm');
PRINT 'End: ' + Format(#rounded_end_time , 'HH:mm');
--SELECT *
--FROM #x
--;
; WITH intervals AS (
SELECT number * 15 AS minute_increments
, DateAdd(mi, number * 15, 0) AS interval_start
, DateAdd(mi, (number + 1) * 15, 0) AS interval_end
FROM #numbers
WHERE number >= 0
AND number < 24 * 4 --number of 15 minute increments in a day
)
, costed_intervals AS (
SELECT intervals.interval_start
, intervals.interval_end
, Cast(intervals.interval_start AS time) As interval_start_time
, Cast(intervals.interval_end AS time) As interval_end_time
, x.price / 4.0 AS interval_price
FROM #x AS x
INNER
JOIN intervals
ON intervals.interval_end <= x.timeto
AND intervals.interval_start >= x.timefrom
)
, applicable_intervals AS (
SELECT interval_start
, interval_end
, interval_start_time
, interval_end_time
, interval_price
FROM costed_intervals
WHERE interval_start_time < #rounded_end_time
AND interval_end_time > #rounded_start_time
)
SELECT Sum(interval_price) AS total_price
FROM applicable_intervals
;
This could use a lot of cleaning up and optimising.
It also only works when the start and end times are within the same day, among other bugs and fun stuff.
thanks to everyones contribution I could find my way and corrected my Duration and the two updates.
Duration:
set #Dauer=datediff(hh, DateAdd(hh, DateDiff(hh, 0, #Datumvon+#Zeitvon), 0),DateAdd(hh, DateDiff(hh, 0, #datumbis+#Zeitbis), 0))
Update the first record
update #t1 set Anzstunden =Anzstunden + (datediff(mi,DateAdd(mi, Round((DatePart(mi, #Zeitvon)-7) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, #Zeitvon), 0)),DateAdd(hh, DateDiff(hh, 0, #Zeitvon), 0))/60.00)
-- case when CONVERT(DECIMAL(6, 2), ROUND(((datediff(minute,[dbo].[sfRoundToHourParts](#AnfangsDatumZeit,1), [dbo].[sfRoundToHourParts](#AnfangsDatumZeit,4)) + 7) / 60.00) / 25, 2) * 25) *-1 > 0 then
-- CONVERT(DECIMAL(6, 2), ROUND(((datediff(minute,[dbo].[sfRoundToHourParts](#AnfangsDatumZeit,1), [dbo].[sfRoundToHourParts](#AnfangsDatumZeit,4)) + 7) / 60.00) / 25, 2) * 25) *-1 else Anzstunden end
from #t1 c where c.Prüfzeit=(SELECT Top 1 Prüfzeit from #t1 order by Prüfzeit)
Update the last record
--Prüft und korrigiert den LETZTEN Datensatz der #t1 auf 15 Minuten-Takt
update #t1 set Anzstunden= Anzstunden + ((datediff(mi,DateAdd(mi, Round((DatePart(mi, #Zeitbis)+7) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, #Zeitbis), 0)),DateAdd(hh, DateDiff(hh, 0, #Zeitbis), 0))/60.00)*-1)
from #t1 c where c.Prüfzeit=(SELECT Top 1 Prüfzeit from #t1 order by Prüfzeit DESC)
now everything is correct.
Thx to everyone.
Michael

Convert Procedural Approach into Set Based Approach in Sql-Server

We are using procedural approach (while loop) for inserting records into a particular table. the insert syntax is like below,
DECLARE #CNT INT = 0,
#WEEK DATE = '2015-11-01',
#FLAG INT
CREATE TABLE #Tmpdata (officeId int,id smallint, weekDate date,startsOn varchar(10),endsOn varchar(10),flag bit);
WHILE (#CNT <7)
BEGIN
SET #WEEK = DATEADD(D,#CNT,#WEEK )
IF EXISTS
(SELECT 1
FROM YEARRANGE
WHERE #WEEK BETWEEN CONVERT(DATE,taxseasonbegin)
AND CONVERT (DATE,taxSeasonEnd)
)
BEGIN
SET #FLAG =1
END
ELSE
BEGIN
SET #FLAG = 0
END
INSERT INTO #Tmpdata
(
officeId,id,weekDate,startsOn,endsOn,flag
)
VALUES
(
5134,#lvCounter,#week,'09:00 AM','05:00 PM',#flag
);
SET #cnt=#cnt+1;
end
(NOTE : TaxSeason is from january to august).
Is it possible to re-write the above logic in set based approach?
This is making a number of assumption because you didn't post ddl or any consumable sample data. Also, there is a variable #lvCounter not defined in your code. This is perfect opportunity to use a tally or numbers table instead of a loop.
declare #lvCounter int = 42;
DECLARE #CNT INT = 0,
#WEEK DATE = '2015-11-01',
#FLAG INT;
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n))
, cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E1
)
select 5134 as officeId
, #lvCounter as Id
, DATEADD(DAY, N - 1, #WEEK) as weekDate
, '09:00 AM' as startsOn
, '05:00 PM' as EndOn
, Flag
from cteTally t
cross apply
(
select CAST(count(*) as bit) as Flag
from YearRange
where DATEADD(Day, t.N, #WEEK) > CONVERT(DATE,taxseasonbegin)
AND DATEADD(Day, t.N, #WEEK) <= CONVERT (DATE,taxSeasonEnd)
) y
where t.N <= 7;
Please can you provide sample data?
You can do something like:
SELECT DateIncrement = SUM(DATEADD(D,#CNT,#WEEK)) OVER (ORDER BY officeID)
FROM...
This gets an incremented date value for each record which you can then check against your start and end dates.
You could try some Kind of this one. This gives you the data I think you Need for your insert. I do not have a table named YEARRANGE so I couldn't test it completely
DECLARE #CNT INT = 0, #WEEK DATE = '2015-11-01', #FLAG INT;
CREATE TABLE #Tmpdata (officeId int,id smallint, weekDate date,startsOn varchar(10),endsOn varchar(10),flag bit);
WITH CTE AS
(
SELECT num AS cnt,
DATEADD(D, SUM(num) OVER(ORDER BY num ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
, #WEEK) AS [week]
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY nl) -1 AS num
FROM
(SELECT NULL AS nl UNION ALL SELECT NULL AS nl UNION ALL SELECT NULL AS nl UNION ALL SELECT NULL AS nl
UNION ALL SELECT NULL AS nl UNION ALL SELECT NULL AS nl UNION ALL SELECT NULL AS nl
) AS ni
) AS no
)
INSERT INTO #Tmpdata (officeId,id,weekDate,startsOn,endsOn,flag)
SELECT 5134 AS officeID, cnt AS id, [week],'09:00 AM' AS startsOn,'05:00 PM' AS endsOn, COALESCE(A1.flag,0) AS flag
FROM CTE
OUTER APPLY (SELECT 1
FROM YEARRANGE
WHERE [week] BETWEEN CONVERT(DATE,taxseasonbegin)
AND CONVERT (DATE,taxSeasonEnd)
) AS A1(flag);

Return rows in a resultset when no record found for particular date

I have created a query to return following output.
Date Day Sale Qty Purchase Qty Transfer Qty
------------------------------------------------------------------
05/04/2015 1 11 0 0
07/04/2015 3 0 16 0
08/04/2015 4 12 14 17
11/04/2015 7 1 2 0
My current query is as follows.
(select T1.Date,T1.Day,T1.SaleQty,0 as PurchaseQty,0 as TransferQty from SaleTable T1)
union all
(select T2.Date,T2.Day,0 as SaleQty,T2.PurchaseQty,0 as TransferQty from PurchaseTable T2)
union all
(select T3.Date,T3.Day,0 as SaleQty,0 as PurchaseQty,T3.TransferQty from TransferTable T3)
Required output is in the following format
Date Day Sale Qty Purchase Qty Transfer Qty
------------------------------------------------------------------
05/04/2015 1 11 0 0
06/04/2015 2 0 0 0
07/04/2015 3 0 16 0
08/04/2015 4 12 14 17
09/04/2015 5 0 0 0
10/04/2015 6 0 0 0
11/04/2015 7 1 2 0
How should I write query to return rows with date and day when no result set is returned for that date output.
You need a table to act as a lookup table for the dates and days that are missing to cover the range of dates in the query results. You can create one like so:
-- add a temp table for your sample data
CREATE TABLE #Results
([Date] datetime, [Day] int, [Sale Qty] int, [Purchase Qty] int, [Transfer Qty] int)
;
-- insert your sample data
INSERT INTO #Results
([Date], [Day], [Sale Qty], [Purchase Qty], [Transfer Qty])
VALUES
('2015-04-05 00:00:00', 1, 11, 0, 0),
('2015-04-07 00:00:00', 3, 0, 16, 0),
('2015-04-08 00:00:00', 4, 12, 14, 17),
('2015-04-11 00:00:00', 7, 1, 2, 0)
;
-- find the max date
DECLARE #MaxDate DATETIME = (SELECT TOP 1 [Date] FROM #Results ORDER BY [Date] DESC)
-- recursive cte to build the date & day lookup table
;WITH cte AS (
-- cte anchor is the min date and day = 1
SELECT MIN([Date]) AS DateValue, 1 AS [Day]
FROM #Results
UNION ALL
-- uses dateadd to increment days until #MaxDate reached
SELECT DATEADD(DAY, 1, cte.DateValue), [Day] +1
FROM cte
WHERE DATEADD(DAY, 1, cte.DateValue) <= #MaxDate
)
-- inserts values into temp lookup table
SELECT *
INTO #DateLookup
FROM cte
This will create a temp table with the range of values using the lowest and highest dates in your results that holds these values:
DateValue Day
----------------------------
2015-04-05 00:00:00.000 1
2015-04-06 00:00:00.000 2
2015-04-07 00:00:00.000 3
2015-04-08 00:00:00.000 4
2015-04-09 00:00:00.000 5
2015-04-10 00:00:00.000 6
2015-04-11 00:00:00.000 7
You will then need to link to this table and replace any NULL values with 0 like so:
SELECT #DateLookup.[DateValue] AS [Date],
#DateLookup.[Day] ,
COALESCE([Sale Qty],0) AS [Sale Qty],
COALESCE([Purchase Qty],0) AS [Purchase Qty],
COALESCE([Transfer Qty],0) AS [Transfer Qty]
FROM #DateLookup
LEFT JOIN #Results ON #DateLookup.DateValue = #Results.[Date]
-- some tidy up
DROP TABLE #Results
DROP TABLE #DateLookup
If you don't already have a dates table in your system, add one. you can use this t-sql script:
CREATE TABLE TblDates(TheDate date not null);
DECLARE #StartDate date,
#NumberOfDates int,
#Counter int
SELECT #StartDate = GETDATE(), -- or whatever date you want
#NumberOfDates = 100, -- or whatever number of dates you want after start date
#Counter = 0;
WHILE(#Counter < #NumberOfDates)
BEGIN
INSERT INTO TblDates(TheDate) VALUES (DATEADD(d, #Counter, #StartDate)
END
After running this script you should have a dates table ready to use.
Then all you have to do is this:
SELECT TheDate,
DATEPART(D, TheDate) As Day,
ISNULL(T1.SaleQty, 0) As SaleQty,
ISNULL(T2.PurchaseQty, 0) As PurchaseQty,
ISNULL(T3.TransferQty, 0) As TransferQty
FROM tblDates LEFT JOIN
SaleTable T1 ON(TheDate = T1.Date) LEFT JOIN
PurchaseTable T2 ON(TheDate = T2.Date) LEFT JOIN
TransferTable T3 ON(TheDate = T3.Date)
Update
Now that i think about it, you can use a numbers table to create a cte that will hold your dates (creating the numbers table is fairly simple and much like the script I wrote for the dates table)
With DatesCTE(TheDate) AS
SELECT DATEADD(D, TheNumber, #startDate)
FROM NumbersTable
WHERE DATEADD(D, TheNumber, #StartDate) < #EndDate
Update 2
As Selva TS mentioned in a comment to this answer, it's possible to generate a dates cte even without a numbers table, using recursive cte like this:
DECLARE #StartDate datetime, #EndDate datetime
SELECT #StartDate = DATEADD(YEAR, -1, GETDATE()), #EndDate = GETDATE()
;WITH Calendar AS(
SELECT #StartDate dateidx
UNION ALL
SELECT dateidx + 1
FROM Calendar
WHERE dateidx + 1 < #EndDate
)