Oracle - 索引

时间:2018-01-25 15:25:37

标签: database oracle indexing

我一直想知道如何在Oracle中改进此查询:

SELECT  FN_FORMAT_PEROPE(PEROPE) AS PERIODO,
        NVL((SELECT COUNT(1) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 1 AND P.PEROPE = R.PEROPE),0) AS NRO_EMITIDOS,
        NVL((SELECT SUM(DP.MONTO) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 1 AND P.PEROPE = R.PEROPE),0) AS MONTO_EMITIDO,
        NVL((SELECT COUNT(1) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 2 AND P.PEROPE = R.PEROPE),0) AS NRO_ABONADOS,
        NVL((SELECT SUM(DP.MONTO) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 2 AND P.PEROPE = R.PEROPE),0) AS MONTO_ABONADO,
        NVL((SELECT COUNT(1) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 3 AND P.PEROPE = R.PEROPE),0) AS NRO_RECHAZADOS,
        NVL((SELECT SUM(DP.MONTO) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 3 AND P.PEROPE = R.PEROPE),0) AS MONTO_RECHAZADO,
        NVL((SELECT COUNT(1) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 4 AND P.PEROPE = R.PEROPE),0) AS NRO_INDEBIDOS,
        NVL((SELECT SUM(DP.MONTO) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 4 AND P.PEROPE = R.PEROPE),0) AS MONTO_INDEBIDO,
        NVL((SELECT COUNT(1) FROM PG_PAGO P /*INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 5*/ WHERE P.ESTADO = 5 AND P.PEROPE = R.PEROPE),0) AS NRO_RECUPEROS,
        NVL((SELECT SUM(DP.MONTO) FROM PG_PAGO P INNER JOIN PG_DETALLE_PAGO DP ON P.IDPAGO = DP.IDPAGO WHERE DP.ESTADO = 5 AND P.PEROPE = R.PEROPE),0) AS MONTO_RECUPERO
        FROM PG_RESOLUCIONES R ORDER BY R.PEROPE ASC;

考虑一下:

  • PG_PAGO PG_DETALLE_PAGO 有大约35.000.000条记录。
  • 我对两个表使用索引,但它不起作用,因为这个查询 太慢了。

感谢您的帮助。

2 个答案:

答案 0 :(得分:3)

假设您的查询中的内部联接的注释是一个测试假象,您可以使用沿着这条线的旋转查询一次性从* _pago表中获取结果:

SELECT *
FROM   (SELECT p.perope,
               dp.estado,
               dp.monto
        FROM pg_pago p
             INNER JOIN pg_detalle_pago dp ON p.idpago = dp.idpago
        WHERE  dp.estado IN (1, 2, 3, 4, 5))
PIVOT (COUNT(*) AS nro,
       SUM(dp.monto) AS monto
       FOR estado IN (1 AS emitidos,
                      2 AS abonados,
                      3 AS rechazados,
                      4 AS indebidos,
                      5 AS recuperos));

N.B。未经测试。此外,这适用于Oracle 11g及更高版本。

获得这组结果后,应该很容易将其加入到pg_resoluciones表中,并根据需要对列进行别名。

答案 1 :(得分:2)

解决数据库性能的关键是一次查询表而不是十次。以下是实现此目的的一种方法(警告:未经测试,可能包含错误)

select  fn_format_perope(r.perope) as periodo
        , sum ( case when dp.estado = 1 and then 1 else 0 end ) as nro_emitidos
        , sum ( case when dp.estado = 1 and then dp.monto else 0 end ) as monto_emitido
        , sum ( case when dp.estado = 2 and then 1 else 0 end ) as nro_abonados
        , sum ( case when dp.estado = 2 and then dp.monto else 0 end ) as monto_abonado
        , sum ( case when dp.estado = 3 and then 1 else 0 end ) as nro_rechazados
        , sum ( case when dp.estado = 3 and then dp.monto else 0 end ) as monto_rechazado
        , sum ( case when dp.estado = 4 and then 1 else 0 end ) as nro_indebidos
        , sum ( case when dp.estado = 4 and then dp.monto else 0 end ) as monto_indebido
        , sum ( case when dp.estado = 5 and then 1 else 0 end ) as nro_recupero
        , sum ( case when dp.estado = 5 and then dp.monto else 0 end ) as monto_recupero
from pg_resoluciones r 
     left outer join pg_pago p 
        on p.perope = r.perope
     left outer join pg_detalle_pago dp 
       on p.idpago = dp.idpago 
order by r.perope asc;

您需要为PG_PAGO和PG_DETALLE_PAGO使用外连接,因为它们似乎没有匹配的记录(在标量查询中使用NVL()推断)。

此版本适用于任何版本的Oracle。 @Boneist编写的PIVOT版本更优雅,如果您使用11g或更高版本,这可能是您应该采用的应用程序。