改善选择语句性能

时间:2014-05-30 14:05:34

标签: sql oracle sql-tuning

我遇到了Select性能问题,如何才能提高SQL的效率?

SELECT
 O.DT_OCORRENCIA,
 S.NU_SMP,
 CLI.NM_APELIDO CLI,
 TRANS.NM_ENTIDADE TRANS,
 TPO.DS_TIPO_OCORRENCIA,
 O.DS_OCORRENCIA,
 REPLACE(FNC_RETORNA_MOTORISTAS(S.CD_SMP), '<br>', ';') AS NOME_MOTORISTA,
 OG.DS_NOME AS DS_NOME_ORIGEM,
 DG.DS_NOME AS DS_NOME_DESTINO,
 U.NM_LOGIN AS DS_USUARIO_OCORRENCIA,
 CASE
   WHEN O.CD_USUARIO_INFORMOU = 717 THEN
    'NAO'
   ELSE
    'SIM'
 END FL_MANUAL,
 CASE
   WHEN (O.FL_ENVIOU_EMAIL_CLIENTE = 'S' OR O.FL_ENVIOU_EMAIL_TRANSP = 'S') THEN
    'SIM'
   ELSE
    'NÃO'
 END ENVIOU_EMAIL,
 DECODE(O.NU_LOCAL_OCORRENCIA,
        '1',
        'Origem',
        '2',
        'Em transito',
        '3',
        'Alvos',
        '4',
        'Indiferente') DS_LOCAL_OCORRENCIA,
 TOPER.DS_TIPO_OPERACAO
  FROM TB_SMP S
 INNER JOIN TB_OCORRENCIA O
    ON O.CD_SMP = S.CD_SMP
 INNER JOIN TB_TIPO_OCORRENCIA TPO
    ON O.CD_TIPO_OCORRENCIA = TPO.CD_TIPO_OCORRENCIA
  LEFT JOIN TB_TIPO_OPERACAO TOPER
    ON TOPER.CD_TIPO_OPERACAO = S.CD_TIPO_OPERACAO
  LEFT JOIN TB_USUARIO U
    ON U.CD_USUARIO = O.CD_USUARIO_INFORMOU
  LEFT JOIN TB_ENTIDADE CLI
    ON CLI.CD_ENTIDADE = S.CD_ENTIDADE_CLIENTE
  LEFT JOIN TB_ENTIDADE TRANS
    ON TRANS.CD_ENTIDADE = S.CD_ENTIDADE_TRANSPORTADOR
  LEFT JOIN TB_PONTO_GEOREFERENCIADO OG
    ON OG.CD_PONTO_GEOREFERENCIADO = S.CD_PONTO_GEO_ORIGEM
  LEFT JOIN TB_PONTO_GEOREFERENCIADO DG
    ON (S.CD_PONTO_GEO_DESTINO IS NULL AND
       DG.CD_PONTO_GEOREFERENCIADO =
       FNC_GET_ULTIMA_ENTREGA_PONTO(S.CD_SMP))
    OR (S.CD_PONTO_GEO_DESTINO IS NOT NULL AND
       DG.CD_PONTO_GEOREFERENCIADO = S.CD_PONTO_GEO_DESTINO)

 WHERE EXISTS (SELECT 1
          FROM TB_TIPO_OCORRENCIA T
         WHERE T.CD_TIPO_OCORRENCIA = O.CD_TIPO_OCORRENCIA)

   AND S.DT_CADASTRO BETWEEN to_date('21/04/2014', 'dd/MM/yyyy') AND to_date('09/06/2014', 'dd/MM/yyyy')
   AND O.DT_OCORRENCIA BETWEEN to_date('10/05/2014','dd/MM/yyyy') AND to_date('20/05/2014','dd/MM/yyyy')
   AND S.cd_tipo_operacao in
       (-1, 27, 11, 13, 37, 6, 7, 21, 12, 36, 28, 4, 5)

-- order by using linq?
 ORDER BY CLI.NM_APELIDO,
          TRANS.NM_APELIDO,
          O.DT_OCORRENCIA,
          DS_TIPO_OCORRENCIA

这是我的执行计划: explain plan

PS:我对sql tunnig一无所知:(

1 个答案:

答案 0 :(得分:0)

您可以将一些WHERE条件移动到JOIN条件,因为您在这些表上执行INNER JOIN,如下所示。然后,当您进入LEFT JOIN条件时,您将在较小的数据集上运行。

SELECT
 O.DT_OCORRENCIA,
 S.NU_SMP,
 CLI.NM_APELIDO CLI,
 TRANS.NM_ENTIDADE TRANS,
 TPO.DS_TIPO_OCORRENCIA,
 O.DS_OCORRENCIA,
 REPLACE(FNC_RETORNA_MOTORISTAS(S.CD_SMP), '<br>', ';') AS NOME_MOTORISTA,
 OG.DS_NOME AS DS_NOME_ORIGEM,
 DG.DS_NOME AS DS_NOME_DESTINO,
 U.NM_LOGIN AS DS_USUARIO_OCORRENCIA,
 CASE
   WHEN O.CD_USUARIO_INFORMOU = 717 THEN
    'NAO'
   ELSE
    'SIM'
 END FL_MANUAL,
 CASE
   WHEN (O.FL_ENVIOU_EMAIL_CLIENTE = 'S' OR O.FL_ENVIOU_EMAIL_TRANSP = 'S') THEN
    'SIM'
   ELSE
    'NÃO'
 END ENVIOU_EMAIL,
 DECODE(O.NU_LOCAL_OCORRENCIA,
        '1',
        'Origem',
        '2',
        'Em transito',
        '3',
        'Alvos',
        '4',
        'Indiferente') DS_LOCAL_OCORRENCIA,
 TOPER.DS_TIPO_OPERACAO
  FROM TB_SMP S
 INNER JOIN TB_OCORRENCIA O
    ON O.CD_SMP = S.CD_SMP
   AND S.DT_CADASTRO BETWEEN to_date('21/04/2014', 'dd/MM/yyyy') AND to_date('09/06/2014', 'dd/MM/yyyy')
   AND O.DT_OCORRENCIA BETWEEN to_date('10/05/2014','dd/MM/yyyy') AND to_date('20/05/2014','dd/MM/yyyy')
   AND S.cd_tipo_operacao in
       (-1, 27, 11, 13, 37, 6, 7, 21, 12, 36, 28, 4, 5)
 INNER JOIN TB_TIPO_OCORRENCIA TPO
    ON O.CD_TIPO_OCORRENCIA = TPO.CD_TIPO_OCORRENCIA
  LEFT JOIN TB_TIPO_OPERACAO TOPER
    ON TOPER.CD_TIPO_OPERACAO = S.CD_TIPO_OPERACAO
  LEFT JOIN TB_USUARIO U
    ON U.CD_USUARIO = O.CD_USUARIO_INFORMOU
  LEFT JOIN TB_ENTIDADE CLI
    ON CLI.CD_ENTIDADE = S.CD_ENTIDADE_CLIENTE
  LEFT JOIN TB_ENTIDADE TRANS
    ON TRANS.CD_ENTIDADE = S.CD_ENTIDADE_TRANSPORTADOR
  LEFT JOIN TB_PONTO_GEOREFERENCIADO OG
    ON OG.CD_PONTO_GEOREFERENCIADO = S.CD_PONTO_GEO_ORIGEM
  LEFT JOIN TB_PONTO_GEOREFERENCIADO DG
    ON (S.CD_PONTO_GEO_DESTINO IS NULL AND
       DG.CD_PONTO_GEOREFERENCIADO =
       FNC_GET_ULTIMA_ENTREGA_PONTO(S.CD_SMP))
    OR (S.CD_PONTO_GEO_DESTINO IS NOT NULL AND
       DG.CD_PONTO_GEOREFERENCIADO = S.CD_PONTO_GEO_DESTINO)
-- order by using linq?
 ORDER BY CLI.NM_APELIDO,
          TRANS.NM_APELIDO,
          O.DT_OCORRENCIA,
          DS_TIPO_OCORRENCIA;

在用于加入/过滤的列上添加索引也会有所帮助,尤其是在

TB_SMP.DT_CADASTRO
TB_SMP.cd_tipo_operacao
TB_OCORRENCIA.DT_OCORRENCIA
TB_ENTIDADE.CD_ENTIDADE