SQL 获取超过某个日期的记录,高于某个值,最小数量
Posted
技术标签:
【中文标题】SQL 获取超过某个日期的记录,高于某个值,最小数量【英文标题】:SQL Get records past a certain date, higher than a certain value, with a minimum amount 【发布时间】:2016-10-19 23:32:10 【问题描述】:我现在很难处理 SQL 查询。我有一个客户订单列表,我想根据特定条件删除一组订单:
-
我们需要保留每位客户过去的至少 6 个订单。
我们需要保留过去 90 天内发生的所有客户订单。
我们需要保留每个客户至少 1 个超过 90 天的订单(如果客户在过去 90 天内有 4 个订单,我们需要保留较早的 2 个订单以达到 6 个订单要求。
因此,例如,如果客户在过去 90 天内有 6 个订单,我们将保留他们的 7 个订单(因为我们包括 1 个超过 90 天的订单)。
如果客户在过去 90 天内有 21 个订单,我们将保留他们的 22 个订单。
如果客户在过去 90 天内有 5 个订单,我们将保留他们的 6 个订单。
这是我用来构建订单表的查询:
INSERT INTO @OrdersToDelete
SELECT TempOrders.Site, TempOrders.Number, TempOrders.RowNumber, TempOrders.CustomerNumber
FROM (SELECT
ROW_NUMBER() OVER ( PARTITION BY CustomerNumber ORDER BY OrderDate DESC) AS 'RowNumber',
Number,
OrderDate,
CustomerNumber
FROM Orders
) TempOrders
LEFT OUTER JOIN (SELECT
ROW_NUMBER() OVER ( PARTITION BY CustomerNumber ORDER BY OrderDate DESC) AS 'RowNumber',
Number,
CustomerNumber
FROM SmartOrders
) SmartOrderOrders
ON TempOrders.Site = SmartOrderOrders.Site
AND TempOrders.Number = SmartOrderOrders.Number
WHERE
(DATEDIFF(dd, OrderDate, GETDATE()) > 90
此查询返回待删除的订单列表(超过 90 天)。在 WHERE 子句中,我还可以检查订单号,但我很难弄清楚如何在 90 天后排除客户的首次订单。
任何帮助将不胜感激。
【问题讨论】:
简化您的要求,显示一些示例数据和相同的预期结果。如果你能设置一个 sqlfiddle 就更好了。 【参考方案1】:--Get the rownumbers using a case expression in order by
--so all the orders within the last 90 days come first
WITH ROWNUMS AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY CustomerNumber
ORDER BY
CASE WHEN DATEDIFF(dd, OrderDate, GETDATE()) < 90 THEN 1 ELSE 0 END DESC,
OrderDate DESC) AS 'RowNumber',
Number,
OrderDate,
CustomerNumber
FROM Orders)
--Get the maximum rownumber per customer in the last 90 days
,MAXROWNUM AS (select CustomerNumber, MAX(rn) maxrn from ROWNUMS
where diff<=90
group by id)
--Join the previous cte's and get all the orders for a customer in the last 90 days
-- + one more row which is the latest before 90 days
SELECT r.*
FROM ROWNUMS r
JOIN MAXROWNUM c ON c.CustomerNumber=r.CustomerNumber
WHERE r.rn <= c.maxrn+1
--use r.rn <= case when c.maxrn <=5 then 5 else c.maxrn end + 1 to get atleast 6 orders per customer
【讨论】:
这似乎对我有用,非常感谢,希望我能在你回答之前让问题更详细。【参考方案2】:试一试。
首先创建 3 个公用表表达式 (CTE)。您可以将它们作为嵌套子查询来执行,但我发现 CTE 更易于阅读和管理,而且它们更易于解释。
WITH ninety_day_cte
AS
(SELECT temporders.site, temporders.number, temporders.customernumber, temporders.orderdate
FROM orders
WHERE
temporders.orderdate >= DATEADD(DAY,-ninety,GETDATE())),
ninety_day_count_cte
AS
(SELECT temporders.customernumber, COUNT(*) AS Order_Count
FROM orders
WHERE
temporders.orderdate >= DATEADD(DAY,-ninety,GETDATE())
GROUP BY
temporders.customernumber),
greater_ninety_day_cte
AS
(SELECT temporders.site, temporders.number, temporders.customernumber, temporders.orderdate,
ROW_NUMBER() OVER(PARTITION BY temporders.customernumber ORDER BY temporders.orderdate DESC) AS Row_Number
FROM orders
WHERE
temporders.orderdate < DATEADD(DAY,-ninety,GETDATE()))
第一个 CTE,ninety_day_cte 将获取过去 90 天内的所有订单 - 我们需要所有客户,我们需要所有订单。很简单,我们可以把这个放在一边。
第二个 CTE,ninety_day_count_cte 用于确定过去 90 天内每位客户的订单总数。我们需要知道这个数字来确定我们需要抓取多少超过 90 天的订单。
第三个 CTE,greater_ninety_day_cte 将获取所有超过 90 天的订单。我们添加 ROW_NUMBER() 以按订单日期对每个客户的订单进行排名 - 这将帮助我们获取过去 90 天所需的订单。
现在我们需要添加查询来获取过去 90 天的订单:
SELECT site, number, customernumber, orderdate
FROM greater_ninety_day_cte AS g
LEFT JOIN ninety_day_count AS c
ON g.customernumber = c.customernumber
WHERE
g.Row_Number <= CASE
WHEN CASE WHEN c.Order_Count IS NULL THEN 0 ELSE c.Order_Count END > 6 THEN 1
ELSE (6 - CASE WHEN c.Order_Count IS NULL THEN 0 ELSE c.Order_Count END)
END
这使用了第 2 和第 3 个 CTE。我们使用 LEFT JOIN,因此我们为只有超过 90 天的订单的客户获取数据。 WHERE 子句从第 3 个 CTE 获取 Row_Number,并将其与来自第 2 个 CTE 的 Order_Count 进行比较。 CASE 子句规定,如果 Order_Count(过去 90 天的订单数)大于 6,我们只想拉取 Row_Number >= 1,但如果 Order_Count 小于 6,那么我们想拉取差值(6 - Order_Count)。这应该会得到所有超过 90 天且符合要求的订单。
现在我们只需要获得少于 90 天的订单。这可以通过使用第一个 CTE 的 UNION ALL 语句轻松完成:
UNION ALL
SELECT site, number, customernumber, orderdate
FROM ninety_day_cte
这应该会为您提供所需的所有结果。至少 6 个订单和至少 1 个超过 90 天的订单。
这是完整的查询:
WITH ninety_day_cte
AS
(SELECT temporders.site, temporders.number, temporders.customernumber, temporders.orderdate
FROM orders
WHERE
temporders.orderdate >= DATEADD(DAY,-ninety,GETDATE())),
ninety_day_count_cte
AS
(SELECT temporders.customernumber, COUNT(*) AS Order_Count
FROM orders
WHERE
temporders.orderdate >= DATEADD(DAY,-ninety,GETDATE())
GROUP BY
temporders.customernumber),
greater_ninety_day_cte
AS
(SELECT temporders.site, temporders.number, temporders.customernumber, temporders.orderdate,
ROW_NUMBER() OVER(PARTITION BY temporders.customernumber ORDER BY temporders.orderdate DESC) AS Row_Number
FROM orders
WHERE
temporders.orderdate < DATEADD(DAY,-ninety,GETDATE()))
SELECT site, number, customernumber, orderdate
FROM greater_ninety_day_cte AS g
LEFT JOIN ninety_day_count AS c
ON g.customernumber = c.customernumber
WHERE
g.Row_Number <= CASE
WHEN CASE WHEN c.Order_Count IS NULL THEN 0 ELSE c.Order_Count END > 6 THEN 1
ELSE (6 - CASE WHEN c.Order_Count IS NULL THEN 0 ELSE c.Order_Count END)
END
UNION ALL
SELECT site, number, customernumber, orderdate
FROM ninety_day_cte
【讨论】:
非常感谢您的回答,我将在这里通读所有答案,以更好地了解它是如何工作的。以上是关于SQL 获取超过某个日期的记录,高于某个值,最小数量的主要内容,如果未能解决你的问题,请参考以下文章
使用 C# DateTime 将 SQL 日期时间设置为最小值