Sql 根据给定的输入获取上一个和下一个数据

Sql 根据给定的输入获取上一个和下一个数据,sql,sql-server,sql-server-2008-r2,Sql,Sql Server,Sql Server 2008 R2,我有下表和样本数据: 表:DummyData 样本数据: 查询:我需要查找空ID前后的n个数据 例如1:我需要从空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

我有下表和样本数据:

表:DummyData

样本数据:

查询:我需要查找空ID前后的n个数据

例如1:我需要从空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
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
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
例如2:我需要从空ID中查找前后两个数据,然后输出为:

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
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
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
例如3:我需要从空ID中查找3个前后数据,然后输出为:

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
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
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

我已经向表中添加了一个递增的主键列,我们将有一个临时列表,列出我们需要的上一行和下一行的范围

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表中没有任何内容,结果如下

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)

这是你在另一篇被删除的帖子中提出的问题的答案。。 这与这个问题以及你关于这个主题的其他问题类似

如果我明白了,你是想把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只是一个简单的计数表,用于获得n行编号,您可以在syspobjects上使用ROW_NUMBERS,这是我的函数

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

您可以选择更改数据库设计吗?如果您可以在DRange中将值存储在两列中,一列是日期数据类型的开始日期,另一列是结束日期,那么处理这种类型的查询会容易得多。@EdHarper,是的!我可以将其更改为两个不同的列。@MAK是以任何方式排序的表,或者是否有可以按其排序的列。@Gnyasha,按ID和日期排序。您好@MAK,我有一个解决您删除的问题的方法。。它和这个很相似。。要我在这里发布吗?SQL不支持滞后/超前2008r2@EdHarper我已经消除了超前和滞后,并致力于增加主键columnOne问题!2008 r2没有超前和滞后。对不起!我不明白。你能在你的查询中做些改变吗?好的,我已经改变了答案,现在应该在sql 2008中也能用了