SQL Server:查询的SUM结果不正确

时间:2014-03-25 20:56:12

标签: sql sql-server

我的SQL Server查询应该计算每个客户的订单数量,以及他们的奖励积分的SUM。对于大多数客户而言,结果是准确的(大多数人只有一个或两个订单)。对于少数人来说,结果非常糟糕。

这是原始查询:

SELECT 
    c.email,
    c.lastlogindate,
    c.custenabled,
    c.maillist,
    d.GroupName,
    COUNT(o.orderid) AS orders,
    SUM(r.points) AS total_points
FROM 
    ((customers c
      LEFT JOIN orders o ON (c.contactid = o.ocustomerid AND o.ostep = 'step 5')
     )
     LEFT JOIN discount_group d ON c.discount = d.id
    )
LEFT JOIN 
    customer_rewards r ON r.contactid = c.contactid
WHERE 
    c.last_update > '2014-02-01'
OR c.lastlogindate > '2014-02-01'
GROUP BY 
    c.email, c.custenabled, c.maillist, c.lastlogindate, d.GroupName;

例如,customerid 1234订购了21个订单,总计2724个点。这将报告他已经下了441个订单(21 * 21),价值57204点(2724 * 21)。原始数据很好,但是每个订单行都被它们下的订单量复制(但不是大多数客户......)

如果我将查询更改为:

SELECT 
    o.orderid,
    c.email,
    COUNT(o.orderid) AS orders,
    SUM(r.points) AS total_points
FROM 
    ((customers c
      INNER JOIN orders o ON (c.contactid = o.ocustomerid AND o.ostep = 'step 5')
     )
    )
INNER JOIN 
    customer_rewards r ON r.contactid = c.contactid
WHERE 
    c.last_update > '2014-02-01'
OR c.lastlogindate > '2014-02-01'
GROUP BY 
    c.email, o.orderid; 

聚合函数计算正确,但每个订单会显示一个结果。所以它将显示“客户1234/21订单/ 2724点”,21次。

我确实删除了第二个查询中的'discount_group'联接,但这只是为了让它更容易阅读和更改。这对结果没有任何影响。

1 个答案:

答案 0 :(得分:1)

以下是使用常用表表达式聚合结果的解决方案。

注意:这不会显示有0个订单或0个奖励积分的客户。如果您想要显示这些内容,请将INNER JOIN更改为LEFT JOIN s

WITH cteOrders AS
(
    SELECT o.ocustomerid, orderCount = count(*)
    FROM orders o
    WHERE o.ostep = 'step 5'
    GROUP BY o.ocustomerid
)
, cteRewards as
(
    SELECT cr.contactid, total_points = SUM(cr.points)
    FROM customer_rewards cr
    GROUP BY cr.contactid
)
SELECT 
    c.email,
    o.orderCount as orders,
    r.total_points
FROM 
    customers c
    INNER JOIN cteOrders o ON c.contactid = o.ocustomerid
    INNER JOIN cteRewards r ON r.contactid = c.contactid 
WHERE 
    c.last_update > '2014-02-01'
OR c.lastlogindate > '2014-02-01'
; 

或使用子查询:

SELECT 
    c.email,
    o.orderCount as orders,
    r.total_points
FROM 
    customers c
    INNER JOIN 
    (
    SELECT o.ocustomerid, orderCount = count(*)
    FROM orders o
    WHERE o.ostep = 'step 5'
    GROUP BY o.ocustomerid
    ) o ON c.contactid = o.ocustomerid
    INNER JOIN 
    (
    SELECT cr.contactid, total_points = SUM(cr.points)
    FROM customer_rewards cr
    GROUP BY cr.contactid
    ) r ON r.contactid = c.contactid 
WHERE 
    c.last_update > '2014-02-01'
OR c.lastlogindate > '2014-02-01'
;