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 获取超过某个日期的记录,高于某个值,最小数量的主要内容,如果未能解决你的问题,请参考以下文章

获取高于某个值的二维数组中的局部最大值坐标

获取平均分高于某个值的老师教授的课程

SQL 中如何统计某一属性为某个值的记录的条数?

使用 C# DateTime 将 SQL 日期时间设置为最小值

SQL 表中如何让某条记录的某个属性值与另一个表中的某条记录的某个属性值相关联

用SQL获取某个某年某个月份的最后一天的日期