I have an INSERT statement that operates within a WHILE loop. In each iteration of the WHILE loop, some functions are called with dates variables passed as parameters. These date variables increase by one day at each iteration of the loop.
This is a stripped down example:
-- Start and End Date
DECLARE
@StartDate DATE = '20170101'
,@EndDate DATE = '20170110'
-- Initialise Loop variables to Start Date
DECLARE
@InsertDate DATE = @StartDate
,@NextDate DATE = @StartDate
-- Loop for All Dates
WHILE (@InsertDate <> @EndDate)
BEGIN
-- Gather Data to Insert
INSERT INTO tblCombinedData
SELECT
a.SomeString
,b.SomeNumber
,dbo.fnDoSomeStuff(a.AKey,@InsertDate,@NextDate)
,dbo.fnDoSomeMoreStuff(b.AKey,@InsertDate,@NextDate)
FROM
tblATable a
INNER JOIN tblAnotherTable b
ON a.ID = b.ID
-- Move to next Set of Dates
SET @InsertDate = DATEADD(DAY,1,@InsertDate)
SET @NextDate = DATEADD(DAY,1,@InsertDate)
END
Is There a more efficient way of achieving this combined insert? (possibly via a CTE?) Thanks.
Note: (SQL Server 2008 R2)
Calendar CTE, anyone?
with CTE as
(
select @startdate as InsertDay
union all
select dateadd(day, 1, @startdate)
from CTE
where @startdate < @enddate
)
insert into tblCombinedData
select a1.stuff, fn(a1.stuff2, InsertDay, dateadd(day,1,InsertDay))
from CTE
cross join
(
select stuff, stuff2
from tab1
inner join tab2
on tab1.thing = tab2.thing
)
Another way....
declare @table1 table (dt datetime)
declare @table2 table (notDT char)
insert into @table1 (dt) values
('1/1/2017'),
('1/2/2017'),
('1/3/2017'),
('1/4/2017')
insert into @table2 (notDT) values
('a'),
('b'),
('c')
;with t2 as(
select
*,
ROW_NUMBER() over (order by (select null)) as rn
from @table2),
t1 as(
select
*,
ROW_NUMBER() over (order by dt) as rn
from @table1)
select
t2.notDT,
t1.dt
from
t2
inner join t1 on t1.rn = t2.rn
You can use an ad-hoc Tally Table in concert with a CROSS APPLY
Declare @Date1 date = '20170101'
Declare @Date2 date = '20170110'
-- Insert Into tblCombinedData
Select B.*
From (Select Top (DateDiff(DD,@Date1,@Date2)+1) D=DateAdd(DD,-1+Row_Number() Over (Order By Number),@Date1) From master..spt_values) DT
Cross Apply (
SELECT a.SomeString
,b.SomeNumber
,dbo.fnDoSomeStuff(a.AKey,DT.D,DT.D) --<< Notice DT.D
,dbo.fnDoSomeMoreStuff(b.AKey,DT.D,DT.D) --<< Notice DT.D
FROM tblATable a
INNER JOIN tblAnotherTable b ON a.ID = b.ID
) B
If it helps with the visualization, the ad-hoc tally table looks like this
D
2017-01-01
2017-01-02
2017-01-03
2017-01-04
2017-01-05
2017-01-06
2017-01-07
2017-01-08
2017-01-09
2017-01-10
try it with Recursive CTE
:
;WITH CTE
AS (
SELECT @StartDate AS StartDate, DATEADD(DAY,1,@StartDate) AS NextDate
UNION ALL
SELECT DATEADD(DAY,1,StartDate) AS StartDate, DATEADD(DAY,1,NextDate) AS NextDate
FROM CTE
WHERE DATEADD(DAY,1,NextDate) <= @EndDate
)
INSERT INTO tblCombinedData
SELECT
a.SomeString
,b.SomeNumber
,dbo.fnDoSomeStuff(a.AKey, CTE.StartDate, CTE.NextDate)
,dbo.fnDoSomeMoreStuff(b.AKey, CTE.StartDate, CTE.NextDate)
FROM tblATable a
INNER JOIN tblAnotherTable b ON a.ID = b.ID
CROSS JOIN CTE
This would be better handled with a Calendar table, but if you must use something to generate the dates on demand then this would do:
declare @fromdate date = '20170101';
declare @thrudate date = '20170110';
;with dates as (
select top (datediff(day, @fromdate, @thrudate)+1)
InsertDate=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
, NextDate =convert(date,dateadd(day,row_number() over(order by (select 1)) ,@fromdate))
from master..spt_values a cross join master..spt_values b
order by 1
)
insert into tblCombinedData
select
a.SomeString
, b.SomeNumber
, dbo.fnDoSomeStuff(a.akey,d.InsertDate,d.NextDate)
, dbo.fnDoSomeMoreStuff(b.akey,d.InsertDate,d.NextDate)
from tblatable a
inner join tblAnotherTable b
on a.id = b.id
cross join dates d;
This does not use a recursive common table expression (cte), just a regular cte. Using a recursive cte to generate a sequence of is one slowest ways to generate a set or sequence without a loop.
Number and Calendar table reference:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.