Sql 您必须记住将其添加到所有表中。这不起作用:对于原始数据集,它将只返回产品A的一行:[对不起,我想我误解了您想要的“间隙和孤岛”.现在我可以给这类问题起个名字了。谢谢!@Danilopiazzarunga-是的,我真的应该加上这个标签;如果你搜索下大约有18
Sql 您必须记住将其添加到所有表中。这不起作用:对于原始数据集,它将只返回产品A的一行:[对不起,我想我误解了您想要的“间隙和孤岛”.现在我可以给这类问题起个名字了。谢谢!@Danilopiazzarunga-是的,我真的应该加上这个标签;如果你搜索下大约有18,sql,oracle,oracle11g,gaps-and-islands,Sql,Oracle,Oracle11g,Gaps And Islands,您必须记住将其添加到所有表中。这不起作用:对于原始数据集,它将只返回产品A的一行:[对不起,我想我误解了您想要的“间隙和孤岛”.现在我可以给这类问题起个名字了。谢谢!@Danilopiazzarunga-是的,我真的应该加上这个标签;如果你搜索下大约有180个问题,那么你可能会得到一些其他的想法和方法。 | PRODUCT | START_DATE | END_DATE | |---------|----
您必须记住将其添加到所有表中。这不起作用:对于原始数据集,它将只返回产品A的一行:[对不起,我想我误解了您想要的“间隙和孤岛”.现在我可以给这类问题起个名字了。谢谢!@Danilopiazzarunga-是的,我真的应该加上这个标签;如果你搜索下大约有180个问题,那么你可能会得到一些其他的想法和方法。
| PRODUCT | START_DATE | END_DATE |
|---------|----------------------------------|----------------------------------|
| A | July, 01 2013 00:00:00+0000 | July, 31 2013 00:00:00+0000 |
| A | August, 01 2013 00:00:00+0000 | August, 31 2013 00:00:00+0000 |
| A | September, 01 2013 00:00:00+0000 | September, 30 2013 00:00:00+0000 |
| B | October, 01 2013 00:00:00+0000 | October, 31 2013 00:00:00+0000 |
| B | November, 01 2013 00:00:00+0000 | November, 30 2013 00:00:00+0000 |
| A | December, 01 2013 00:00:00+0000 | December, 31 2013 00:00:00+0000 |
| A | January, 01 2014 00:00:00+0000 | January, 31 2014 00:00:00+0000 |
| A | February, 01 2014 00:00:00+0000 | February, 28 2014 00:00:00+0000 |
| A | March, 01 2014 00:00:00+0000 | March, 31 2014 00:00:00+0000 |
| PRODUCT | START_DATE | END_DATE |
|---------|---------------------------------|----------------------------------|
| A | July, 01 2013 00:00:00+0000 | September, 30 2013 00:00:00+0000 |
| B | October, 01 2013 00:00:00+0000 | November, 30 2013 00:00:00+0000 |
| A | December, 01 2013 00:00:00+0000 | March, 31 2014 00:00:00+0000 |
with dat as (
select 'A' as product, sysdate-3 as start_dte, sysdate-2 as end_dte from dual
union all
select 'A' as product, sysdate-2 as start_dte, sysdate-1 as end_dte from dual
union all
select 'B' as product, sysdate-5 as start_dte, sysdate-4 as end_dte from dual
)
SELECT product,
MIN(start_dte) KEEP (DENSE_RANK FIRST ORDER BY start_dte) "Start",
MAX(end_dte) KEEP (DENSE_RANK LAST ORDER BY end_dte) "End"
FROM dat
GROUP BY product
ORDER BY product;
PRODUCT Start End
A 2/24/2014 10:25:53 AM 2/26/2014 10:25:53 AM
B 2/22/2014 10:25:53 AM 2/23/2014 10:25:53 AM
-- Sample Data
CREATE TABLE AdjacentValidity
(
RowID INT IDENTITY(1,1) NOT NULL,
Product VARCHAR(1) NOT NULL,
Start_Date DATETIME NOT NULL,
End_Date DATETIME NOT NULL
)
INSERT INTO AdjacentValidity (Product, Start_Date, End_Date)
SELECT 'A', '7/1/2013', '7/31/2013' UNION
SELECT 'A', '8/1/2013', '8/31/2013' UNION
SELECT 'A', '9/1/2013', '9/30/2013' UNION
SELECT 'B', '10/1/2013', '10/31/2013' UNION
SELECT 'B', '11/1/2013', '11/30/2013' UNION
SELECT 'A', '12/1/2013', '12/31/2013' UNION
SELECT 'A', '1/1/2014', '1/31/2014' UNION
SELECT 'A', '2/1/2014', '2/28/2014' UNION
SELECT 'A', '3/1/2014', '3/31/2014'
-- Modify the sample data to include necessary tags
CREATE TABLE #RawData
(
RawData_ID INT IDENTITY(1,1) NOT NULL,
Product VARCHAR(1) NOT NULL,
Start_Date DATETIME NOT NULL,
End_Date DATETIME NOT NULL,
isFirstOccurrence BIT NULL,
isLastOccurrence BIT NULL,
isFirstInstance BIT NULL,
isLastInstance BIT NULL
)
-- Load and flag first occurrences of a natural key
INSERT INTO #RawData
(
Product,
Start_Date,
End_Date,
isFirstInstance
)
SELECT
Product,
Start_Date,
End_Date,
CASE WHEN ROW_NUMBER() OVER
(
--PARTITION BY <NaturalKey>
ORDER BY Start_date
) = 1 THEN 1 ELSE 0 END AS isFirstOccurrence
FROM AdjacentValidity
-- update to flag the last sequential instance of a particalar data set, and the last occurrence of a natural key
UPDATE a
SET
a.isLastInstance =
CASE
WHEN
a.Product <> b.Product OR
DATEADD(m, 1, a.Start_Date) <> b.Start_Date OR
b.RawData_ID IS NULL
THEN 1
ELSE 0
END,
a.isLastOccurrence =
CASE
WHEN
b.RawData_ID IS NULL
THEN 1
ELSE 0
END
FROM
#RawData a
LEFT JOIN
#RawData b ON
b.RawData_ID = a.RawData_ID + 1 --AND
--b.<NaturalKey> = a.<NaturalKey>
-- flag first sequential instance of a particular data set
UPDATE b
SET
b.isFirstInstance =
CASE
WHEN
a.isLastInstance = 1
THEN 1
ELSE 0
END
FROM
#RawData a
LEFT JOIN
#RawData b ON
b.RawData_ID = a.RawData_ID + 1 --AND
--b.<NaturalKey> = a.<NaturalKey>
-- reduce the records to only those that are the first or last occurrence of a particular data set
CREATE TABLE #UniqueData
(
[UniqueData_ID] [int] IDENTITY(1,1) NOT NULL,
Start_Date DATETIME NOT NULL,
End_Date DATETIME NOT NULL,
Product VARCHAR(1) NULL,
isFirstOccurrence BIT NULL,
isLastOccurrence BIT NULL,
isFirstInstance BIT NULL,
isLastInstance BIT NULL
)
INSERT INTO #UniqueData
(
Start_Date,
End_Date,
Product,
isFirstOccurrence,
isLastOccurrence,
isFirstInstance,
isLastInstance
)
SELECT
Start_Date,
End_Date,
Product,
isFirstOccurrence,
isLastOccurrence,
isFirstInstance,
isLastInstance
FROM
#RawData
WHERE
isFirstOccurrence = 1 OR
isFirstInstance = 1 OR
isLastInstance = 1
ORDER BY RawData_ID, Start_Date
-- combine the first and last occurrences in any given sequence into a single row
SELECT
a.Start_Date,
ISNULL(b.Start_Date, a.End_Date) End_Date,
a.Product
FROM
#UniqueData a
LEFT JOIN
#UniqueData b ON
b.UniqueData_ID = a.UniqueData_ID + 1 AND
--b.<NaturalKey> = a.<NaturalKey> AND
a.isLastInstance <> 1
WHERE a.isFirstInstance = 1 or a.isFirstOccurrence = 1
ORDER BY a.UniqueData_ID
-- clean up
/*
DROP TABLE AdjacentValidity
DROP TABLE #RawData
DROP TABLE #UniqueData
*/
select distinct product,
case when start_date is null then lag(start_date)
over (partition by product order by rn) else start_date end as start_date,
case when end_date is null then lead(end_date)
over (partition by product order by rn) else end_date end as end_date
from (
select product, start_date, end_date, rn
from (
select t.product,
case when lag(end_date)
over (partition by product order by start_date) is null
or lag(end_date)
over (partition by product order by start_date) != start_date - 1
then start_date end as start_date,
case when lead(start_date)
over (partition by product order by start_date) is null
or lead(start_date)
over (partition by product order by start_date) != end_date + 1
then end_date end as end_date,
row_number() over (partition by product order by start_date) as rn
from t
)
where start_date is not null or end_date is not null
)
order by start_date, product;
PRODUCT START_DATE END_DATE
------- ---------- ---------
A 01-JUL-13 30-SEP-13
B 01-OCT-13 30-NOV-13
A 01-DEC-13 31-MAR-14
select t.product,
case when lag(end_date)
over (partition by product order by start_date) is null
or lag(end_date)
over (partition by product order by start_date) != start_date - 1
then start_date end as start_date,
case when lead(start_date)
over (partition by product order by start_date) is null
or lead(start_date)
over (partition by product order by start_date) != end_date + 1
then end_date end as end_date
from t;
PRODUCT START_DATE END_DATE
------- ---------- ---------
A 01-JUL-13
A
A 30-SEP-13
A 01-DEC-13
A
A
A 31-MAR-14
B 01-OCT-13
B 30-NOV-13
PRODUCT START_DATE END_DATE
------- ---------- ---------
A 01-JUL-13
A 30-SEP-13
A 01-DEC-13
A 31-MAR-14
B 01-OCT-13
B 30-NOV-13
with data as (
select 'A' product, to_date('7/1/2013', 'MM/DD/YYYY') start_date, to_date('7/31/2013', 'MM/DD/YYYY') end_date from dual union all
select 'A' product, to_date('8/1/2013', 'MM/DD/YYYY') start_date, to_date('8/31/2013', 'MM/DD/YYYY') end_date from dual union all
select 'A' product, to_date('9/1/2013', 'MM/DD/YYYY') start_date, to_date('9/30/2013', 'MM/DD/YYYY') end_date from dual union all
select 'B' product, to_date('10/1/2013', 'MM/DD/YYYY') start_date, to_date('10/31/2013', 'MM/DD/YYYY') end_date from dual union all
select 'B' product, to_date('11/1/2013', 'MM/DD/YYYY') start_date, to_date('11/30/2013', 'MM/DD/YYYY') end_date from dual union all
select 'A' product, to_date('12/1/2013', 'MM/DD/YYYY') start_date, to_date('12/31/2013', 'MM/DD/YYYY') end_date from dual union all
select 'A' product, to_date('1/1/2014', 'MM/DD/YYYY') start_date, to_date('1/31/2014', 'MM/DD/YYYY') end_date from dual union all
select 'A' product, to_date('2/1/2014', 'MM/DD/YYYY') start_date, to_date('2/28/2014', 'MM/DD/YYYY') end_date from dual union all
select 'A' product, to_date('3/1/2014', 'MM/DD/YYYY') start_date, to_date('3/31/2014', 'MM/DD/YYYY') end_date from dual
),
start_points as
(
select product, start_date, end_date, prior_end+1, case when prior_end + 1 = start_date then null else 'Y' end start_point
from (
select product, start_date, end_date, lag(end_date,1) over (partition by product order by end_date) prior_end
from data
)
)
select product, min(start_date) start_date, max(end_date) end_date
from (
select product, start_date, end_date, level, connect_by_root(start_date) root_start
from start_points
start with start_point = 'Y'
connect by prior end_date = start_date - 1
and prior product = product
)
group by product, root_start;
PRODUCT START_DATE END_DATE
------- ---------- ---------
A 01-JUL-13 30-SEP-13
A 01-DEC-13 31-MAR-14
B 01-OCT-13 30-NOV-13