My current query give my this as a result;
Address PK StartDate EndDate Rent Cost NoDays
1 water lane 3435 01/04/2018 12/02/2020 500 11210.95 682
7 get road 5456 14/06/2019 01/02/2020 700 5339.18 232
I want to outline how many days per month/ or how much per month spent.
this is what i want to see after NoDays or even on a new query result.
04/2018 05/2018 06/2018 07/2018 so on ....
30 31 30 31 so on ....
0 0 0 0 so on ....
or
04/2018 05/2018 06/2018 07/2018 so on ....
500 500 500 500 so on ....
0 0 0 0 so on ....
Here a solution which generates a calendar using recursion
then adding columns and updating them using cursor and dynamic script
set dateformat dmy
declare #table as table(pk int, startdate date,enddate date,rent int,cost float)
insert into #table values(3435,'01/04/2018','12/02/2020',500,11210.95),(5456,'14/06/2019','01/02/2020',700,5339.18)
declare #table2 as table(pk int)
insert into #table2 select distinct(pk) from #table
declare #calendar as table (date date)
declare #mindate as date
declare #maxdate as date
select #mindate=min(startdate) from #table
select #maxdate=max(enddate) from #table;
with cte as(select #mindate as mydate union all select dateadd(day,1,mydate) from cte
where mydate < #maxdate)
insert into #calendar select * from cte
option(maxrecursion 0);
declare #tabresultsrows as table(pk int,MO varchar(7),N int,M int,Y int);
declare #tabmonths as table(Mo varchar(7),M int,Y int);
with cal as(
select t2.pk,c.date ,t.startdate,t.enddate ,month(date) M, year(date) y ,concat(RIGHT('00' + CONVERT(NVARCHAR(2), month(date)), 2),'/', year(date)) Mo,
case when c.date >= t.startdate and c.date <=t.enddate then 1 else 0 end N from #calendar c
cross join #table2 t2
inner join #table t on t2.pk=t.pk),
caltab as(select pk,Mo,sum(N) N ,Y,M from cal group by pk,Y,M,Mo )
insert into #tabresultsrows select pk,MO,N,M,Y from caltab order by pk,Y,M
insert into #tabmonths select distinct(MO),M,Y from #tabresultsrows
IF OBJECT_ID('tempdb..#tabresultscolumns') IS NOT NULL DROP TABLE #tabresultscolumns
select * into #tabresultscolumns from #table
declare #script as varchar(max)
declare mycursor cursor for select mo from #tabmonths order by Y,M
declare #mo as varchar(7)
open mycursor
fetch mycursor into #mo
while ##fetch_status=0
begin
set #script='alter table #tabresultscolumns add ['+#mo+'] int'
print #script
exec(#script)
fetch mycursor into #mo
end
close mycursor
deallocate mycursor
declare secondcursor cursor for select pk,Mo,N from #tabresultsrows
declare #PK AS INT
declare #n as int
open secondcursor
fetch secondcursor into #pk,#mo,#n
while ##fetch_status=0
begin
set #script=concat('update #tabresultscolumns set ['+#mo+']=',#n,' where pk=',#pk )
print #script
exec(#script)
fetch secondcursor into #pk,#mo,#n
end
close secondcursor
deallocate secondcursor
select * from #tabresultscolumns
Try something like this:
-- You can set the variables to get as an input the StartDate, EndDate from your table
DECLARE #StartDate DATE = '20200101'
, #EndDate DATE = '20200331'
;with datecreator as (
SELECT DATEADD(DAY, nbr - 1, #StartDate) as dates
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY c.PK ) AS Nbr
FROM Test c
) nbrs
WHERE nbr - 1 <= DATEDIFF(DAY, #StartDate, #EndDate)
)
,CTE AS
(
select distinct Month(dates) rnk,Convert(char(3), dates, 0) MM from datecreator
)
,CTE3 AS
(
SELECT T.* , rnk
FROM Test T INNER JOIN CTE c ON C.Mm = T.Months
)
,CTE4 AS
(
SELECT Years,[1] Jan ,[2] Feb ,[3] Mar FROM CTE3
PIVOT
(SUM(Rent) FOR rnk IN ([1],[2],[3])) p
)
SELECT Years , SUM(Jan) Jan , SUM(Feb) Feb , SUM(Mar) Mar FROM CTE4 GROUP BY Years
You'll get the result :
Year1 Jan Feb Mar
2013 3000 3000 3000
2014 3500 3500 3500
As you can see above I used only the first Quarter (First 3 Months) but you can use the whole year.
Please let me know if you have any questions/Feedbacks :)
Related
I want to show all dates between two dates when there is any date data missing then its should show zero in val column .
declare #temp table (
id int identity(1,1) not null,
CDate smalldatetime ,
val int
)
INSERT STATEMENT FOR DATA TO CHECK
insert into #temp select '10/2/2012',1
insert into #temp select '10/3/2012',1
insert into #temp select '10/5/2012',1
insert into #temp select '10/7/2012',2
insert into #temp select '10/9/2012',2
insert into #temp select '10/10/2012',2
insert into #temp select '10/13/2012',2
insert into #temp select '10/15/2012',2
Retrieve records between first day of month and today
select * from #temp where CDate between '10/01/2012' AND '10/15/2012'
As i run this query its show me all data between these two dates but i want to also include missing dates with val=0
SQL FIDDLE WITH SAMPLE DATA
;with d(date) as (
select cast('10/01/2012' as datetime)
union all
select date+1
from d
where date < '10/15/2012'
)
select t.ID, d.date CDate, isnull(t.val, 0) val
from d
left join temp t
on t.CDate = d.date
order by d.date
OPTION (MAXRECURSION 0) -- use this if your dates are >99 days apart
You need to make up the dates, so I've use a recursive common table expression here.
SQL Fiddle
MAXRECURSION number
Specifies the maximum number of recursions allowed for this query. number is a nonnegative
integer between 0 and 32767. When 0 is specified, no limit is applied. If this option is
not specified, the default limit for the server is 100.
When the specified or default number for MAXRECURSION limit is reached during query
execution, the query is ended and an error is returned.
This will work as long as there are less than 2047 days between from and to dates
declare #from smalldatetime = '10/01/2012'
declare #to smalldatetime = '10/15/2012'
select t.id, dateadd(day, number,#from), isnull(val, 0) val from #temp t
right join master..spt_values s
on dateadd(d, s.number, #from) = t.CDate
where
datediff(day, #from, #to ) > s.number
and s.type = 'P'
I think the best way to do this is to create your own table with dates (you can also use master.dbo.spt_values, but I personally don't like that solution)
declare #Temp_Dates table (CDate datetime)
declare #Date datetime
select #Date = (select min(CDate) from temp)
while #Date <= (select max(CDate) from temp)
begin
insert into #Temp_Dates (CDate)
select #Date
select #Date = dateadd(dd, 1, #Date)
end
select D.CDate, isnull(T.id, 0) as id
from #Temp_Dates as D
left outer join temp as T on T.CDate = D.CDate
you can also use recursive solution with CTE
DECLARE #min DATETIME,
#max DATETIME,
#val INT
SELECT #min = Min(CDATE),
#max = Max(CDATE)
FROM TEMP
DECLARE #temp TABLE
(
CDATE SMALLDATETIME,
VAL INT
)
WHILE #min < #max
BEGIN
SELECT #val = VAL
FROM TEMP
WHERE CDATE = #min
INSERT #temp
VALUES (#min,
#val)
SET #min = Dateadd(D, 1, #min)
SET #val = 0
END
SELECT *
FROM #temp
Declare #temp Table(id int identity(1,1) not null,CDate smalldatetime ,val int)
insert into #temp select '10/2/2012',1
insert into #temp select '10/3/2012',1
insert into #temp select '10/5/2012',1
insert into #temp select '10/7/2012',2
insert into #temp select '10/9/2012',2
insert into #temp select '10/10/2012',2
insert into #temp select '10/13/2012',2
insert into #temp select '10/15/2012',2
DECLARE #startDate DATE= '10/01/2012'
DECLARE #endDate DATE= '10/15/2012'
SELECT t.Id, X.[Date],Val = COALESCE(t.val,0)
FROM
(SELECT [Date] = DATEADD(Day,Number,#startDate)
FROM master..spt_values
WHERE Type='P'
AND DATEADD(day,Number,#startDate) <= #endDate)X
LEFT JOIN #temp t
ON X.[Date] = t.CDate
using a recursive cte with min and max
declare #T table (id int identity(1,1) primary key, dt date not null, val int not null);
insert into #T (dt, val) values
('10/2/2012',1)
, ('10/3/2012',1)
, ('10/5/2012',1)
, ('10/7/2012',2)
, ('10/9/2012',2)
, ('10/10/2012',2)
, ('10/13/2012',2)
, ('10/15/2012',2);
--select * from #T;
with cte as
( select min(dt) as dt, max(dt) as mx
from #T
union all
select dateadd(dd, 1, dt), mx
from CTE
where dt < mx
)
select c.dt, isnull(t.val, 0) as val
from cte c
left join #T t
on c.dt = t.dt
order by c.dt
option (maxrecursion 0);
dt val
---------- -----------
2012-10-02 1
2012-10-03 1
2012-10-04 0
2012-10-05 1
2012-10-06 0
2012-10-07 2
2012-10-08 0
2012-10-09 2
2012-10-10 2
2012-10-11 0
2012-10-12 0
2012-10-13 2
2012-10-14 0
2012-10-15 2
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
I have a data something like this:
LoanId PaymentDate PaymentMonth PaymentAmount
L1 12-01-2008 01 100
L2 15-02-2008 02 300
L3 01-04-2008 04 500
L3 01-10-2008 10 500
I want to add missing PaymentMonth's for each loanId's, like this:
LoanId PaymentYear PaymentMonth PaymentAmount
L1 2008 01 100
L1 2008 02 0
L1 2008 03 0
.. .. .. ..
L1 2008 12 0
L2 2008 01 0
L2 2008 02 300
L2 2008 03 0
.. .. .. ..
L3 2008 01 0
L3 2008 02 0
L3 2008 03 0
L3 2008 04 500
.. .. .. ..
L3 2008 10 500
.. .. .. ..
L3 2008 12 0
Was doing it manually, but now got more than 100k LoanId's from 2008-20012
Try to do this:
use db_test;
go
create table dbo.test1
(
loanId varchar(2),
paymentDate date,
paymentMonth varchar(2),
paymentAmount float
);
set dateformat dmy;
insert into dbo.test1
values
('L1', '12-01-2008', '01', 100),
('L2', '15-02-2008', '02', 300),
('L3', '01-04-2008', '04', 500),
('L3', '01-10-2008', '10', 500);
set dateformat ymd;
with cte as (
select cast('2008-01-31' as date) as month_dt, 1 as month_nm, format(1, 'd2') as paymentMonth
union all
select eomonth(dateadd(month, 1, month_dt)), month_nm + 1, format(month(month_dt) % 12 + 1, 'd2')
from cte
where month_dt < '2012-12-31'
), cte2 as (
select
t.loanId,
x.month_dt,
x.paymentMonth
from (
select distinct loanId from dbo.test1
) t
join cte x
on 1 = 1
)
select
a.loanId, year(a.month_dt) as paymentYear, a.paymentMonth, coalesce(b.sm, 0) as paymentAmount
from
cte2 a
left join (
select loanId, eomonth(paymentDate) as paymentDate, paymentMonth, sum(paymentAmount) as sm
from dbo.test1
group by loanId, eomonth(paymentDate), paymentMonth
) b
on a.month_dt = b.paymentDate
and a.loanId = b.loanId
order by
paymentYear asc,
loanId asc,
paymentMonth;
you could try like:
1.) Getting your MIN & MAX PaymentDate (as i asume those are the your ranges)
2.) Creating all months within this range - in my example with a common table expression).
3.) Finally selecting your data and joining with those month-dates and grouping the result
DECLARE #StartDate DATETIME,
#EndDate DATETIME;
SET #StartDate = SELECT MIN(PaymentDate)
FROM yourtable
SET #EndDate = SELECT MAX(PaymentDate)
FROM yourtable
;WITH CTE AS (
SELECT DATEADD(MONTH, x.number, #StartDate) as Months
FROM master.dbo.spt_values x
WHERE x.type = 'P'
AND x.number <= DATEDIFF(MONTH, #StartDate, #EndDate)
)
SELECT yourtable.LoanID
,yourtable.PaymentYear
,yourtable.PaymentMonth
,SUM(ISNULL(PaymentAmount,0)) as PaymentAmount
FROM CTE
INNER JOIN yourtable
ON yourtable.PaymentYear = CONVERT(VARCHAR(4),DATEPART(YEAR, Months))
AND yourtable.PaymentMonth = RIGHT('0' + CONVERT(VARCHAR(2),DATEPART(MONTH, Months)),2)
GROUP BY yourtable.LoanID
,yourtable.PaymentYear
,yourtable.PaymentMonth
This is one way of doing it, pretty straightforward. Necessary comments are in the code.
declare #LoanData table (
ID char(2),
PaymentDate date,
PaymentAmount int
)
insert into #LoanData values
('L1', '01-12-2008',100),
('L2', '02-15-2008',300),
('L3', '04-01-2008',500),
('L3', '10-01-2008',500)
declare #TableID table(id char(2))
--list of IDs
insert into #TableID select distinct ID from #LoanData
declare #PaymentMonth table(
LoanID char(2),
PaymentYear int,
PaymentMonth int,
PaymentAmount int
)
declare #month int, #year int, #i int, #id char(2)
select #i = count(*) from #TableID
--first get the table which has recotrd for every month for every id (default value in PaymentAmount is 0)
while #i > 0
begin
select top 1 #id = id from #TableID
set #year=2008
while #year <= 2012
begin
set #month=1
while #month <= 12
begin
insert into #PaymentMonth values (#id, #year, #month, 0)
set #month = #month + 1
end
set #year = #year + 1
end
delete from #TableID where id = #id
set #i = #i - 1
end
--update table based on your initial data
update #PaymentMonth
set PaymentAmount = A.PaymentAmount from #LoanData as A
where LoanID = A.ID and PaymentYear = datepart(YEAR, A.PaymentDate) and PaymentMonth = datepart(MONTH, A.PaymentDate)
select * from #PaymentMonth
create table temp_loantable (
loanid bigint,
paymentdate date,
paymentmonth varchar(2),
paymentamount numeric(10,2))
Having a date range ( year & month) and then doing a left outer join should get the desired output.
select * from
(select years.n yearval , months.n monthval
from (values(2008), (2009), (2010), (2011), (2012)) years(n),
(values(1),(2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) months(n)) a
left outer join temp_loantable l
on year(paymentdate) = a.yearval
and month(paymentdate) = a.monthval
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);
I want to show all dates between two dates when there is any date data missing then its should show zero in val column .
declare #temp table (
id int identity(1,1) not null,
CDate smalldatetime ,
val int
)
INSERT STATEMENT FOR DATA TO CHECK
insert into #temp select '10/2/2012',1
insert into #temp select '10/3/2012',1
insert into #temp select '10/5/2012',1
insert into #temp select '10/7/2012',2
insert into #temp select '10/9/2012',2
insert into #temp select '10/10/2012',2
insert into #temp select '10/13/2012',2
insert into #temp select '10/15/2012',2
Retrieve records between first day of month and today
select * from #temp where CDate between '10/01/2012' AND '10/15/2012'
As i run this query its show me all data between these two dates but i want to also include missing dates with val=0
SQL FIDDLE WITH SAMPLE DATA
;with d(date) as (
select cast('10/01/2012' as datetime)
union all
select date+1
from d
where date < '10/15/2012'
)
select t.ID, d.date CDate, isnull(t.val, 0) val
from d
left join temp t
on t.CDate = d.date
order by d.date
OPTION (MAXRECURSION 0) -- use this if your dates are >99 days apart
You need to make up the dates, so I've use a recursive common table expression here.
SQL Fiddle
MAXRECURSION number
Specifies the maximum number of recursions allowed for this query. number is a nonnegative
integer between 0 and 32767. When 0 is specified, no limit is applied. If this option is
not specified, the default limit for the server is 100.
When the specified or default number for MAXRECURSION limit is reached during query
execution, the query is ended and an error is returned.
This will work as long as there are less than 2047 days between from and to dates
declare #from smalldatetime = '10/01/2012'
declare #to smalldatetime = '10/15/2012'
select t.id, dateadd(day, number,#from), isnull(val, 0) val from #temp t
right join master..spt_values s
on dateadd(d, s.number, #from) = t.CDate
where
datediff(day, #from, #to ) > s.number
and s.type = 'P'
I think the best way to do this is to create your own table with dates (you can also use master.dbo.spt_values, but I personally don't like that solution)
declare #Temp_Dates table (CDate datetime)
declare #Date datetime
select #Date = (select min(CDate) from temp)
while #Date <= (select max(CDate) from temp)
begin
insert into #Temp_Dates (CDate)
select #Date
select #Date = dateadd(dd, 1, #Date)
end
select D.CDate, isnull(T.id, 0) as id
from #Temp_Dates as D
left outer join temp as T on T.CDate = D.CDate
you can also use recursive solution with CTE
DECLARE #min DATETIME,
#max DATETIME,
#val INT
SELECT #min = Min(CDATE),
#max = Max(CDATE)
FROM TEMP
DECLARE #temp TABLE
(
CDATE SMALLDATETIME,
VAL INT
)
WHILE #min < #max
BEGIN
SELECT #val = VAL
FROM TEMP
WHERE CDATE = #min
INSERT #temp
VALUES (#min,
#val)
SET #min = Dateadd(D, 1, #min)
SET #val = 0
END
SELECT *
FROM #temp
Declare #temp Table(id int identity(1,1) not null,CDate smalldatetime ,val int)
insert into #temp select '10/2/2012',1
insert into #temp select '10/3/2012',1
insert into #temp select '10/5/2012',1
insert into #temp select '10/7/2012',2
insert into #temp select '10/9/2012',2
insert into #temp select '10/10/2012',2
insert into #temp select '10/13/2012',2
insert into #temp select '10/15/2012',2
DECLARE #startDate DATE= '10/01/2012'
DECLARE #endDate DATE= '10/15/2012'
SELECT t.Id, X.[Date],Val = COALESCE(t.val,0)
FROM
(SELECT [Date] = DATEADD(Day,Number,#startDate)
FROM master..spt_values
WHERE Type='P'
AND DATEADD(day,Number,#startDate) <= #endDate)X
LEFT JOIN #temp t
ON X.[Date] = t.CDate
using a recursive cte with min and max
declare #T table (id int identity(1,1) primary key, dt date not null, val int not null);
insert into #T (dt, val) values
('10/2/2012',1)
, ('10/3/2012',1)
, ('10/5/2012',1)
, ('10/7/2012',2)
, ('10/9/2012',2)
, ('10/10/2012',2)
, ('10/13/2012',2)
, ('10/15/2012',2);
--select * from #T;
with cte as
( select min(dt) as dt, max(dt) as mx
from #T
union all
select dateadd(dd, 1, dt), mx
from CTE
where dt < mx
)
select c.dt, isnull(t.val, 0) as val
from cte c
left join #T t
on c.dt = t.dt
order by c.dt
option (maxrecursion 0);
dt val
---------- -----------
2012-10-02 1
2012-10-03 1
2012-10-04 0
2012-10-05 1
2012-10-06 0
2012-10-07 2
2012-10-08 0
2012-10-09 2
2012-10-10 2
2012-10-11 0
2012-10-12 0
2012-10-13 2
2012-10-14 0
2012-10-15 2