2014-03-25 6 views
1

SQL Server 쿼리는 각 고객의 주문 수를 계산하고 보상 포인트가 SUM 인 것으로 가정합니다. 대부분의 고객에게 결과는 정확합니다 (대부분의 사람들은 한 두 가지 주문 만 가지고 있습니다). 소수의 사람들에게는 그 결과가 크게 벗어났습니다.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 2724 포인트를 총 21 개 주문을 배치했습니다

여기에 원래 쿼리입니다. 이것은 57204 점 (2724 * 21)에 해당하는 441 건의 주문 (21 * 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'조인을 제거했지만 읽기 및 변경이 더 쉽습니다. 결과에 아무런 영향을 미치지 않았습니다.

+0

당신이 테이블과 일부 샘플 행을 생성하는 SQL을 줄 수 있습니까? 또한 예상되는 출력 표와 잘못된 결과를 표시 할 수 있습니까? –

+2

귀하의 쿼리는 각 고객에 대해 카티 젼 제품을 생성하고 있습니다. 이것은 독립 치수를 따라 조인 할 때 발생합니다. 해결 방법은 조인을 수행하기 전에 하위 쿼리에서 집계하는 것입니다. –

+0

discount_group 테이블에 id 값 당 둘 이상의 행이있는 것처럼 보입니다. 이 경우인가요? – Hogan

답변

1

다음은 공통 테이블 표현식을 사용하여 결과를 집계하는 솔루션입니다.

참고 : 0 주문 또는 0 보상 포인트가있는 고객은 표시되지 않습니다. 당신이이를 표시하고 싶은 경우 LEFT JOIN

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' 
; 

또는 사용하여 하위 쿼리에 INNER JOIN의 변경 :

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' 
; 
+0

당신. 테스트 데이터에서이 작업을 검증 할 수있었습니다. 불행히도 실제 데이터의 경우 API를 통해 db에 액세스합니다 (SQL 문자열을 인수로 취함). CTE가 해당 API를 통해 작동하지 않는 것처럼 보입니다. :(올바른 것으로 표시해 두 겠지만 다른 방법으로 생각하면 감사하겠습니다.) – Nelluk

+0

컴퓨터로 돌아 가면 몇 분 후에 업데이트를 게시 할 것입니다. cteOrders 및 cteRewards를 각 쿼리 텍스트로 대체하십시오. 각 cte에 대해 "as"다음에 괄호를 포함하십시오. – BateTech

+0

"with"단어 앞에 세미콜론을 추가하고 API를 사용하여 작동 시키도록하십시오. – BateTech