I have a 2 tables that look like this:
MonthEndDate
2016-06-30 00:00:00.000
2016-07-31 00:00:00.000
2016-08-31 00:00:00.000
2016-09-30 00:00:00.000
2016-10-31 00:00:00.000
2016-11-30 00:00:00.000
2016-12-31 00:00:00.000
AND
MonthEndDate CustomerId Flag
2016-06-30 00:00:00.000 123 1
2016-07-31 00:00:00.000 123 1
2016-08-31 00:00:00.000 123 1
2016-09-30 00:00:00.000 123 1
I would like an output that looks like this:
MonthEndDate CustomerId Flag
2016-06-30 00:00:00.000 123 1
2016-07-31 00:00:00.000 123 1
2016-08-31 00:00:00.000 123 1
2016-09-30 00:00:00.000 123 1
2016-10-31 00:00:00.000 123 0
2016-11-30 00:00:00.000 123 0
2016-12-31 00:00:00.000 123 0
Table 1 is a DimDate table that has month end date.
Table
2 is the CustomerInfo table.
Each customer has a Flag set to 1 whenever that customer has a value for the given Month End.
I want to get an output that will have every Month End Date (that's why I'm suing DimDate table) and when a customer does not have a value for the Month End I want the flag to show 0.
I'm using SQL Server 2005
Here is some sample code I used:
DECLARE #table1 TABLE
(
MonthEndDate DATETIME
)
INSERT INTO #table1
VALUES('2016-06-30 00:00:00.000')
INSERT INTO #table1
VALUES('2016-07-31 00:00:00.000')
INSERT INTO #table1
VALUES('2016-08-31 00:00:00.000')
INSERT INTO #table1
VALUES('2016-09-30 00:00:00.000')
INSERT INTO #table1
VALUES('2016-10-31 00:00:00.000')
INSERT INTO #table1
VALUES('2016-11-30 00:00:00.000')
INSERT INTO #table1
VALUES('2016-12-31 00:00:00.000')
DECLARE #table2 TABLE
(
MonthEndDate DATETIME
,CustomerId INT
,Flag INT
)
INSERT INTO #table2
VALUES('2016-06-30 00:00:00.000',123,1)
INSERT INTO #table2
VALUES('2016-07-31 00:00:00.000',123,1)
INSERT INTO #table2
VALUES('2016-08-31 00:00:00.000',123,1)
INSERT INTO #table2
VALUES('2016-09-30 00:00:00.000',123,1)
SELECt * FROM #table1
SELECt * FROM #table2
You need to do a CROSS JOIN on to get all combinations of MonthEndDate and CustomerId. When you have that, do a LEFT JOIN on table2 to get the Flag:
SELECT
t1.MonthEndDate,
c.CustomerId,
Flag = ISNULL(t2.Flag, 0)
FROM #table1 t1
CROSS JOIN (SELECT DISTINCT CustomerId FROM #table2) c
LEFT JOIN #table2 t2
ON t1.MonthEndDate = t2.MonthEndDate
AND c.CustomerId = t2.CustomerId
I think you just want a left join:
select t1.*, coalesce(t2.flag, 0) as flag
from #table1 t1 left join
#table2 t2
on t1.MonthEndDate = t2.MonthEndDate;
Here is the table that I am working with:
MemberID MembershipStartDate MembershipEndDate
=================================================================
123 2010-01-01 00:00:00.000 2012-12-31 00:00:00.000
123 2011-01-01 00:00:00.000 2012-12-31 00:00:00.000
123 2013-05-01 00:00:00.000 2013-12-31 00:00:00.000
123 2014-01-01 00:00:00.000 2014-12-31 00:00:00.000
123 2015-01-01 00:00:00.000 2015-03-31 00:00:00.000
What I want is to create one row that shows continuous membership,
and a second row if the membership breaks by more than 2 days, with a new start and end date..
So the output I am looking for is like:
MemberID MembershipStartDate MembershipEndDate
=================================================================
123 2010-01-01 00:00:00.000 2012-12-31 00:00:00.000
123 2013-05-01 00:00:00.000 2015-03-31 00:00:00.000
There is a memberID field attached to these dates which is how they are grouped.
I've had to deal with this kind of thing before
I use something like this
USE tempdb
--Create test Data
DECLARE #Membership TABLE (MemberID int ,MembershipStartDate date,MembershipEndDate date)
INSERT #Membership
(MemberID,MembershipStartDate,MembershipEndDate)
VALUES (123,'2010-01-01','2012-12-31'),
(123,'2011-01-01','2012-12-31'),
(123,'2013-05-01','2013-12-31'),
(123,'2014-01-01','2014-12-31'),
(123,'2015-01-01','2015-03-31')
--Create a table to hold all the dates that might be turning points
DECLARE #SignificantDates Table(MemberID int, SignificantDate date, IsMember bit DEFAULT 0)
--Populate table with the start and end dates as well as the days just before and just after each period
INSERT #SignificantDates (MemberID ,SignificantDate)
SELECT MemberID, MembershipStartDate FROM #Membership
UNION
SELECT MemberID,DATEADD(day,-1,MembershipStartDate ) FROM #Membership
UNION
SELECT MemberID,MembershipEndDate FROM #Membership
UNION
SELECT MemberID,DATEADD(day,1,MembershipEndDate) FROM #Membership
--Set the is member flag for each date that is covered by a membership
UPDATE sd SET IsMember = 1
FROM #SignificantDates sd
JOIN #Membership m ON MembershipStartDate<= SignificantDate AND SignificantDate <= MembershipEndDate
--To demonstrate what we're about to do, Select all the dates and show the IsMember Flag and the previous value
SELECT sd.MemberID, sd.SignificantDate,sd.IsMember, prv.prevIsMember
FROM
#SignificantDates sd
JOIN (SELECT
MemberId,
SignificantDate,
IsMember,
Lag(IsMember,1) OVER (PARTITION BY MemberId ORDER BY SignificantDate desc) AS prevIsMember FROM #SignificantDates
) as prv
ON sd.MemberID = prv.MemberID
AND sd.SignificantDate = prv.SignificantDate
ORDER BY sd.MemberID, sd.SignificantDate
--Delete the ones where the flag is the same as the previous value
delete sd
FROM
#SignificantDates sd
JOIN (SELECT MemberId, SignificantDate,IsMember, Lag(IsMember,1) OVER (PARTITION BY MemberId ORDER BY SignificantDate) AS prevIsMember FROM #SignificantDates ) as prv
ON sd.MemberID = prv.MemberID
AND sd.SignificantDate = prv.SignificantDate
AND prv.IsMember = prv.prevIsMember
--SELECT the Start date for each period of membership and the day before the following period of non membership
SELECT
nxt.MemberId,
nxt.SignificantDate AS MembershipStartDate,
DATEADD(day,-1,nxt.NextSignificantDate) AS MembershipEndDate
FROM
(
SELECT
MemberID,
SignificantDate,
LEAd(SignificantDate,1) OVER (PARTITION BY MemberId ORDER BY SignificantDate) AS NextSignificantDate,
IsMember
FROM #SignificantDates
) nxt
WHERE nxt.IsMember = 1
I have a Table with these columns
- ProjectId
- Generation
- Expected
- CarryOver
I am trying to update my already populated table in this fashion:
Generation = Integral Part of ((Generation + CarryOver of Previous Row)/10 )
CarryOver = decimal part of ((Generation + CarryOver of Previous Row)/10 )
where Previous Row and Current Row both have same projectId
Below is the query am using to achieve this:
UPDATE TTable
SET
TTable.Expected=(TTable.Generation+ ISNULL(STable.CarryOver,0)),
TTable.CarryOver =(TTable.Generation+ISNULL(STable.CarryOver,0))-CONVERT(INT,(TTable.Generation+ISNULL(STable.CarryOver,0)))
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
)TTable,
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) STable
Where
TTable.RowNumber = STable.RowNumber+1 AND
TTable.ProjectId = STable.ProjectId
....but something strange happens, update happens only for first two rows. For other rows,
ISNULL(STable.CarryOver,0) returns 0. why??
Please help me. or suggest some other way to achieve this
EDIT: on running the query
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56.748 56 0.748
10 2011-11-01 00:00:00.000 11 2011 12.004 12 0.752
10 2011-12-01 00:00:00.000 12 2011 10.632 10 0.632
10 2012-01-01 00:00:00.000 01 2012 11.928 11 0.928
10 2012-02-01 00:00:00.000 02 2012 7.580 7 0.580
100 2011-12-01 00:00:00.000 12 2011 5.897 5 0.897
100 2012-01-01 00:00:00.000 01 2012 0.881 1 0.778
data is generated as shown above. notice how the logic doesn't work after 3row
Original Ouput. Before running the update query:
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56.748 56 0.748
10 2011-11-01 00:00:00.000 11 2011 12.004 NULL NULL
10 2011-12-01 00:00:00.000 12 2011 10.632 NULL NULL
10 2012-01-01 00:00:00.000 01 2012 11.928 NULL NULL
10 2012-02-01 00:00:00.000 02 2012 7.580 NULL NULL
100 2011-12-01 00:00:00.000 12 2011 5.897 5 0.897
100 2012-01-01 00:00:00.000 01 2012 0.881 NULL NULL
The following query will give you the expected results
(Test code https://gist.github.com/1969171 for those that want to play)
PID Date M Y Generation Expected CarryOver
10 2011-10-01 10 2011 56.748 56 0.748
10 2011-11-01 11 2011 12.004 12 0.752
10 2011-12-01 12 2011 10.632 11 0.384
10 2012-01-01 01 2012 11.928 12 0.312
10 2012-02-01 02 2012 7.58 7 0.892
100 2011-12-01 12 2011 5.897 5 0.897
100 2012-01-01 01 2012 0.881 1 0.778
DECLARE rowItems CURSOR FOR
SELECT ProjectId, [Month], [Year], Generation FROM TTable
ORDER BY ProjectId,[Year] ,CAST([Month] as int)
DECLARE #p int, #m VARCHAR(5), #y int, #g FLOAT, #priorP int,
#carryOver FLOAT, #expected FLOAT
OPEN rowItems
FETCH NEXT FROM rowItems INTO #p, #m, #y, #g
SET #priorP = -1
SET #carryOver = 0.0
WHILE ##FETCH_STATUS = 0
BEGIN
IF NOT #p = #priorP SET #carryOver = 0.0
SET #expected = #g+#carryOver
SET #carryOver = ROUND(#expected-FLOOR(#expected),3,0)
UPDATE TTable
SET EXPECTED = FLOOR(#expected), CarryOver = #carryOver
WHERE ProjectId = #p and [Month] = #m and [Year] = #y
SET #priorP = #p
FETCH NEXT FROM rowItems INTO #p, #m, #y, #g
END
CLOSE rowItems
DEALLOCATE rowItems
SELECT * FROM TTable
You need to do this in a loop. The query you use is getting the original value of the carry over column not the updated value.
Sorry about the confutation. Here is my suggestion.
Test data
CREATE TABLE TTable
(
ProjectId INT,
MonthYear DATETIME,
Month VARCHAR(5),
Year INT,
Generation FLOAT,
Expected FLOAT,
CarryOver FLOAT
)
INSERT INTO TTable
VALUES
(10,'2011-10-01 00:00:00.000','10',2011,56.748,56,0.748),
(10,'2011-11-01 00:00:00.000','11',2011,12.004,NULL,NULL),
(10,'2011-12-01 00:00:00.000','12',2011,10.632,NULL,NULL),
(10,'2012-01-01 00:00:00.000','01',2012,11.928,NULL,NULL),
(10,'2012-02-01 00:00:00.000','02',2012,7.580,NULL,NULL),
(100,'2011-12-01 00:00:00.000','12',2011,5.897,5,0.897),
(100,'2012-01-01 00:00:00.000','01',2012,0.881,NULL,NULL)
Query
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY tbl.ProjectId,tbl.Month) AS RowNbr,
tbl.ProjectId,
tbl.MonthYear,
tbl.Month,
tbl.Year,
tbl.Generation,
(
FLOOR(tbl.Generation)
) AS Expected,
(
ROUND(tbl.Generation-FLOOR(tbl.Generation),3,0)
)AS CarryOver
FROM
TTable AS tbl
)
SELECT
CTE.ProjectId,
CTE.MonthYear,
CTE.Month,
CTE.Year,
CTE.Generation,
CTE.Expected,
(
CTE.CarryOver+ISNULL(Previus.CarryOver,0)
) AS CarryOver
FROM
CTE
LEFT JOIN CTE AS Previus
ON CTE.RowNbr=Previus.RowNbr+1
Result
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56,748 56 0,748
10 2011-11-01 00:00:00.000 11 2011 12,004 12 0,752
10 2011-12-01 00:00:00.000 12 2011 10,632 10 0,636
10 2012-01-01 00:00:00.000 01 2012 11,928 11 1,56
10 2012-02-01 00:00:00.000 02 2012 7,58 7 1,508
100 2011-12-01 00:00:00.000 12 2011 5,897 5 1,477
100 2012-01-01 00:00:00.000 01 2012 0,881 0 1,778
Update query
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY tbl.ProjectId,tbl.Month) AS RowNbr,
tbl.ProjectId,
tbl.MonthYear,
tbl.Month,
tbl.Year,
tbl.Generation,
(
FLOOR(tbl.Generation)
) AS Expected,
(
ROUND(tbl.Generation-FLOOR(tbl.Generation),3,0)
)AS CarryOver
FROM
TTable AS tbl
)
UPDATE TTable
SET TTable.CarryOver=CTE.CarryOver+ISNULL(Previus.CarryOver,0),
TTable.Expected=CTE.Expected
FROM
(
SELECT
ROW_NUMBER() OVER(ORDER BY TTable.ProjectId,
TTable.Month) AS RowNbr,
TTable.CarryOver,
TTable.Expected
FROM
TTable
) AS TTable
JOIN CTE
ON TTable.RowNbr=CTE.RowNbr
LEFT JOIN CTE AS Previus
ON CTE.RowNbr=Previus.RowNbr+1
Now when selecting for the updated table. Then you will get this result:
ProjectId MonthYear Month Year Generation Expected CarryOver
10 2011-10-01 00:00:00.000 10 2011 56,748 56 0,748
10 2011-11-01 00:00:00.000 11 2011 12,004 12 0,752
10 2011-12-01 00:00:00.000 12 2011 10,632 10 0,636
10 2012-01-01 00:00:00.000 01 2012 11,928 11 1,56
10 2012-02-01 00:00:00.000 02 2012 7,58 7 1,508
100 2011-12-01 00:00:00.000 12 2011 5,897 5 1,477
100 2012-01-01 00:00:00.000 01 2012 0,881 0 1,778
This is how I did it:
For updating the first record corresponding to each ProjectId:
Update TTable
SET
TTable.Expected=(TTable.Generation/10),
TTable.CarryOver =(TTable.Generation/10)-CONVERT(INT,TTable.Generation/10)
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) TTable where TTable.RowNumber in
(
Select Min(TTable.RowNumber)as RowNumber From
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable GROUP by ProjectId
)
And for updating rest of the records:
DECLARE #RowNumber INT = -99
DECLARE #CarryOver decimal(4,3) =0
DECLARE #Expected INT =0
DECLARE #ProjectId int =0
WHILE #RowNumber IS NOT NULL
BEGIN
SET #RowNumber= (SELECT MIN(RowNumber) FROM (
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable
WHERE TTable.RowNumber> #RowNumber)
IF #RowNumber IS NULL
BREAK
SELECT #CarryOver= TTable.CarryOver,#ProjectId=TTable.ProjectId FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable where TTable.RowNumber = #RowNumber
UPDATE TTable
SET TTable.Expected =(TTable.Generation+#CarryOver)/10,
CarryOver = (TTable.Generation+#CarryOver)/10 - CONVERT(INT,(TTable.Generation+#CarryOver)/10)
FROM
(
SELECT ROW_NUMBER()OVER(order by ProjectId,MonthYear) as RowNumber,ProjectId,
[MonthYear],[Month],[Generation],[Expected],[CarryOver]
FROM #SRECEsimated
) as TTable where TTable.RowNumber = #RowNumber+1 AND TTable.ProjectId=#ProjectId
END
I have some data on my table like:
DAY | QTY | Name
1/1/2010 | 1 | jack
5/1/2010 | 5 | jack
2/1/2010 | 3 | wendy
5/1/2010 | 2 | wendy
my goal is to have a SP requesting a period of time (example: '2010-1-1' to '2010-1-5'), and get no gaps.
Output example:
DAY | QTY | Name
1/1/2010 | 1 | jack
2/1/2010 | 0 | jack
3/1/2010 | 0 | jack
4/1/2010 | 0 | jack
5/1/2010 | 5 | jack
1/1/2010 | 3 | wendy
2/1/2010 | 0 | wendy
3/1/2010 | 0 | wendy
4/1/2010 | 2 | wendy
5/1/2010 | 0 | wendy
Any gaps is filled with 0-
I know that I can create a loop to will solve me the problem, but is very slow.
Does anyone have any ideas how to optimize this?
WITH DateRangeCTE([d]) AS
(
SELECT
CONVERT(DATETIME, '2010-01-01') AS [d]
UNION ALL
SELECT
DATEADD(d, 1, [d]) AS [d]
FROM
DateRangeCTE
WHERE [d] < DATEADD(d, -1, CONVERT(DATETIME, '2010-1-31'))
)
SELECT
DateRangeCTE.d, YourTable.Qty, YourTable.Name
FROM DateRangeCTE
LEFT JOIN YourTable ON DateRangeCTE.d = YourTable.DAY
If you get the error "The statement terminated. The maximum recursion 100 has been exhausted before statement completion." then use the maxrecursion hint.
Here's a solution that you can use if you don't know the date range in advance. It derives the date range based on the data. The solution uses a numbers table, which uses an existing table in the master database (spt_values).
WITH MinMax AS
( SELECT DISTINCT [Name],
MIN([DAY]) OVER () AS min_day, MAX([DAY]) OVER () AS max_day
FROM mytable
)
, DateRange AS
( SELECT MinMax.[Name], DATEADD(mm, n.number, MinMax.min_day) AS [Date]
FROM MinMax
JOIN master.dbo.spt_values n ON n.type = 'P'
AND DATEADD(mm, n.number, MinMax.min_day) <= MinMax.max_day
)
SELECT dr.[Name], COALESCE(mt.[qty], 0) AS [QTY], dr.Date
FROM DateRange dr
LEFT OUTER JOIN MyTable mt ON dr.Name = mt.Name AND mt.Day = dr.Date
ORDER BY dr.Name, dr.Date ;
Here's another way:
DECLARE #output TABLE (
DateValue datetime,
Qty varchar(50),
LastName varchar(25)
PRIMARY KEY (DateValue, LastName)
)
DECLARE #minMonth datetime, #maxMonth datetime, #lastName varchar(25)
-- whatever your business logic dictates for these
SET #minMonth = '01/01/2010'
SET #maxMonth = '12/01/2010';
with cte as (
SELECT #minMonth AS DateValue
UNION ALL
SELECT DATEADD(month, 1, DateValue)
FROM cte
WHERE DATEADD(month, 1, DateValue) <= #maxMonth
)
INSERT INTO #output (DateValue, Qty, LastName)
SELECT cte.DateValue,
ISNULL(tbl.Alias,0),
tbl.Name
FROM cte LEFT JOIN dbo.YourTable tbl ON tbl.[Day] = cte.Mth
UPDATE #output SET
LastName = CASE WHEN LastName IS NULL THEN #lastName ELSE LastName END,
#lastName = LastName
FROM #output
SELECT * FROM #output
I leave were the correct answer based on the help of everyone
-- dummy data
declare #table table
(
DAY datetime,
QTY int,
Name nvarchar (500) NULL
)
insert #table values('2010-1-1', 1, 'jack')
insert #table values('2010-1-3', 5, 'jack')
insert #table values('2010-1-2', 3 , 'wendy')
insert #table values('2010-1-6', 2 , 'wendy')
-- algorithm
DECLARE #output TABLE (
DAY datetime,
Qty int,
Name varchar(25)
)
DECLARE #minMonth datetime, #maxMonth datetime, #lastName varchar(25)
SET #minMonth = '2010-1-1'
SET #maxMonth = '2010-1-6';
WITH cte AS (
SELECT #minMonth AS DateValue
UNION ALL
SELECT DATEADD(day, 1, DateValue)
FROM cte
WHERE DATEADD(day, 1, DateValue) <= #maxMonth
)
INSERT INTO #output
SELECT
cte.DateValue,
ISNULL(tbl.qty,0),
tbl.Name
FROM
cte cross JOIN
#table tbl
update #output
set qty = 0
where cast(DAY as nvarchar)+'#'+cast(Qty as nvarchar)+'#'+Name in
(
select cast(DAY as nvarchar)+'#'+cast(Qty as nvarchar)+'#'+Name from #output
except
select cast(DAY as nvarchar)+'#'+cast(Qty as nvarchar)+'#'+Name from #table
)
SELECT DAY, sum(qty) as qty, Name
FROM #output
GROUP BY DAY, Name
order by 3,1
and the output that I pretend
2010-01-01 00:00:00.000 1 jack
2010-01-02 00:00:00.000 0 jack
2010-01-03 00:00:00.000 5 jack
2010-01-04 00:00:00.000 0 jack
2010-01-05 00:00:00.000 0 jack
2010-01-06 00:00:00.000 0 jack
2010-01-01 00:00:00.000 0 wendy
2010-01-02 00:00:00.000 3 wendy
2010-01-03 00:00:00.000 0 wendy
2010-01-04 00:00:00.000 0 wendy
2010-01-05 00:00:00.000 0 wendy
2010-01-06 00:00:00.000 2 wendy
Although the solution is correct, doesn't fit my need because recursion limitation.
Hopefully this script will help anyone with similar questions
Thank you to all