除第一个之外的所有值的总和

时间:2014-02-21 22:59:38

标签: sql sql-server tsql exception sum

我有以下三个表:

  

客户:   CUST_ID,   CUST_NAME

     

产品:   PROD_ID,   Prod_Price

     

订单:   Order_ID上,   CUST_ID,   PROD_ID,   数量,   ORDER_DATE

如何显示每个客户以及他们在首次购买时的花费?

[A] - 我可以通过乘以Products.Prod_Price和Orders.Quantity,然后乘以Cust_ID

来获得总数

[B] - 我也可以在Order_Date上为每个客户使用TOP 1进行首次购买。

但我无法弄清楚如何在一个查询中产生[A] - [B]。

非常感谢任何帮助。

4 个答案:

答案 0 :(得分:2)

哪个版本的SQL?如果2012年你可以用OFFSET 1做一些有趣的事情,但我不得不考虑更多如何使用分组。


编辑:添加受@ypercube启发的2012特定解决方案

我希望能够在WINDOW中使用OFFSET 1一步到位,但我想要的语法无效:

SUM(o.Quantity * p.Prod_Price) OVER (PARTITION BY c.Cust_ID 
                                         ORDER BY o.Order_Date 
                                        OFFSET 1)

相反,我可以指定行装箱,但必须将结果集过滤到正确的集合。查询计划与@ ypercube不同,但两者在一起运行时显示50%。它们的速度是我原来答案的两倍。

WITH cte AS (
  SELECT c.Cust_ID
        ,c.Cust_Name          
        ,SUM(o.Quantity * p.Prod_Price) OVER(PARTITION BY c.Cust_ID
                                                 ORDER BY o.Order_ID
                                                  ROWS BETWEEN 1 FOLLOWING
                                                           AND UNBOUNDED FOLLOWING) AmountSpent
        ,rn = ROW_NUMBER() OVER(PARTITION BY c.Cust_ID ORDER BY o.Order_ID)
FROM Customers AS c
     INNER JOIN
     Orders AS o ON o.Cust_ID = c.Cust_ID
     INNER JOIN
     Products AS p ON p.Prod_ID = o.Prod_ID 
)
SELECT Cust_ID
      ,Cust_Name
      ,ISNULL(AmountSpent ,0) AmountSpent
  FROM cte WHERE rn=1

我更通用的解决方案类似于peter.petrov,但他的样本数据“开箱即用”。这可能是我的样本数据的问题。差异包括使用CTE和带有相关子查询的NOT EXISTS。

CREATE TABLE Customers (Cust_ID INT, Cust_Name VARCHAR(10))
CREATE TABLE Products (Prod_ID INT, Prod_Price MONEY)
CREATE TABLE Orders (Order_ID INT, Cust_ID INT, Prod_ID INT, Quantity INT, Order_Date DATE)

INSERT INTO Customers SELECT 1 ,'Able' 
                UNION SELECT 2, 'Bob' 
                UNION SELECT 3, 'Charlie' 
INSERT INTO Products SELECT 1, 10.0
INSERT INTO Orders SELECT 1, 1, 1, 1, GetDate()
             UNION SELECT 2, 1, 1, 1, GetDate()
             UNION SELECT 3, 1, 1, 1, GetDate()
             UNION SELECT 4, 2, 1, 1, GetDate()
             UNION SELECT 5, 2, 1, 1, GetDate()
             UNION SELECT 6, 3, 1, 1, GetDate()

;WITH CustomersFirstOrder AS (
  SELECT Cust_ID
        ,MIN(Order_ID) Order_ID 
    FROM Orders 
   GROUP BY Cust_ID
)
SELECT c.Cust_ID
      ,c.Cust_Name
      ,ISNULL(SUM(Quantity * Prod_Price),0) CustomerOrderTotalAfterInitialPurchase
  FROM Customers c
       LEFT JOIN (
         SELECT Cust_ID
               ,Quantity
               ,Prod_Price 
           FROM Orders o 
                INNER JOIN
                Products p ON o.Prod_ID = p.Prod_ID
          WHERE NOT EXISTS (SELECT 1 FROM CustomersFirstOrder a WHERE a.Order_ID=o.Order_ID)
        ) b ON c.Cust_ID = b.Cust_ID

 GROUP BY c.Cust_ID
         ,c.Cust_Name

DROP TABLE Customers
DROP TABLE Products 
DROP TABLE Orders 

答案 1 :(得分:2)

对于SQL-Server 2005,2008和2008R2:

; WITH cte AS
  ( SELECT 
        c.Cust_ID, c.Cust_Name, 
        Amount = o.Quantity * p.Prod_Price,
        Rn = ROW_NUMBER() OVER (PARTITION BY c.Cust_ID
                                ORDER BY o.Order_Date)
    FROM
        Customers AS c
      JOIN
        Orders AS o    ON o.Cust_ID = c.Cust_ID
      JOIN
        Products AS p  ON p.Prod_ID = o.Prod_ID 
  ) 
SELECT 
    Cust_ID, Cust_Name,
    AmountSpent = SUM(Amount)
FROM 
    cte 
WHERE 
    Rn >= 2
GROUP BY 
    Cust_ID, Cust_Name ;

对于SQL-Server 2012,使用FIRST_VALUE()分析函数:

SELECT DISTINCT
    c.Cust_ID, c.Cust_Name,
    AmountSpent = SUM(o.Quantity * p.Prod_Price) 
                      OVER (PARTITION BY c.Cust_ID)
                - FIRST_VALUE(o.Quantity * p.Prod_Price) 
                      OVER (PARTITION BY c.Cust_ID
                            ORDER BY o.Order_Date)
FROM
    Customers AS c
  JOIN
    Orders AS o    ON o.Cust_ID = c.Cust_ID
  JOIN
    Products AS p  ON p.Prod_ID = o.Prod_ID ;

使用OFFSET FETCHCROSS APPLY的另一种方式(仅适用于2012年):

SELECT 
    c.Cust_ID, c.Cust_Name,
    AmountSpent = SUM(x.Quantity * x.Prod_Price) 
FROM
    Customers AS c
  CROSS APPLY
    ( SELECT 
          o.Quantity, p.Prod_Price
      FROM 
          Orders AS o 
        JOIN
          Products AS p  ON p.Prod_ID = o.Prod_ID 
      WHERE
          o.Cust_ID = c.Cust_ID
      ORDER BY
          o.Order_Date
      OFFSET 
          1 ROW
      -- FETCH NEXT                        -- not needed,
      --     20000000000 ROWS ONLY         -- can be removed
    ) AS x 
GROUP BY 
    c.Cust_ID, c.Cust_Name ;

SQL-Fiddle

进行测试

请注意,第二个解决方案也会返回只有一个订单的客户(Amount0),而其他两个解决方案不会返回这些客户。

答案 2 :(得分:0)

试试这个。它应该这样做。

SELECT  c1.cust_name ,
        c1.cust_id ,
        SUM(p1.Prod_Price)
FROM    orders o1
        JOIN products p1 ON o1.prod_id = p1.prod_id
        JOIN customers c1 ON o1.cust_id = c1.cust_id
        LEFT JOIN ( SELECT  o2.cust_id ,
                            MIN(o2.Order_Date) AS Order_Date
                    FROM    orders o2
                    GROUP BY o2.cust_id
                  ) t ON o1.cust_id = t.cust_id
                         AND o1.Order_Date = t.Order_Date
WHERE   t.Order_Date IS NULL
GROUP BY c1.cust_name ,
        c1.cust_id 

答案 3 :(得分:0)

您必须按客户编号,然后您可以获得第一个订单的金额以及下一个订单的CTE和ROW_NUMBER(),如下所示:

; WITH    NumberedOrders
      AS ( SELECT   Customers.Cust_Id ,
                    Customers.Cust_Name ,
                    ROW_NUMBER() OVER ( ORDER BY Customers.Cust_id ) AS Order_Number ,
                    Orders.Order_Date ,
                    Products.Prod_price * Orders.Quantity AS Amount
           FROM     Orders
                    INNER JOIN Customers ON Orders.Cust_Id = Customers.Cust_Id
                    INNER JOIN Products ON Orders.Prod_Id = Products.Prod_Id
         )
SELECT  Cust_Id ,
        SUM(CASE WHEN Order_Number = 1 THEN Amount
                 ELSE 0
            END) AS A_First_Order ,
        SUM(CASE WHEN Order_Number = 1 THEN 0
                 ELSE Amount
            END) AS B_Other_orders ,
        SUM(Amount) AS C_All_orders
FROM    NumberedOrders
GROUP BY Cust_Id
ORDER BY Cust_Id