我有一张包含订单的表格。我想为特定客户选择相隔一定天数的订单。例如,在下表中,我想选择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
答案 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
已经发布了答案,但经过长时间尝试,我无法控制自己:
在LAG
和LEAD
的帮助下,我们可以在同一列之间进行比较
根据你的问题,你正在寻找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