2

我的任务是:

  • 选择在特定日期范围(通常是特定月份)内有第 n 个订单的所有客户的列表。
  • 此列表需要包含:客户 ID、前 n 个订单的总和

我的表是这样的:

  • [dbo.customers]:客户ID
  • [dbo.orders]:orderID、customerID、orderDate、orderTotal

这是我迄今为止尝试过的:

-- Let's assume our threshold (n) is 10
-- Let's assume our date range is April 2013
-- Get customers that already had n orders before the beginning of the given date range.
DECLARE @tmpcustomers TABLE (tmpcustomerID varchar(8))
INSERT INTO
    @tmpcustomers
SELECT
    c.customerID
FROM
    orders o
    INNER JOIN customers c ON o.customerID = c.customerID
WHERE
    o.orderDate < '2013-04-01'
GROUP BY c.customerID
HAVING (COUNT(o.orderID) >= 10)


-- Now get all customers that have n orders sometime within the given date range
-- but did not have n orders before the beginning of the given date range.
SELECT
    a.customerID, SUM(orderTotal) AS firstTenOrderTotal
SELECT 
    o.customerID, o.orderID, o.orderTotal
FROM
    orders o
    INNER JOIN customers c ON c.customerID = o.customerID    
WHERE
    a.customerID NOT IN ( SELECT tmpcustomerID FROM @tmpcustomers )
AND
    o.orderDate > '2013-04-01'
AND
    o.orderDate < '2013-05-01'
GROUP BY c.customerID
    HAVING COUNT(o.orderID) >= 10

这似乎有效,但它笨重且缓慢。另一个大问题是 firstTenOrderTotal 实际上是给定日期范围结束时订单总量的总和,不一定是前 10 个。

任何关于更好方法的建议将不胜感激。

4

3 回答 3

0

这是我的建议:

Use Northwind
GO


select ords.OrderID , ords.OrderDate , '<-->' as Sep1 , derived1.* from
dbo.Orders ords 
join
(
select CustomerID, OrderID, ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY OrderId DESC) AS ThisCustomerCardinalOrderNumber from dbo.Orders 
) as derived1
    on ords.OrderID = derived1.OrderID

where 
derived1.ThisCustomerCardinalOrderNumber = 3
and ords.OrderDate between '06/01/1997' and '07/01/1997' 

编辑:::::::::

我拿了我的 CTE 示例,并为多个客户重新设计了它(见下文)。给它大学尝试。

Use Northwind
GO



declare @BeginDate datetime
declare @EndDate datetime

select @BeginDate = '01/01/1900'
select @EndDate = '12/31/2010'

;
 WITH
 MyCTE /* http://technet.microsoft.com/en-us/library/ms175972.aspx */
 ( ShipName,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address],
 City,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName,
 ProductID,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight,ROWID) AS
 (
 SELECT
 ShipName ,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address]
 ,City ,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName
 ,ProductID ,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight
 , ROW_NUMBER() OVER (PARTITION BY CustomerID  ORDER BY OrderDate , ProductName ASC ) as ROWID  /* Note that the ORDER BY (here) is directly related to the ORDER BY (near the very end of the query) */
 FROM
 dbo.Invoices inv /* “Invoices” is a VIEW, FYI */
 where
(inv.OrderDate between @BeginDate and @EndDate)
 )
 SELECT
 /*
 ShipName,ShipAddress,ShipCity,ShipRegion,ShipPostalCode,ShipCountry,CustomerID,CustomerName,[Address],
 City,Region,PostalCode,Country,Salesperson,OrderID,OrderDate,RequiredDate,ShippedDate,ShipperName,
 ProductID,ProductName,UnitPrice,Quantity,Discount,ExtendedPrice,Freight,
 */

/*trim the list down a little for the final output */

CustomerID ,OrderID , OrderDate, (ExtendedPrice + Freight) as ComputedTotal

/*The below line is the “trick”. I reference the above CTE, but only get data that is less than or equal to the row that I am on (outerAlias.ROWID)*/
 , (Select SUM (ExtendedPrice + Freight) from MyCTE innerAlias where innerAlias.ROWID <= outerAlias.ROWID and innerAlias.CustomerID = outerAlias.CustomerID) as RunningTotal
 , ROWID as ROWID_SHOWN_FOR_KICKS , OrderDate as OrderDate
 FROM
 MyCTE outerAlias

 GROUP BY CustomerID ,OrderID, OrderDate, ProductName,(ExtendedPrice + Freight) ,ROWID,OrderDate

/*Two Order By Options*/
 ORDER BY outerAlias.CustomerID , outerAlias.OrderDate , ProductName

/* << Whatever the ORDER BY is here, should match the “ROW_NUMBER() OVER ( ORDER BY ________ ASC )” statement inside the CTE */
 /*ORDER BY outerAlias.ROWID */ /* << Or, to keep is more “trim”, ORDER BY the ROWID, which will of course be the same as the “ROW_NUMBER() OVER ( ORDER BY” inside the CTE */
于 2013-05-10T16:33:24.440 回答
0

试试这个。根据您的订单分布,它可能会表现得更好。在此查询中,我组装了该范围内的订单列表,然后回头计算先前订单的数量(也获取 orderTotal)。

注意:我假设 orderID 随着订单的下达而增加。如果不是这种情况,只需在日期上使用 row_number 将序列投影到查询中。

declare @orders table (orderID int primary key identity(1,1), customerID int, orderDate datetime, orderTotal int)
insert into @orders (customerID, orderDate, orderTotal)
    select 1, '2013-01-01', 1 union all
    select 1, '2013-01-02', 2 union all
    select 1, '2013-02-01', 3 union all
    select 2, '2013-01-25', 5 union all
    select 2, '2013-01-26', 5 union all
    select 2, '2013-02-02', 10 union all
    select 2, '2013-02-02', 10 union all
    select 2, '2013-02-04', 20

declare @N int, @StartDate datetime, @EndDate datetime
select  @N = 3,
        @StartDate = '2013-02-01',
        @EndDate = '2013-02-20'

select  o.customerID, 
        [total] = o.orderTotal + p.total --the nth order + total prior
from    @orders o
cross
apply   (   select  count(*)+1, sum(orderTotal)
            from    @orders
            where   customerId = o.customerID and 
                    orderID < o.orderID and
                    orderDate <= o.orderDate
        ) p(n, total)
where   orderDate between @StartDate and @EndDate and p.n = @N
于 2013-05-10T16:12:02.210 回答
0
  1. 在插入@tmpcustomers 时,您为什么要重新加入客户表?订单表已经有您想要的 customerID。另外,您为什么要查找订单日期早于您的日期范围的订单?您不只是希望在一个日期范围内有超过 n 个订单的客户吗?这将使第二个查询更容易。

  2. 通过在表变量@tmpcustomers 中仅包含具有 n 个或更多订单的客户,您应该能够将它和第二个查询中的订单表连接起来,以获得您将再次限制的那些客户的所有订单的总和订单表记录到您的日期范围(因此您不会收到超出该范围的订单)。这将在您的最终结果查询中删除 having 语句和到 customers 表的连接。

于 2013-05-10T15:59:31.267 回答