Есть ли способ улучшить этот запрос

#sql #oracle #query-performance

#sql #Oracle #запрос-производительность

Вопрос:

Я изучал, как использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ, поэтому я изменил следующий запрос, чтобы использовать внутреннее соединение, но у меня возникли проблемы с последним фрагментом запроса, можно ли использовать там внутреннее соединение? У меня возникли проблемы, потому что «MOV» — это запрос, выполненный перед этим фрагментом… Может кто-нибудь помочь мне понять, как его использовать? Следующие запросы работают, просто пытаясь улучшить их настолько, насколько я могу

фрагмент:

 FROM   TBLMOVOBLIGACIONES MOV1
                                  WHERE MOV1.STRMOVANOMES = :periodo
                                   AND  MOV1.STRCLINIT = MOV.STRCLINIT
                                   AND  MOV1.STROBLOBLIGSARC = MOV.STROBLOBLIGSARC
  

Полный запрос:

 SELECT * FROM (

 

SELECT MOV.NUMPROCODIGO APLICATIVO,
       MOV.STRCLINIT NIT,
       CL.STRCLINOMBRE CLIENTE,
       MOV.STROBLOBLIGSARC OBLIGACION,
       MOV.NUMMOVVLRCAPCREDITO   MOV.NUMMOVVLRINTCREDI   MOV.NUMMOVVLRCAPOTRO SALDO_OBL,
       MOV.NUMMOVTIPOCREDITO TIPO_CREDITO,
       MOV.NUMMOVNRODIASCAP DIAS_CAP,
       MOV.NUMMOVNRODIASINT DIAS_INT,
       MOV.STRMOVCALIFEDAD,
       CASE 
        WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=0 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 30 THEN 'A'
        WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=31 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 90 THEN 'B'
        WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=91 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 180 THEN 'C'
        WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=181 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 360 THEN 'D'
        WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=361 THEN 'E'
       END AS CALIFEDAD_REAL
FROM TBLMOVOBLIGACIONES MOV INNER JOIN TBLCLIENTES CL ON MOV.STRCLINIT = CL.STRCLINIT 
WHERE MOV.STRMOVANOMES = :periodo
 AND  MOV.NUMPROCODIGO NOT IN (24)
 AND MOV.NUMMOVVLRCAPCREDITO   MOV.NUMMOVVLRINTCREDI   MOV.NUMMOVVLRCAPOTRO > 0
 AND  MOV.NUMMOVTIPOCREDITO = 1
 AND  NVL(MOV.STRMOVCALIFEDAD,'N') != (SELECT CASE 
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=0 AND GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) <= 30  THEN 'A'
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=31 AND GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) <= 90 THEN 'B'
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=91 AND GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) <= 180 THEN 'C'
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=181 AND GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) <= 360 THEN 'D'
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=361 THEN 'E'
                                     END AS CALIF_CALC
                              FROM   TBLMOVOBLIGACIONES MOV1
                              WHERE MOV1.STRMOVANOMES = :periodo
                               AND  MOV1.STRCLINIT = MOV.STRCLINIT
                               AND  MOV1.STROBLOBLIGSARC = MOV.STROBLOBLIGSARC)
  

Комментарии:

1. Пожалуйста, задавайте только один вопрос за сообщение — это значительно упрощает ответ. Кроме того, не могли бы вы опубликовать DDL, образцы данных и ожидаемые результаты, в идеале с dbfiddle? Наконец, чего вы пытаетесь достичь — пытаетесь ли вы сделать код «лучше» или у вас проблемы с производительностью?

2. Какой запрос следует изучить? полностью запутался в вопросе.

3. Извините, я сократил вопрос только до основного запроса, который мне нужна помощь в улучшении, я пытаюсь повысить его производительность, а также улучшить обе вещи, но главное, как я могу использовать inner join для последнего фрагмента запроса

4. Преобразование последнего фрагмента для использования внутреннего соединения не имеет смысла (см. Ответ @MatthewMcPeak), и это, вероятно, не сильно улучшит производительность.

Ответ №1:

Я бы поставил под сомнение эту часть вашего запроса:

  AND  NVL(MOV.STRMOVCALIFEDAD,'N') != (SELECT CASE 
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=0 AND GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) <= 30  THEN 'A'
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=31 AND GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) <= 90 THEN 'B'
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=91 AND GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) <= 180 THEN 'C'
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=181 AND GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) <= 360 THEN 'D'
                                      WHEN GREATEST(NVL(MOV1.NUMMOVNRODIASCAP,0), NVL(MOV1.NUMMOVNRODIASINT,0)) >=361 THEN 'E'
                                     END AS CALIF_CALC
                              FROM   TBLMOVOBLIGACIONES MOV1
                              WHERE MOV1.STRMOVANOMES = :periodo
                               AND  MOV1.STRCLINIT = MOV.STRCLINIT
                               AND  MOV1.STROBLOBLIGSARC = MOV.STROBLOBLIGSARC)
  

Либо (strmovanomes, strclinit, stroblobligsarc) включает (или точно есть) первичный ключ tblmovobligaciones , либо нет.

Если if делает, то подзапрос запрашивает ту же строку, tblmovobligaciones что у вас уже есть. В этом случае подзапрос должен быть удален, а условие заменено на:

  AND  NVL(MOV.STRMOVCALIFEDAD,'N') != CASE 
                                      WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=0 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 30  THEN 'A'
                                      WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=31 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 90 THEN 'B'
                                      WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=91 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 180 THEN 'C'
                                      WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=181 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 360 THEN 'D'
                                      WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=361 THEN 'E'
                                     END
  

Если это не так, то что мешает этому подзапросу возвращать несколько строк (что может вызвать ошибку во время выполнения)?

Вы также можете выбрать это большое выражение один раз и повторно использовать его. Это вопрос предпочтений. Это будет выглядеть так:

 SELECT MOV.NUMPROCODIGO APLICATIVO,
       MOV.STRCLINIT NIT,
       CL.STRCLINOMBRE CLIENTE,
       MOV.STROBLOBLIGSARC OBLIGACION,
       MOV.NUMMOVVLRCAPCREDITO   MOV.NUMMOVVLRINTCREDI   MOV.NUMMOVVLRCAPOTRO SALDO_OBL,
       MOV.NUMMOVTIPOCREDITO TIPO_CREDITO,
       MOV.NUMMOVNRODIASCAP DIAS_CAP,
       MOV.NUMMOVNRODIASINT DIAS_INT,
       MOV.STRMOVCALIFEDAD,
       mov.califedad_real_expr CALIFEDAD_REAL
FROM ( SELECT mov.*,
              CASE 
                WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=0 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 30 THEN 'A'
                WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=31 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 90 THEN 'B'
                WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=91 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 180 THEN 'C'
                WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=181 AND GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) <= 360 THEN 'D'
                WHEN GREATEST(NVL(MOV.NUMMOVNRODIASCAP,0), NVL(MOV.NUMMOVNRODIASINT,0)) >=361 THEN 'E'
              END AS califedad_real_expr
        FROM TBLMOVOBLIGACIONES MOV) MOV 
INNER JOIN TBLCLIENTES CL ON MOV.STRCLINIT = CL.STRCLINIT 
WHERE MOV.STRMOVANOMES = :periodo
 AND  MOV.NUMPROCODIGO NOT IN (24)
 AND MOV.NUMMOVVLRCAPCREDITO   MOV.NUMMOVVLRINTCREDI   MOV.NUMMOVVLRCAPOTRO > 0
 AND  MOV.NUMMOVTIPOCREDITO = 1
 AND  NVL(MOV.STRMOVCALIFEDAD,'N') != mov.califedad_real_expr
  

(Предполагая, что я прав в том, что подзапрос не нужен)