Reputation: 3
I have the following tables. As Canada day is July 1st and my data source is in business days only. Notice that July 1st is missing from TABLE_A (name: conm
) which is my source table.
CREATE TABLE [dbo].[comn]
(
CONM varchar(48),
valuedate datetime,
closeprice decimal(5,2)
)
GO
INSERT INTO comn VALUES ('SAPUTO INC', '2016-06-27', 37.66);
INSERT INTO comn VALUES ('SAPUTO INC', '2016-06-28', 38.34);
INSERT INTO comn VALUES ('SAPUTO INC', '2016-06-29', 38.48);
INSERT INTO comn VALUES ('SAPUTO INC', '2016-06-30', 38.37);
INSERT INTO comn VALUES ('SAPUTO INC', '2016-07-04', 38.12);
INSERT INTO comn VALUES ('SAPUTO INC', '2016-07-05', 38.59);
INSERT INTO comn VALUES ('SAPUTO INC', '2016-07-06', 38.75);
GO
I also have TABLE_B (businessdaysCAN
) with all Canadian holidays.
CREATE TABLE [dbo].[businessdaysCAN]
(
valuedate datetime,
isholidayCA decimal(5,2)
)
GO
INSERT INTO businessdaysCAN VALUES ('2016-02-15',1);
INSERT INTO businessdaysCAN VALUES ('2016-03-25', 1);
INSERT INTO businessdaysCAN VALUES ('2016-05-23', 1);
INSERT INTO businessdaysCAN VALUES ('2016-07-01', 1);
INSERT INTO businessdaysCAN VALUES ('2016-08-01', 1);
INSERT INTO businessdaysCAN VALUES ('2016-09-05', 1);
INSERT INTO businessdaysCAN VALUES ('2016-10-10', 1);
GO
I would like to have an output table such as when there is a Canadian holiday, I have the holiday date in my final table with the price of the day before.
CONM valuedate closeprice
------------------------------------------------
SAPUTO INC 2016-06-27 00:00:00.000 37.66
SAPUTO INC 2016-06-28 00:00:00.000 38.34
SAPUTO INC 2016-06-29 00:00:00.000 38.48
SAPUTO INC 2016-06-30 00:00:00.000 38.37
SAPUTO INC 2016-07-04 00:00:00.000 38.12
SAPUTO INC 2016-07-05 00:00:00.000 38.59
SAPUTO INC 2016-07-06 00:00:00.000 38.75
Upvotes: 0
Views: 80
Reputation: 1080
You can try it
DECLARE @comn table (conm varchar(10) , valueDate date, closePrice decimal(10,2));
DECLARE @businessdaysCAN table(calDate date, isHolidayCA bit);
INSERT @comn (conm,valueDate,closePrice) VALUES
('SAPUTO INC','2016-06-27',37.66)
,('SAPUTO INC','2016-06-28',38.34)
,('SAPUTO INC','2016-06-29',38.48)
,('SAPUTO INC','2016-06-30',38.37)
,('SAPUTO INC','2016-07-04',38.12)
,('SAPUTO INC','2016-07-05',38.59)
,('SAPUTO INC','2016-07-06',38.75);
INSERT @businessdaysCAN (calDate, isHolidayCA) VALUES
('2016-05-23',1)
,('2016-07-01',1)
,('2016-08-01',1);
WITH
cteMinMaxDates as
(
select
conm
,Min(valueDate) as startdate
,Max(valueDate) as enddate
from @comn
Group by conm
),
cteAllDates
AS (SELECT conm
,startdate
,enddate
,valueDate = ( SELECT valueDate FROM @comn a where a.valueDate = t.startdate and a.conm = t.conm)
FROM cteMinMaxDates t
UNION ALL
SELECT conm
,Dateadd(day, 1, startdate) startdate
,enddate
,valueDate = CASE WHEN ( SELECT valueDate FROM @comn a where a.valueDate = startdate and a.conm = conm) IS NULL THEN valueDate ELSE ( SELECT valueDate FROM @comn a where a.valueDate = startdate and a.conm = conm) END
FROM cteAllDates
WHERE startdate < enddate)
SELECT
D.conm
,D.startdate valuedate
,IsNull(A.closePrice , B.closePrice) closePrice
FROM cteAllDates D
LEFT JOIN @comn A
ON D.conm = A.conm
AND D.startdate = A.valuedate
LEFT JOIN @comn B
ON D.valuedate = B.valuedate
WHERE A.valuedate IS NOT NULL
OR EXISTS (SELECT 1 FROM @businessdaysCAN c where d.startdate = c.calDate)
Result
conm valuedate closePrice
---------- ---------- ---------------------------------------
SAPUTO INC 2016-06-27 37.66
SAPUTO INC 2016-06-28 38.34
SAPUTO INC 2016-06-29 38.48
SAPUTO INC 2016-06-30 38.37
SAPUTO INC 2016-07-01 38.37
SAPUTO INC 2016-07-04 38.12
SAPUTO INC 2016-07-05 38.59
SAPUTO INC 2016-07-06 38.75
If you need all dates in the range remove the
WHERE A.valuedate IS NOT NULL
OR EXISTS (SELECT 1 FROM @table_b c where d.startdate = c.calDate)
Result
conm valuedate closePrice
---------- ---------- ---------------------------------------
SAPUTO INC 2016-06-27 37.66
SAPUTO INC 2016-06-28 38.34
SAPUTO INC 2016-06-29 38.48
SAPUTO INC 2016-06-30 38.37
SAPUTO INC 2016-07-01 38.37
SAPUTO INC 2016-07-02 38.37
SAPUTO INC 2016-07-03 38.37
SAPUTO INC 2016-07-04 38.12
SAPUTO INC 2016-07-05 38.59
SAPUTO INC 2016-07-06 38.75
Upvotes: 0
Reputation: 635
This will give you a table of all dates since 01/01/2000, if you need a longer or shorter range just change the date in the code (if longer you may need to add an additional "PASS" in the CTE but 65k is a lot of days!); you can then LEFT JOIN
or RIGHT JOIN
this to your table and use the values from this where yours is null...
WITH
_PASS0 AS (SELECT 1 AS Num UNION ALL SELECT 1), --2 ROWS
_PASS1 AS (SELECT 1 AS Num FROM _PASS0 AS A, _PASS0 AS B), --4 ROWS
_PASS2 AS (SELECT 1 AS Num FROM _PASS1 AS A, _PASS1 AS B), --16 ROWS
_PASS3 AS (SELECT 1 AS Num FROM _PASS2 AS A, _PASS2 AS B), --256 ROWS
_PASS4 AS (SELECT 1 AS Num FROM _PASS3 AS A, _PASS3 AS B), --65,536 ROWS
_TALLY AS (SELECT 0 AS Number UNION ALL SELECT ROW_NUMBER() OVER(ORDER BY Num) AS Number FROM _PASS4)
SELECT dateadd(DAY,Number,'20000101') AS CalendarDate
FROM _Tally
WHERE Number <= datediff(DAY,'20000101',getdate());
Upvotes: 0