Sql server SQL-筛选日期X与上一个日期相隔的天数

Sql server SQL-筛选日期X与上一个日期相隔的天数,sql-server,tsql,sql-server-2012,Sql Server,Tsql,Sql Server 2012,我有一张包含订单的桌子。我想为特定客户选择相隔一定天数的订单。例如,在下表中,我想选择CustomerID=10的所有订单,这些订单与前一个实例至少相隔30天。起始点为第一次发生(本数据中为2014年5月7日) 我想选择OrderID(1,6,8),因为它们彼此相隔30天,并且都来自CustomerID=10。订单ID 2和7将不包括在内,因为它们在该客户上一次订单的30天内 让我困惑的是如何将“检查点”设置为最后一个有效日期。下面是一个小的“伪”SQL 更新 一种稍微可靠一点的方法是使用一张临

我有一张包含订单的桌子。我想为特定客户选择相隔一定天数的订单。例如,在下表中,我想选择CustomerID=10的所有订单,这些订单与前一个实例至少相隔30天。起始点为第一次发生(本数据中为2014年5月7日)

我想选择OrderID(1,6,8),因为它们彼此相隔30天,并且都来自
CustomerID=10
。订单ID 2和7将不包括在内,因为它们在该客户上一次订单的30天内

让我困惑的是如何将“检查点”设置为最后一个有效日期。下面是一个小的“伪”SQL


更新

一种稍微可靠一点的方法是使用一张临时桌子。但是原始表
tbl
可以保持不变。请看这里:

CREATE TABLE #tmp (id int);   -- set-up temp table
INSERT INTO #tmp VALUES (1);  -- plant "seed": first oid
WHILE (@@ROWCOUNT>0)
  INSERT INTO #tmp (id)
  SELECT TOP 1 OrderId FROM tbl 
  WHERE OrderId>0 AND CustomerId=10 
        AND OrderDate>(SELECT max(OrderDate)+30 FROM tbl INNER JOIN #tmp ON id=OrderId)
  ORDER BY OrderDate;

-- now list all found entries of tbl:
SELECT * FROM tbl WHERE EXISTS (SELECT 1 FROM #tmp WHERE id=OrderId)
您可以将SQL Server 2012中提供的
LAG()
函数与公共表表达式一起使用。您计算客户当前订单与客户上一订单之间的天数,然后使用过滤器
=30

with cte as
(select OrderId
       ,CustomerId
       ,datediff(d
                ,lag(orderdate) over (partition by CustomerId order by OrderDate)
                ,OrderDate) DaysSinceLastOrder
 from Orders)
select OrderId, CustomerId, DaysSinceLastOrder
from cte
where DaysSinceLastOrder >= 30 or DaysSinceLastOrder is null
结果:

OrderId    CustomerId    DaysSinceLastOrder
1          10            NULL
6          10            70
3          11            NULL
4          11            31
5          11            32

(请注意,1970-01-01是任意选择的,您可以选择任何日期)

我来到这里,看到
@SveinFidjestøl
已经发布了答案,但经过长期尝试后,我无法控制自己: 借助于
LAG
LEAD
我们可以在同一列之间进行比较 根据您的Q,您正在查找
1,6,8
。也许这会有帮助

SQL SERVER 2012及其后版本

declare @temp table
(orderid int,
customerid int,
orderDate date
);

insert into @temp values  (1,           10,        '07/05/2014')
insert into @temp values  (2,           10,        '07/15/2014')
insert into @temp values  (3,           11,        '07/20/2014')
insert into @temp values  (4,           11,        '08/20/2014')
insert into @temp values  (5,           11,        '09/21/2014')
insert into @temp values  (6,           10,        '09/23/2014')
insert into @temp values  (7,           10,        '10/15/2014')
insert into @temp values  (8,           10,        '10/30/2014');

with cte as
(SELECT orderid,customerid,orderDate,
LAG(orderDate) OVER (ORDER BY orderid ) PreviousValue,
LEAD(orderDate) OVER (ORDER BY orderid) NextValue,
rownum = ROW_NUMBER() OVER (ORDER BY orderid) 
FROM @temp
WHERE customerid = 10) 

select orderid,customerid,orderDate from cte
where DATEDIFF ( day , PreviousValue  ,  orderDate) > 30 
or PreviousValue is null or NextValue is null
WITH CTE AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY p.orderid),
p.orderid,
p.customerid,
p.orderDate
FROM @temp p
where p.customerid = 10)

SELECT CTE.orderid,CTE.customerid,CTE.orderDate,
prev.orderDate PreviousValue,
nex.orderDate NextValue
FROM CTE
LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
where CTE.customerid = 10
 and
DATEDIFF ( day , prev.orderDate  ,  CTE.orderDate) > 30 
or prev.orderDate is null or nex.orderDate is null
GO
SQL SERVER 2005及以后版本

declare @temp table
(orderid int,
customerid int,
orderDate date
);

insert into @temp values  (1,           10,        '07/05/2014')
insert into @temp values  (2,           10,        '07/15/2014')
insert into @temp values  (3,           11,        '07/20/2014')
insert into @temp values  (4,           11,        '08/20/2014')
insert into @temp values  (5,           11,        '09/21/2014')
insert into @temp values  (6,           10,        '09/23/2014')
insert into @temp values  (7,           10,        '10/15/2014')
insert into @temp values  (8,           10,        '10/30/2014');

with cte as
(SELECT orderid,customerid,orderDate,
LAG(orderDate) OVER (ORDER BY orderid ) PreviousValue,
LEAD(orderDate) OVER (ORDER BY orderid) NextValue,
rownum = ROW_NUMBER() OVER (ORDER BY orderid) 
FROM @temp
WHERE customerid = 10) 

select orderid,customerid,orderDate from cte
where DATEDIFF ( day , PreviousValue  ,  orderDate) > 30 
or PreviousValue is null or NextValue is null
WITH CTE AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY p.orderid),
p.orderid,
p.customerid,
p.orderDate
FROM @temp p
where p.customerid = 10)

SELECT CTE.orderid,CTE.customerid,CTE.orderDate,
prev.orderDate PreviousValue,
nex.orderDate NextValue
FROM CTE
LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
where CTE.customerid = 10
 and
DATEDIFF ( day , prev.orderDate  ,  CTE.orderDate) > 30 
or prev.orderDate is null or nex.orderDate is null
GO

@tinka展示了如何使用CTE来实现这一点,而新的窗口函数(2012年及以后)可能是最好的答案。假设您没有非常大的数据集,还可以选择使用递归CTE

例如:

declare @customerid int = 10;
declare @temp table
(orderid int,
customerid int,
orderDate date
);

insert into @temp values  (1,           10,        '07/05/2014')
insert into @temp values  (2,           10,        '07/15/2014')
insert into @temp values  (3,           11,        '07/20/2014')
insert into @temp values  (4,           11,        '08/20/2014')
insert into @temp values  (5,           11,        '09/21/2014')
insert into @temp values  (6,           10,        '09/23/2014')
insert into @temp values  (7,           10,        '10/15/2014')
insert into @temp values  (8,           10,        '10/30/2014');

with datefilter AS
(
    SELECT row_number() OVER(PARTITION BY CustomerId ORDER BY OrderDate) as RowId, 
        OrderId, 
        CustomerId,
        OrderDate, 
        DATEADD(day, 30, OrderDate) as FilterDate
    from @temp
    WHERE CustomerId = @customerid
)
    , firstdate as 
(
    SELECT RowId, OrderId, CustomerId, OrderDate, FilterDate
    FROM datefilter
    WHERE rowId = 1
    union all
    SELECT datefilter.RowId, datefilter.OrderId, datefilter.CustomerId, 
        datefilter.OrderDate, datefilter.FilterDate
    FROM datefilter
    join firstdate
        on datefilter.CustomerId = firstdate.CustomerId 
        and datefilter.OrderDate > firstdate.FilterDate
    WHERE NOT EXISTS 
    (
        SELECT 1 FROM datefilter betweens 
        WHERE betweens.CustomerId = firstdate.CustomerId 
        AND betweens.orderdate > firstdate.FilterDate 
        AND datefilter.orderdate > betweens.orderdate
    )
)

SELECT * FROM firstdate 

您使用的是什么版本的SQL Server?您如何知道2014-07-05是客户10的“最后有效订单日期”?具体来说,是什么使它成为更大数据集中的最后一个有效顺序?我使用的是SQL Server 2012。假设这是完整的数据集,2014-07-05是第一个条目。因此,您希望选择
CustomerID=10
的所有内容,以及日期介于第一个日期和之后30天之间的所有内容?@Chase-第一次出现在第一个日期后30天,然后第一次出现在该日期后30天,等等。。。