根据投影从同一列中减去值

Subtract value from same column based on projection

我有一个信用变动模式,如下所示:

id | orderno | due   | bookingdate | movementtype
=================================================
1  | o11     | 30.50 | 10.01.2017  | CREDIT
2  | o22     | 50.99 | 11.01.2017  | DEBIT
3  | o11     | 20.40 | 12.01.2017  | DEBIT
4  | o22     | 77.88 | 13.01.2017  | CREDIT
5  | o11     | 05.20 | 14.01.2017  | DEBIT

我想检索给定订单号和年份的 CREDIT 减去 DEBIT 的总和。

所以对于上面的虚拟数据,传递 orderno=o11booking=2017,我希望 4.9.

我提出了一个包含两个子查询的查询:

SELECT
  (credit - debit) AS total
FROM
  (
    SELECT
      COALESCE(SUM(due), 0.0) AS credit
    FROM
      accountactivity
    WHERE
      orderno = :ordernoParam
      AND YEAR(bookingdate) = :bookingParam
      AND movementtype = 'CREDIT'
  ),
  (
    SELECT
      COALESCE(SUM(due), 0.0) AS debit
    FROM
      accountactivity
    WHERE
      orderno = :ordernoParam
      AND YEAR(bookingdate) = :bookingParam
      AND movementtype = 'DEBIT'
  )

问题:是否可以针对我的任务优化查询?出于性能原因,我想避免子查询。它应该适用于 DB2 和 Oracle 11g,可以通过本机查询,或者最好是通过 HibernateQL。

我会用CASE

SELECT
  COALESCE(SUM(CASE WHEN movementtype = 'CREDIT' THEN due END), 0.0) -
  COALESCE(SUM(CASE WHEN movementtype = 'DEBIT' THEN due END), 0.0) AS total
FROM
  accountactivity
WHERE
  orderno = :ordernoParam
  AND YEAR(bookingdate) = :bookingParam

如果您的覆盖索引 accountactivity(orderno) 包括 bookingdatemovementtypedue 属性,则应通过一次范围扫描执行它。

另一个可以优化的问题是YEAR(bookingdate) = :bookingParam条件。查询处理器不能使用它通过索引中的查找操作进行搜索。如果您将其重写为 bookingdate >= CAST('1.1.' + :bookingParam AS DATE) and bookingdate <= CAST('31.12.' + :bookingParam AS DATE) 之类的内容(这可能是特定于 DBMS 的),那么您可以拥有一个包含 movementtypedue 属性的索引 accountactivity(orderno, bookingdate) 并且范围扫描将读取仅相关行。