SQL - 过滤日期X与前一天

时间:2015-07-28 14:40:05

标签: sql-server tsql sql-server-2012

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

OrderID | CustomerID |  OrderDate
==========================================
  1           10        07/05/2014
  2           10        07/15/2014
  3           11        07/20/2014
  4           11        08/20/2014
  5           11        09/21/2014
  6           10        09/23/2014
  7           10        10/15/2014
  8           10        10/30/2014

我想选择OrderIDs(1,6,8),因为它们相隔30天,而且都来自CustomerID = 10。订单ID 2和7将不包括在内,因为它们是在该客户之前订单的30天内。

令我困惑的是如何设置"检查点"到最后一个有效日期。这是一个小"伪" SQL。

SELECT OrderID
FROM Orders
WHERE CusomerID = 10 
  AND OrderDate > LastValidOrderDate + 30

4 个答案:

答案 0 :(得分:1)

<强>更新

稍微更可靠的方法是使用临时表。但是原始表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)

答案 1 :(得分:1)

You can use the LAG() function, available in SQL Server 2012, together with a Common Table Expression. You calculate the days between the customer's current order and the customer's previous order and then query the Common Table Expression using the filter >= 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

Results:

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

(Note that 1970-01-01 is chosen arbitrarily, you may choose any date)

答案 2 :(得分:1)

我来到这里,我看到@SveinFidjestøl已经发布了答案,但经过长时间尝试,我无法控制自己: 在LAGLEAD的帮助下,我们可以在同一列之间进行比较 根据你的问题,你正在寻找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

SQL SERVER 2005及之后

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

答案 3 :(得分:0)

@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