See if this gives you what you want. It seems to work against your test data.
--demo setup
drop table if exists dbo.product
go
create table dbo.Product
(
ProductId int,
ProductTitle varchar(55),
ProductCategory varchar(255),
Loaddate datetime
)
insert into dbo.Product
values
(1,'Table','ABCD','3/4/2018')
,(1,'Table','ABCD','3/5/2018')
,(1,'Table','ABCD','3/5/2018')
,(1,'Table','ABCD','3/6/2018')
,(1,'Table','XYZ','3/7/2018')
,(1,'Table','XYZ','3/8/2018')
,(1,'Table','XYZ','3/8/2018')
,(1,'Table','XYZ','3/9/2018')
,(1,'Table-Dinner', 'GHI','3/10/2018')
,(1,'Table-Dinner', 'GHI','3/11/2018')
Using a common table expression and lag
against the other columns you are interested in, I populate a new column called ischange
;WITH BaseDataAndIsChanged
AS (
SELECT *
,CASE
WHEN (
lag(ProductTitle, 1, '') OVER (
PARTITION BY ProductId ORDER BY LoadDate
)
) = ProductTitle
AND (
lag(ProductCategory, 1, '') OVER (
PARTITION BY ProductId ORDER BY LoadDate
)
) = ProductCategory
THEN 0
ELSE 1
END ischange
FROM dbo.product
)
select * from BaseDataAndIsChanged
| ProductId | ProductTitle | ProductCategory | Loaddate | ischange |
|-----------|--------------|-----------------|-------------------------|----------|
| 1 | Table | ABCD | 2018-03-04 00:00:00.000 | 1 |
| 1 | Table | ABCD | 2018-03-05 00:00:00.000 | 0 |
| 1 | Table | ABCD | 2018-03-05 00:00:00.000 | 0 |
| 1 | Table | ABCD | 2018-03-06 00:00:00.000 | 0 |
| 1 | Table | XYZ | 2018-03-07 00:00:00.000 | 1 |
| 1 | Table | XYZ | 2018-03-08 00:00:00.000 | 0 |
| 1 | Table | XYZ | 2018-03-08 00:00:00.000 | 0 |
| 1 | Table | XYZ | 2018-03-09 00:00:00.000 | 0 |
| 1 | Table-Dinner | GHI | 2018-03-10 00:00:00.000 | 1 |
| 1 | Table-Dinner | GHI | 2018-03-11 00:00:00.000 | 0 |
I then use another common table expression to generate two additional columns (BeginDate
and EndDate
).
;WITH BaseDataAndIsChanged
AS (
SELECT *
,CASE
WHEN (
lag(ProductTitle, 1, '') OVER (
PARTITION BY ProductId ORDER BY LoadDate
)
) = ProductTitle
AND (
lag(ProductCategory, 1, '') OVER (
PARTITION BY ProductId ORDER BY LoadDate
)
) = ProductCategory
THEN 0
ELSE 1
END ischange
FROM dbo.product
)
--select * from BaseDataAndIsChanged
,BaseDataAndBeginEndDates
AS (
SELECT *
,CASE
WHEN ischange = 1
THEN Loaddate
END AS BeginDate
,CASE
WHEN lead(ischange, 1, '') OVER (
PARTITION BY ProductId ORDER BY LoadDate
) = 1
THEN Loaddate
END AS EndDate
FROM BaseDataAndIsChanged
)
select * from BaseDataAndBeginEndDates
| ProductId | ProductTitle | ProductCategory | Loaddate | ischange | BeginDate | EndDate |
|-----------|--------------|-----------------|-------------------------|----------|-------------------------|-------------------------|
| 1 | Table | ABCD | 2018-03-04 00:00:00.000 | 1 | 2018-03-04 00:00:00.000 | NULL |
| 1 | Table | ABCD | 2018-03-05 00:00:00.000 | 0 | NULL | NULL |
| 1 | Table | ABCD | 2018-03-05 00:00:00.000 | 0 | NULL | NULL |
| 1 | Table | ABCD | 2018-03-06 00:00:00.000 | 0 | NULL | 2018-03-06 00:00:00.000 |
| 1 | Table | XYZ | 2018-03-07 00:00:00.000 | 1 | 2018-03-07 00:00:00.000 | NULL |
| 1 | Table | XYZ | 2018-03-08 00:00:00.000 | 0 | NULL | NULL |
| 1 | Table | XYZ | 2018-03-08 00:00:00.000 | 0 | NULL | NULL |
| 1 | Table | XYZ | 2018-03-09 00:00:00.000 | 0 | NULL | 2018-03-09 00:00:00.000 |
| 1 | Table-Dinner | GHI | 2018-03-10 00:00:00.000 | 1 | 2018-03-10 00:00:00.000 | NULL |
| 1 | Table-Dinner | GHI | 2018-03-11 00:00:00.000 | 0 | NULL | NULL |
Here's the complete solution where we pull everything together and find the min(BeginDate) and Max(EndDate) group by the ProductId and other columns you're interested in
--demo setup
drop table if exists dbo.product
go
create table dbo.Product
(
ProductId int,
ProductTitle varchar(55),
ProductCategory varchar(255),
Loaddate datetime
)
insert into dbo.Product
values
(1,'Table','ABCD','3/4/2018')
,(1,'Table','ABCD','3/5/2018')
,(1,'Table','ABCD','3/5/2018')
,(1,'Table','ABCD','3/6/2018')
,(1,'Table','XYZ','3/7/2018')
,(1,'Table','XYZ','3/8/2018')
,(1,'Table','XYZ','3/8/2018')
,(1,'Table','XYZ','3/9/2018')
,(1,'Table-Dinner', 'GHI','3/10/2018')
,(1,'Table-Dinner', 'GHI','3/11/2018')
;WITH BaseDataAndIsChanged
AS (
SELECT *
,CASE
WHEN (
lag(ProductTitle, 1, '') OVER (
PARTITION BY ProductId ORDER BY LoadDate
)
) = ProductTitle
AND (
lag(ProductCategory, 1, '') OVER (
PARTITION BY ProductId ORDER BY LoadDate
)
) = ProductCategory
THEN 0
ELSE 1
END ischange
FROM dbo.product
)
--select * from BaseDataAndIsChanged
,BaseDataAndBeginEndDates
AS (
SELECT *
,CASE
WHEN ischange = 1
THEN Loaddate
END AS BeginDate
,CASE
WHEN lead(ischange, 1, '') OVER (
PARTITION BY ProductId ORDER BY LoadDate
) = 1
THEN Loaddate
END AS EndDate
FROM BaseDataAndIsChanged
)
--select * from BaseDataAndBeginEndDates
SELECT productid
,ProductTitle
,ProductCategory
,min(begindate) AS BeginDate
,isnull(max(EndDate), '9999-12-31') AS EndDate
FROM BaseDataAndBeginEndDates
GROUP BY productid
,ProductTitle
,ProductCategory
| productid | ProductTitle | ProductCategory | BeginDate | EndDate |
|-----------|--------------|-----------------|-------------------------|-------------------------|
| 1 | Table | ABCD | 2018-03-04 00:00:00.000 | 2018-03-06 00:00:00.000 |
| 1 | Table | XYZ | 2018-03-07 00:00:00.000 | 2018-03-09 00:00:00.000 |
| 1 | Table-Dinner | GHI | 2018-03-10 00:00:00.000 | 9999-12-31 00:00:00.000 |
I'd also be curious to know if a simple cursor
solution gives you the performance you want. Try this example against your dataset.
--demo setup
drop table if exists dbo.product
go
create table dbo.Product
(
ProductId int,
ProductTitle varchar(55),
ProductCategory varchar(255),
Loaddate datetime
)
insert into dbo.Product
values
(1,'Table','ABCD','3/4/2018')
,(1,'Table','ABCD','3/5/2018')
,(1,'Table','ABCD','3/5/2018')
,(1,'Table','ABCD','3/6/2018')
,(1,'Table','XYZ','3/7/2018')
,(1,'Table','XYZ','3/8/2018')
,(1,'Table','XYZ','3/8/2018')
,(1,'Table','XYZ','3/9/2018')
,(1,'Table-Dinner', 'GHI','3/10/2018')
,(1,'Table-Dinner', 'GHI','3/11/2018')
----------------
DECLARE @ProductId INT
DECLARE @ProductTitle VARCHAR(55)
DECLARE @ProductCategory VARCHAR(255)
DECLARE @LoadDate DATETIME
DECLARE @PrevProductId INT
DECLARE @PrevProductTitle VARCHAR(55)
DECLARE @PrevProductCategory VARCHAR(255)
DECLARE @PrevLoadDate DATETIME
DECLARE @BeginDate DATETIME
DECLARE @EndDate DATETIME
DECLARE @ResultTable TABLE (
ProductId INT
,ProductTitle VARCHAR(55)
,ProductCategory VARCHAR(255)
,BeginDate DATETIME
,EndDate DATETIME
)
DECLARE _CURSOR CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT ProductId
,ProductTitle
,ProductCategory
,LoadDate
FROM dbo.Product
ORDER BY Productid
,Loaddate
OPEN _CURSOR
FETCH NEXT
FROM _CURSOR
INTO @ProductId
,@ProductTitle
,@ProductCategory
,@LoadDate
SET @PrevProductId = @ProductId
SET @PrevProductTitle = @ProductTitle
SET @PrevProductCategory = @ProductCategory
SET @BeginDate = @LoadDate
SET @PrevLoadDate = @LoadDate
WHILE @@FETCH_STATUS = 0
BEGIN
IF @ProductId <> @PrevProductId
OR @ProductTitle <> @PrevProductTitle
OR @ProductCategory <> @PrevProductCategory
BEGIN
IF @ProductId <> @PrevProductId
SET @EndDate = '9999-12-31'
ELSE
SET @EndDate = @PrevLoadDate
INSERT INTO @ResultTable (
ProductId
,ProductTitle
,ProductCategory
,BeginDate
,EndDate
)
VALUES (
@PrevProductId
,@PrevProductTitle
,@PrevProductCategory
,@BeginDate
,@EndDate
)
SET @BeginDate = @LoadDate
END
SET @PrevProductId = @ProductId
SET @PrevProductTitle = @ProductTitle
SET @PrevProductCategory = @ProductCategory
SET @PrevLoadDate = @LoadDate
FETCH NEXT
FROM _CURSOR
INTO @ProductId
,@ProductTitle
,@ProductCategory
,@LoadDate
END --End While
SET @EndDate = '9999-12-31'
INSERT INTO @ResultTable (
ProductId
,ProductTitle
,ProductCategory
,BeginDate
,EndDate
)
VALUES (
@PrevProductId
,@PrevProductTitle
,@PrevProductCategory
,@BeginDate
,@EndDate
)
CLOSE _CURSOR
DEALLOCATE _CURSOR
SELECT *
FROM @ResultTable