Oracle: PLSQL-запрос с расслабляющим, где условие - элегантный способ реализовать - PullRequest
1 голос
/ 21 марта 2012

Посмотрите на следующий запрос.Как я мог написать это более читабельным способом?Я хотел бы уменьшить использование IF

  vSQL := ' SELECT loc.RMV_REMI_VIRTUALE_COD, loc.LOC_DATA_VER_FIN FROM ENI_SAG_TSF_LOCALITA_DEF loc WHERE 1=1 ';
  IF P_LOC_LOCALITA_COD IS NOT NULL THEN
    vSQL := vSQL||' AND LOC.LOC_LOCALITA_COD = '''||P_LOC_LOCALITA_COD||''' ';
  END IF;
  IF P_ISTAT_CITTA IS NOT NULL THEN
    vSQL := vSQL||' AND loc.COMB_ISTAT_COD = '''||P_ISTAT_CITTA||''' ';
  END IF;
  IF P_PLAY_PLAYER_COD IS NOT NULL THEN
    vSQL := vSQL||' AND LOC.PLAY_PLAYER_COD = '''||P_PLAY_PLAYER_COD||''' ';
  END IF;
  IF P_LOC_DATA IS NOT NULL THEN
    vSQL := vSQL||' AND TO_TIMESTAMP ('''||P_LOC_DATA||''' , ''DD/MM/YYYY HH24:MI:SS.FF3'') BETWEEN LOC.LOC_DATA_VER_INI AND LOC.LOC_DATA_VER_FIN ';
  END IF; 

Ответы [ 3 ]

4 голосов
/ 21 марта 2012

Что ж, я всегда нахожу код, который использует переменные связывания, а не конкатенацию значений, легче для чтения (и это гораздо лучшая практика). Вы не говорите, как будет выполняться SQL, но предполагая, что REF CURSOR вы можете сделать это так:

vSQL := ' SELECT loc.RMV_REMI_VIRTUALE_COD, loc.LOC_DATA_VER_FIN FROM ENI_SAG_TSF_LOCALITA_DEF loc WHERE 1=1 ';
  IF P_LOC_LOCALITA_COD IS NOT NULL THEN
    vSQL := vSQL||' AND LOC.LOC_LOCALITA_COD = :P_LOC_LOCALITA_COD ';
  ELSE
    vSQL := vSQL||' AND (1=1 OR :P_LOC_LOCALITA_COD IS NULL)';
  END IF;
  IF P_ISTAT_CITTA IS NOT NULL THEN
    vSQL := vSQL||' AND loc.COMB_ISTAT_COD = :P_ISTAT_CITTA ';
  ELSE
    vSQL := vSQL||' AND (1=1 OR :P_ISTAT_CITTA IS NULL)';
  END IF;
  IF P_PLAY_PLAYER_COD IS NOT NULL THEN
    vSQL := vSQL||' AND LOC.PLAY_PLAYER_COD = :P_PLAY_PLAYER_COD ';
  ELSE
    vSQL := vSQL||' AND (1=1 OR :P_PLAY_PLAYER_COD IS NULL)';
  END IF;
  IF P_LOC_DATA IS NOT NULL THEN
    vSQL := vSQL||' AND TO_TIMESTAMP (:P_LOC_DATA, ''DD/MM/YYYY HH24:MI:SS.FF3'') BETWEEN LOC.LOC_DATA_VER_INI AND LOC.LOC_DATA_VER_FIN ';
  ELSE
    vSQL := vSQL||' AND (1=1 OR :P_LOC_DATA IS NULL)';
  END IF; 

  OPEN refcur FOR vSQL USING P_LOC_LOCALITA_COD, P_ISTAT_CITTA, P_PLAY_PLAYER_COD, P_LOC_DATA;

Я добавил предложения ELSE, потому что для встроенного динамического SQL число переменных связывания в операторе должно быть фиксированным. Если вы используете пакет DBMS_SQL, вам не нужно этого делать.

Что касается избежания IF, вы можете сделать это:

vSQL := ' SELECT loc.RMV_REMI_VIRTUALE_COD, loc.LOC_DATA_VER_FIN FROM ENI_SAG_TSF_LOCALITA_DEF loc WHERE 1=1 ';
  || CASE WHEN P_LOC_LOCALITA_COD IS NOT NULL THEN
              ' AND LOC.LOC_LOCALITA_COD = :P_LOC_LOCALITA_COD '
          ELSE
              ' AND (1=1 OR :P_LOC_LOCALITA_COD IS NULL)'
          END
  || CASE WHEN P_ISTAT_CITTA IS NOT NULL THEN
              ' AND loc.COMB_ISTAT_COD = :P_ISTAT_CITTA '
          ELSE
              ' AND (1=1 OR :P_ISTAT_CITTA IS NULL)'
          END
... etc.

Очевидно, что теперь у вас есть CASE, но, по крайней мере, вы потеряете все vSQL := vSQL || битов.

Если вы добавляете много похожих условий, вы можете заключить логику в функцию, например:

vSQL := ' SELECT loc.RMV_REMI_VIRTUALE_COD, loc.LOC_DATA_VER_FIN FROM ENI_SAG_TSF_LOCALITA_DEF loc WHERE 1=1 ';
  || and_condition ('LOC.LOC_LOCALITA_COD', 'BV1', P_LOC_LOCALITA_COD)
  || and_condition ('loc.COMB_ISTAT_COD', 'BV2', P_ISTAT_CITTA)
  ... etc.

(Конечно, это не работает для условия МЕЖДУ).

2 голосов
/ 21 марта 2012

«Более читабельное» немного субъективно, но есть пара вариантов, если вам не нравятся блоки IF THEN:

 vSQL := ' SELECT loc.RMV_REMI_VIRTUALE_COD, loc.LOC_DATA_VER_FIN FROM ENI_SAG_TSF_LOCALITA_DEF loc WHERE 1=1 ';

 vSQL := vSQL||
         NVL2(P_LOC_LOCALITA_COD, 
             ' AND LOC.LOC_LOCALITA_COD = '''||P_LOC_LOCALITA_COD||''' ',
              NULL);

 vSQL := vSQL||
         NVL2(P_ISTAT_CITTA,
              ' AND loc.COMB_ISTAT_COD = '''||P_ISTAT_CITTA||''' ',
              NULL);

 vSQL := vSQL||
         NVL2(P_PLAY_PLAYER_COD, 
              ' AND LOC.PLAY_PLAYER_COD = '''||P_PLAY_PLAYER_COD||''' ',
              NULL);

 vSQL := vSQL||
         NVL2(P_LOC_DATA, 
              ' AND TO_TIMESTAMP ('''||P_LOC_DATA||''' , ''DD/MM/YYYY HH24:MI:SS.FF3'') BETWEEN LOC.LOC_DATA_VER_INI AND LOC.LOC_DATA_VER_FIN ',
              NULL);

или

  vSQL := ' SELECT loc.RMV_REMI_VIRTUALE_COD, loc.LOC_DATA_VER_FIN FROM ENI_SAG_TSF_LOCALITA_DEF loc WHERE 1=1 '
        ||NVL2(P_LOC_LOCALITA_COD, 
               ' AND LOC.LOC_LOCALITA_COD = '''||P_LOC_LOCALITA_COD||''' ',
               NULL)
        ||NVL2(P_ISTAT_CITTA,
               ' AND loc.COMB_ISTAT_COD = '''||P_ISTAT_CITTA||''' ',
               NULL)
        ||NVL2(P_PLAY_PLAYER_COD,
               ' AND LOC.PLAY_PLAYER_COD = '''||P_PLAY_PLAYER_COD||''' ',
               NULL)
        ||NVL2(P_LOC_DATA,
               ' AND TO_TIMESTAMP ('''||P_LOC_DATA||''' , ''DD/MM/YYYY HH24:MI:SS.FF3'') BETWEEN LOC.LOC_DATA_VER_INI AND LOC.LOC_DATA_VER_FIN ',
               NULL);

Надеюсьпомогает ...

0 голосов
/ 21 марта 2012

Читая ваш ответ, я нашел эту формулировку.Что вы думаете об этом?

SELECT loc.RMV_REMI_VIRTUALE_COD, loc.LOC_DATA_VER_FIN
  FROM ENI_SAG_TSF_LOCALITA_DEF loc
 WHERE 1 = 1
       AND (LOC.LOC_LOCALITA_COD = :P_LOC_LOCALITA_COD OR :P_LOC_LOCALITA_COD IS NULL)
       AND (loc.COMB_ISTAT_COD = :P_ISTAT_CITTA OR :P_ISTAT_CITTA IS NULL)
       AND (LOC.PLAY_PLAYER_COD = :P_PLAY_PLAYER_COD OR LOC.PLAY_PLAYER_COD IS NULL)
       AND (TO_TIMESTAMP (:P_LOC_DATA, 'DD/MM/YYYY HH24:MI:SS.FF3') BETWEEN LOC.LOC_DATA_VER_INI AND LOC.LOC_DATA_VER_FIN OR :P_LOC_DATA IS NULL)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...