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

これは機能しているように見えますが、扱いにくく遅いです。もう 1 つの大きな問題は、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 に答える