在MySQL中没有CTE的情况下有效地连接相同的数据两次?

时间:2017-08-25 14:51:40

标签: mysql

我有这个查询,总结付款和报销的承诺。它有效,但闻起来很糟糕:

select P.pledgeID, P.decamount, 
(
    sum(coalesce(C1.decamount, 0)) - sum(coalesce(C2.decamount, 0))
) as paymentTotal
from Pledge P
left join (select C.*, CT.eaddOrSubtract 
             from `Payment` C 
             left join PaymentType CT on C.paymentTypeID = CT.paymentTypeID ) 
         C1 on P.pledgeID = C1.pledgeID and C1.eaddOrSubtract = 'add' 
left join (select C.*, CT.eaddOrSubtract 
             from `Payment` C 
             left join PaymentType CT on C.paymentTypeID = CT.paymentTypeID) 
         C2 on P.pledgeID = C2.pledgeID and C2.eaddOrSubtract = 'subtract' 
group by pledgeID

特别是,我认为应该有更好的方法来处理joins内的joins,特别是因为它们会产生相同的结果。在另一个RDBMS上,我使用的是CTE,但这里没有。是否有更有效的方法来计算这些支付总额(考虑到一些是净增加和其他净减法的事实)?

架构信息:

PaymentType
---
| paymentTypeID | eaddOrSubtract | ...
| 1             | add            |
| 2             | add            |
| 3             | subtract       |
| 4             | add            |
| 5             | subtract       |



Payment
---
| checkID | pledgeID | paymentTypeID | decamount | ...
| 1       | 19415    | 4             | 15.19     |
| 2       | 19414    | 2             | 900.00    |
| 3       | 19106    | 5             | 3856.00   |
| 4       | 19106    | 3             | 52.00     |
| 5       | 19414    | 1             | 15.00     |

1 个答案:

答案 0 :(得分:0)

查询应选择所有承诺(他们的pledgeIDdecamount)以及每个承诺的付款总额。有些付款是积极的,有些是负面的。

您查询选择所有承诺,将正付款加入每个承诺,并将负付款加入与承诺相关的每一行。如果最多只有一个负数且最多一个正数,那么它几乎可以正常工作(除非它没有付款时返回NULL而不是0)。一旦在一个类别(正面/负面)中至少有两个付款,而在另一个类别中至少有一个付款,则会出现问题。每个负面付款都与同一承诺中的每个正付款相结合,所有货币对都会相加。

直接查看问题的一种更简洁的方法是在本答案的第一段之后。只需要在eaddOrSubtract上使用过滤器进行两次连接,就可以在子查询中进行一次连接,其中子查询在内部处理要求量的符号。 CASE operator非常适合这样的工作。

SELECT
  P.pledgeID
, P.decamount
, COALESCE(SUM(C.signedDecamount), 0) AS paymentTotal
FROM Pledge P
LEFT JOIN (
  SELECT
    C.*
  , CASE CT.eaddOrSubtract
    WHEN 'add' THEN C.decamount
    WHEN 'subtract' THEN -C.decamount
    END AS signedDecamount
  FROM Payment C
  LEFT JOIN PaymentType CT ON C.paymentTypeID = CT.paymentTypeID
) C ON P.pledgeID = C.pledgeID
GROUP BY P.pledgeID

如果没有付款加入承诺,或者他们加入的所有付款都为空COALESCE(),那么就会发出decamount电话。 COALESCE()SUM()到{0}总是可以安全地省略,因为SUM()会跳过NULL;我猜这些调用只是在原始查询中破解连接的角落情况的工件。

SQL Fiddle