如何选择两个日期与下一行和上一行之间的行

mhs*_*kar 3 sql sql-server sql-server-2016

我有一个具有以下结构的表

ID  PersonID    Date
---------------------------
1   1           2017-04-01
2   1           2017-04-15
3   1           2017-05-13
4   1           2017-06-15
5   1           2017-08-13
6   1           2017-10-02
7   2           2017-05-04
8   2           2017-09-16
9   3           2017-04-23
10  3           2017-07-06
11  4           2017-06-01
Run Code Online (Sandbox Code Playgroud)

如果存在,我希望Select在每个Next和Previous行之间2017-05-01和之间行.2017-08-26PersonID

我想要这个结果:

ID  PersonID    Date
------------------------------
2   1           2017-04-15
3   1           2017-05-13
4   1           2017-06-15
5   1           2017-08-13
6   1           2017-10-02
7   2           2017-05-04
8   2           2017-09-16
9   3           2017-04-23
10  3           2017-07-06
11  4           2017-06-01
Run Code Online (Sandbox Code Playgroud)

Ser*_*hov 5

请尝试以下查询

create table TestData(ID int,PersonID int,[Date] date)

insert TestData(ID,PersonID,[Date])values
(1 ,1,'20170401'),(2 ,1,'20170415'),(3 ,1,'20170513'),
(4 ,1,'20170615'),(5 ,1,'20170813'),(6 ,1,'20171002'),
(7 ,2,'20170504'),(8 ,2,'20170916'),(9 ,3,'20170423'),
(10,3,'20170706'),(11,4,'20170601')
----------------
DECLARE
  @FromDate date='20170501',
  @ToDate date='20170826'

SELECT *
FROM
  (
    SELECT
      *,
      LAG(IIF([Date] BETWEEN @FromDate AND @ToDate,1,0))OVER(PARTITION BY PersonID ORDER BY [Date],ID) LagOK,
      LEAD(IIF([Date] BETWEEN @FromDate AND @ToDate,1,0))OVER(PARTITION BY PersonID ORDER BY [Date],ID) LeadOK
    FROM TestData
  ) q
WHERE ([Date] BETWEEN @FromDate AND @ToDate OR LagOK=1 OR LeadOK=1)
Run Code Online (Sandbox Code Playgroud)

CTE和的变体ROW_NUMBER

;WITH numCTE AS(
  SELECT
    *,
    ROW_NUMBER()OVER(PARTITION BY PersonID ORDER BY [Date],ID) N
  FROM TestData
)
SELECT n.*
FROM
  (
    SELECT PersonID,MIN(N)-1 MinN,MAX(N)+1 MaxN
    FROM numCTE
    WHERE [Date] BETWEEN @FromDate AND @ToDate
    GROUP BY PersonID
  ) q
JOIN numCTE n on n.PersonID=q.PersonID AND n.N BETWEEN q.MinN AND q.MaxN
Run Code Online (Sandbox Code Playgroud)

我也为这种情况添加了新的测试数据和修改过的查询

create table TestData(ID int,PersonID int,[Date] date)

insert TestData(ID,PersonID,[Date])values
(1 ,1,'20170401'),(2 ,1,'20170415'),(3 ,1,'20170513'),
(4 ,1,'20170615'),(5 ,1,'20170813'),(6 ,1,'20171002'),
(7 ,2,'20170504'),(8 ,2,'20170916'),(9 ,3,'20170423'),
(10,3,'20170706'),(11,4,'20170601'),
(14,6,'20170415'),(15,6,'20170913'),(16,6,'20171015') -- new test data

DECLARE
  @FromDate date='20170501',
  @ToDate date='20170826'

SELECT *
FROM
  (
    SELECT
      *,
      LAG(IIF([Date] BETWEEN @FromDate AND @ToDate,1,0))OVER(PARTITION BY PersonID ORDER BY [Date],ID) LagOK,
      LEAD(IIF([Date] BETWEEN @FromDate AND @ToDate,1,0))OVER(PARTITION BY PersonID ORDER BY [Date],ID) LeadOK
    FROM
      (
        SELECT ID,PersonID,[Date]
        FROM TestData

        UNION ALL

        SELECT DISTINCT NULL,PersonID,@FromDate -- add phantom rows for some people
        FROM TestData p
        WHERE NOT EXISTS(SELECT * FROM TestData d WHERE d.[Date] BETWEEN @FromDate AND @ToDate AND d.PersonID=p.PersonID)
      ) q
  ) q
WHERE ([Date] BETWEEN @FromDate AND @ToDate OR LagOK=1 OR LeadOK=1)
  AND ID IS NOT NULL -- exclude phantom rows from result
Run Code Online (Sandbox Code Playgroud)

以及CTE和ROW_NUMBER的新变种

;WITH numCTE AS(
  SELECT
    *,
    ROW_NUMBER()OVER(PARTITION BY PersonID ORDER BY [Date],ID) N
  FROM
    (
      SELECT ID,PersonID,[Date]
      FROM TestData

      UNION ALL

      SELECT DISTINCT NULL,PersonID,@FromDate -- add phantom rows for some people
      FROM TestData p
      WHERE NOT EXISTS(SELECT * FROM TestData d WHERE d.[Date] BETWEEN @FromDate AND @ToDate AND d.PersonID=p.PersonID)
    ) q
)
SELECT n.*
FROM
  (
    SELECT PersonID,MIN(N)-1 MinN,MAX(N)+1 MaxN
    FROM numCTE
    WHERE [Date] BETWEEN @FromDate AND @ToDate
    GROUP BY PersonID
  ) q
JOIN numCTE n on n.PersonID=q.PersonID AND n.N BETWEEN q.MinN AND q.MaxN
WHERE ID IS NOT NULL -- exclude phantom rows from result
Run Code Online (Sandbox Code Playgroud)