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

时间:2018-01-03 08:38:15

标签: sql hibernate java-ee hql

我有一个信用变动的架构,如下所示:

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。

1 个答案:

答案 0 :(得分:1)

我会使用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的),那么您可以拥有包含accountactivity(orderno, bookingdate)movementtype属性的索引due,并且范围扫描将会读取只有相关的行。