根据给定的输入获取上一个和下一个数据
Get previous and next data based on given input
我有以下 table 示例数据:
Table: DummyData
CREATE TABLE DummyData
(
ID int,
Dates_Range VARCHAR(50)
);
示例数据:
INSERT INTO DummyData VALUES(1,'2019-01-01');
INSERT INTO DummyData VALUES(1,'2019-01-02');
INSERT INTO DummyData VALUES(1,'2019-01-03');
INSERT INTO DummyData VALUES(NULL,'2019-01-04 - 2019-02-01');
INSERT INTO DummyData VALUES(1,'2019-02-02');
INSERT INTO DummyData VALUES(2,'2019-01-06');
INSERT INTO DummyData VALUES(NULL,'2019-01-07');
INSERT INTO DummyData VALUES(2,'2019-01-08');
INSERT INTO DummyData VALUES(2,'2019-01-09');
INSERT INTO DummyData VALUES(3,'2019-01-02');
INSERT INTO DummyData VALUES(3,'2019-01-03');
INSERT INTO DummyData VALUES(NULL,'2019-01-04 - 2019-01-09');
INSERT INTO DummyData VALUES(3,'2019-01-10');
查询: 我需要找到NULL
个ID前后的n个数字数据。
例如 1:我需要从 NULL
个 ID 前后找到 1 个数据,然后输出将是:
ID DRange
-------------------------------
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
例如 2:我需要从 NULL
个 ID 前后找到 2 个数据,然后输出将是:
ID DRange
-------------------------------
1 2019-01-02
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
2 2019-01-09
3 2019-01-02
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
例如3:我需要从NULL
个ID中找到前后3个数据然后输出将是:
ID DRange
-------------------------------
1 2019-01-01
1 2019-01-02
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
2 2019-01-09
3 2019-01-02
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
我在 table 中添加了一个递增的主键列,我们将有一个我们需要的前后行范围的临时列表。
DECLARE @MyList TABLE (Value INT)
INSERT INTO @MyList VALUES (1)
INSERT INTO @MyList VALUES (2)
select ID,Dates_Range from DummyData where PiD in (select PiD+Value as myCollection from (
select Pid from DummyData where id is null
) a
cross join
@MyList
union all
select PiD-Value from (
select Pid from DummyData where id is null
) a
cross join
@MyList ) or id is null
结果是
ID Dates_Range
1 2019-01-02
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
2 2019-01-09
3 2019-01-02
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
如果我们只在我们的列表中插入一个值,我们将有这个查询部分
DECLARE @MyList TABLE (Value INT)
INSERT INTO @MyList VALUES (1)
select ID,Dates_Range from DummyData where PiD in (select PiD+Value as myCollection from (
--
结果将是
ID Dates_Range
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
现在我们的 MyList 中没有任何内容table结果如下
ID Dates_Range
NULL 2019-01-04 - 2019-02-01
NULL 2019-01-07
NULL 2019-01-04 - 2019-01-09
如果您想要 3 列,只需添加
INSERT INTO @MyList VALUES (1)
INSERT INTO @MyList VALUES (2)
INSERT INTO @MyList VALUES (3)
这是您在另一个 post 中提出的问题的答案,您已删除..
它类似于这个问题和你关于这个主题的其他问题..
如果我理解你正在尝试让 n 行邻接一个间隙..
--DROP TABLE #TempTest
CREATE TABLE #TempTest
(
Series INT,
ID int,
DATES DATE
);
--Records:
INSERT INTO #TempTest VALUES(1,101,'2019-11-01');
INSERT INTO #TempTest VALUES(1,101,'2019-11-02');
INSERT INTO #TempTest VALUES(1,101,'2019-11-04');
INSERT INTO #TempTest VALUES(1,101,'2019-11-06');
INSERT INTO #TempTest VALUES(2,201,'2019-11-02');
INSERT INTO #TempTest VALUES(2,201,'2019-11-03');
INSERT INTO #TempTest VALUES(2,201,'2019-11-04');
INSERT INTO #TempTest VALUES(2,201,'2019-12-09');
INSERT INTO #TempTest VALUES(2,201,'2019-12-10');
INSERT INTO #TempTest VALUES(2,201,'2019-12-20');
INSERT INTO #TempTest VALUES(3,301,'2019-12-01');
INSERT INTO #TempTest VALUES(3,301,'2019-12-05');
INSERT INTO #TempTest VALUES(3,301,'2019-12-15');
INSERT INTO #TempTest VALUES(3,301,'2019-12-16');
INSERT INTO #TempTest VALUES(3,301,'2019-12-17');
INSERT INTO #TempTest VALUES(3,301,'2019-12-18');
INSERT INTO #TempTest VALUES(4,401,'2019-12-01');
INSERT INTO #TempTest VALUES(4,401,'2019-12-02');
INSERT INTO #TempTest VALUES(4,401,'2019-12-04');
INSERT INTO #TempTest VALUES(4,401,'2019-12-06');
INSERT INTO #TempTest VALUES(4,401,'2019-12-08');
INSERT INTO #TempTest VALUES(4,401,'2019-12-10')
DECLARE @BeforeAfter INT;
DECLARE @MinDate DATE;
DECLARE @MaxDate DATE;
SET @BeforeAfter = 3; --2,3....n numbers.
SELECT @MinDate = MIN(DATES), @MaxDate = MAX(DATES) FROM #TempTest;
; with
d as (
-- filtered data (add a key)
SELECT Series, ID, DATES, ROW_NUMBER() OVER (ORDER BY Series, ID, DATES) rk
FROM #TempTest
WHERE DATES between @MinDate AND @MaxDate
),
g as (
-- series groups with min/max dates
select series, id, min(dates) dmin, max(dates) dmax
from d
group by series, id
),
dt as (
-- tally table with all needed dates to examine
select *, dateadd(d, n-1, @mindate) dt
from FN_NUMBERS(datediff(d, @MinDate, @MaxDate)+1) n
),
dx0 as (
-- data expanded over the serie period (add a new key to calc prec/succ)
select g.series, g.id id, dt.dt, d.id idx, d.rk
, ROW_NUMBER() over(partition BY g.Series, g.ID order by dt) gk
from g
join dt on dt.dt between g.dmin and g.dmax
left join d on d.series = g.series and d.id = g.id and d.dates = dt.dt
),
dx as (
-- calc of prec/succ keys
select d.*, d1.rk rk_prec, d2.rk rk_succ
from dx0 d
left join dx0 d1 on d1.Series = d.Series and d1.ID = d.id and d1.gk = d.gk -1
left join dx0 d2 on d2.Series = d.Series and d2.ID = d.id and d2.gk = d.gk +1
),
gaps as(
-- single day gaps (add gap type = 0)
select *, DATEADD(d, -@BeforeAfter, dt) dmin, DATEADD(d, @BeforeAfter, dt) dmax, 0 gt
from dx
where rk_prec = rk_succ-1
union
-- period gaps (add gap type = null)
select *
from (
select d1.*, DATEADD(d, -@BeforeAfter, d1.dt) dmin, DATEADD(d, @BeforeAfter, d2.dt) dmax, null gt
from dx d1, dx d2
where d1.rk_prec = d2.rk_succ -1
and d1.rk_succ is null and d2.rk_prec is null
and d1.idx is null and d2.idx is null
) dd
),
r as (
-- before/after rows for each gap (possible duplicates) (add row type = 0)
select g.series, g.id, g.dt, g.dmin, g.dmax, d.dates, gt, 0 rt
from gaps g
join d on d.series = g.series and d.id = g.id and d.dates between dmin and dmax
union
-- add gaps to output (add row type = null)
select g.series, g.id, g.dt, g.dmin, g.dmax, dt, gt, null rt
from gaps g
),
r2 as (
select
-- prepare output with calc id and calc date/period
r.series,
case when rt is null then null else id end id,
case when gt is null and rt is null then cast(dateadd(d, @BeforeAfter, dmin) as varchar(10)) + ' to ' + cast(dateadd(d, -@BeforeAfter, dmax) as varchar(10)) else cast(dates as varchar(10)) end dates,
ROW_NUMBER() over (partition by r.series, r.id, r.dates order by dates) flt,
gt, rt
from r
),
r3 as (
-- final output with duplicates filtered out
select series, id, dates
from r2
where flt=1
)
select *
from r3
order by 1,3
函数FN_NUMBERS
只是一个简单的计数table得到n行编号,你可以在syspobjects
上使用ROW_NUMBER
,这是我的函数..
CRATE FUNCTION [dbo].[FN_NUMBERS](
@MAX INT
)
RETURNS @N TABLE (N INT NOT NULL PRIMARY KEY)
BEGIN
WITH
Pass0 as (select /*TOP (@MAX) */'1' as C union all select '1'), --2 rows
Pass1 as (select /*TOP (@MAX) */'1' as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select /*TOP (@MAX) */'1' as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select /*TOP (@MAX) */'1' as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select TOP (@MAX) '1' as C from Pass3 as A, Pass3 as B) --65536 rows
,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass2 as B, Pass1 as C) --4194304 rows
--,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass3 as B) --16777216 rows
--,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass4 as B) --4294836225 rows
INSERT INTO @N
SELECT TOP (@MAX) ROW_NUMBER() OVER(ORDER BY C) AS N
FROM Tally
RETURN
END
我有以下 table 示例数据:
Table: DummyData
CREATE TABLE DummyData
(
ID int,
Dates_Range VARCHAR(50)
);
示例数据:
INSERT INTO DummyData VALUES(1,'2019-01-01');
INSERT INTO DummyData VALUES(1,'2019-01-02');
INSERT INTO DummyData VALUES(1,'2019-01-03');
INSERT INTO DummyData VALUES(NULL,'2019-01-04 - 2019-02-01');
INSERT INTO DummyData VALUES(1,'2019-02-02');
INSERT INTO DummyData VALUES(2,'2019-01-06');
INSERT INTO DummyData VALUES(NULL,'2019-01-07');
INSERT INTO DummyData VALUES(2,'2019-01-08');
INSERT INTO DummyData VALUES(2,'2019-01-09');
INSERT INTO DummyData VALUES(3,'2019-01-02');
INSERT INTO DummyData VALUES(3,'2019-01-03');
INSERT INTO DummyData VALUES(NULL,'2019-01-04 - 2019-01-09');
INSERT INTO DummyData VALUES(3,'2019-01-10');
查询: 我需要找到NULL
个ID前后的n个数字数据。
例如 1:我需要从 NULL
个 ID 前后找到 1 个数据,然后输出将是:
ID DRange
-------------------------------
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
例如 2:我需要从 NULL
个 ID 前后找到 2 个数据,然后输出将是:
ID DRange
-------------------------------
1 2019-01-02
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
2 2019-01-09
3 2019-01-02
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
例如3:我需要从NULL
个ID中找到前后3个数据然后输出将是:
ID DRange
-------------------------------
1 2019-01-01
1 2019-01-02
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
2 2019-01-09
3 2019-01-02
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
我在 table 中添加了一个递增的主键列,我们将有一个我们需要的前后行范围的临时列表。
DECLARE @MyList TABLE (Value INT)
INSERT INTO @MyList VALUES (1)
INSERT INTO @MyList VALUES (2)
select ID,Dates_Range from DummyData where PiD in (select PiD+Value as myCollection from (
select Pid from DummyData where id is null
) a
cross join
@MyList
union all
select PiD-Value from (
select Pid from DummyData where id is null
) a
cross join
@MyList ) or id is null
结果是
ID Dates_Range
1 2019-01-02
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
2 2019-01-09
3 2019-01-02
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
如果我们只在我们的列表中插入一个值,我们将有这个查询部分
DECLARE @MyList TABLE (Value INT)
INSERT INTO @MyList VALUES (1)
select ID,Dates_Range from DummyData where PiD in (select PiD+Value as myCollection from (
--
结果将是
ID Dates_Range
1 2019-01-03
NULL 2019-01-04 - 2019-02-01
1 2019-02-02
2 2019-01-06
NULL 2019-01-07
2 2019-01-08
3 2019-01-03
NULL 2019-01-04 - 2019-01-09
3 2019-01-10
现在我们的 MyList 中没有任何内容table结果如下
ID Dates_Range
NULL 2019-01-04 - 2019-02-01
NULL 2019-01-07
NULL 2019-01-04 - 2019-01-09
如果您想要 3 列,只需添加
INSERT INTO @MyList VALUES (1)
INSERT INTO @MyList VALUES (2)
INSERT INTO @MyList VALUES (3)
这是您在另一个 post 中提出的问题的答案,您已删除.. 它类似于这个问题和你关于这个主题的其他问题..
如果我理解你正在尝试让 n 行邻接一个间隙..
--DROP TABLE #TempTest
CREATE TABLE #TempTest
(
Series INT,
ID int,
DATES DATE
);
--Records:
INSERT INTO #TempTest VALUES(1,101,'2019-11-01');
INSERT INTO #TempTest VALUES(1,101,'2019-11-02');
INSERT INTO #TempTest VALUES(1,101,'2019-11-04');
INSERT INTO #TempTest VALUES(1,101,'2019-11-06');
INSERT INTO #TempTest VALUES(2,201,'2019-11-02');
INSERT INTO #TempTest VALUES(2,201,'2019-11-03');
INSERT INTO #TempTest VALUES(2,201,'2019-11-04');
INSERT INTO #TempTest VALUES(2,201,'2019-12-09');
INSERT INTO #TempTest VALUES(2,201,'2019-12-10');
INSERT INTO #TempTest VALUES(2,201,'2019-12-20');
INSERT INTO #TempTest VALUES(3,301,'2019-12-01');
INSERT INTO #TempTest VALUES(3,301,'2019-12-05');
INSERT INTO #TempTest VALUES(3,301,'2019-12-15');
INSERT INTO #TempTest VALUES(3,301,'2019-12-16');
INSERT INTO #TempTest VALUES(3,301,'2019-12-17');
INSERT INTO #TempTest VALUES(3,301,'2019-12-18');
INSERT INTO #TempTest VALUES(4,401,'2019-12-01');
INSERT INTO #TempTest VALUES(4,401,'2019-12-02');
INSERT INTO #TempTest VALUES(4,401,'2019-12-04');
INSERT INTO #TempTest VALUES(4,401,'2019-12-06');
INSERT INTO #TempTest VALUES(4,401,'2019-12-08');
INSERT INTO #TempTest VALUES(4,401,'2019-12-10')
DECLARE @BeforeAfter INT;
DECLARE @MinDate DATE;
DECLARE @MaxDate DATE;
SET @BeforeAfter = 3; --2,3....n numbers.
SELECT @MinDate = MIN(DATES), @MaxDate = MAX(DATES) FROM #TempTest;
; with
d as (
-- filtered data (add a key)
SELECT Series, ID, DATES, ROW_NUMBER() OVER (ORDER BY Series, ID, DATES) rk
FROM #TempTest
WHERE DATES between @MinDate AND @MaxDate
),
g as (
-- series groups with min/max dates
select series, id, min(dates) dmin, max(dates) dmax
from d
group by series, id
),
dt as (
-- tally table with all needed dates to examine
select *, dateadd(d, n-1, @mindate) dt
from FN_NUMBERS(datediff(d, @MinDate, @MaxDate)+1) n
),
dx0 as (
-- data expanded over the serie period (add a new key to calc prec/succ)
select g.series, g.id id, dt.dt, d.id idx, d.rk
, ROW_NUMBER() over(partition BY g.Series, g.ID order by dt) gk
from g
join dt on dt.dt between g.dmin and g.dmax
left join d on d.series = g.series and d.id = g.id and d.dates = dt.dt
),
dx as (
-- calc of prec/succ keys
select d.*, d1.rk rk_prec, d2.rk rk_succ
from dx0 d
left join dx0 d1 on d1.Series = d.Series and d1.ID = d.id and d1.gk = d.gk -1
left join dx0 d2 on d2.Series = d.Series and d2.ID = d.id and d2.gk = d.gk +1
),
gaps as(
-- single day gaps (add gap type = 0)
select *, DATEADD(d, -@BeforeAfter, dt) dmin, DATEADD(d, @BeforeAfter, dt) dmax, 0 gt
from dx
where rk_prec = rk_succ-1
union
-- period gaps (add gap type = null)
select *
from (
select d1.*, DATEADD(d, -@BeforeAfter, d1.dt) dmin, DATEADD(d, @BeforeAfter, d2.dt) dmax, null gt
from dx d1, dx d2
where d1.rk_prec = d2.rk_succ -1
and d1.rk_succ is null and d2.rk_prec is null
and d1.idx is null and d2.idx is null
) dd
),
r as (
-- before/after rows for each gap (possible duplicates) (add row type = 0)
select g.series, g.id, g.dt, g.dmin, g.dmax, d.dates, gt, 0 rt
from gaps g
join d on d.series = g.series and d.id = g.id and d.dates between dmin and dmax
union
-- add gaps to output (add row type = null)
select g.series, g.id, g.dt, g.dmin, g.dmax, dt, gt, null rt
from gaps g
),
r2 as (
select
-- prepare output with calc id and calc date/period
r.series,
case when rt is null then null else id end id,
case when gt is null and rt is null then cast(dateadd(d, @BeforeAfter, dmin) as varchar(10)) + ' to ' + cast(dateadd(d, -@BeforeAfter, dmax) as varchar(10)) else cast(dates as varchar(10)) end dates,
ROW_NUMBER() over (partition by r.series, r.id, r.dates order by dates) flt,
gt, rt
from r
),
r3 as (
-- final output with duplicates filtered out
select series, id, dates
from r2
where flt=1
)
select *
from r3
order by 1,3
函数FN_NUMBERS
只是一个简单的计数table得到n行编号,你可以在syspobjects
上使用ROW_NUMBER
,这是我的函数..
CRATE FUNCTION [dbo].[FN_NUMBERS](
@MAX INT
)
RETURNS @N TABLE (N INT NOT NULL PRIMARY KEY)
BEGIN
WITH
Pass0 as (select /*TOP (@MAX) */'1' as C union all select '1'), --2 rows
Pass1 as (select /*TOP (@MAX) */'1' as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select /*TOP (@MAX) */'1' as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select /*TOP (@MAX) */'1' as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select TOP (@MAX) '1' as C from Pass3 as A, Pass3 as B) --65536 rows
,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass2 as B, Pass1 as C) --4194304 rows
--,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass3 as B) --16777216 rows
--,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass4 as B) --4294836225 rows
INSERT INTO @N
SELECT TOP (@MAX) ROW_NUMBER() OVER(ORDER BY C) AS N
FROM Tally
RETURN
END