Вложенное декодирование в предложении where - PullRequest
0 голосов
/ 06 марта 2020

У меня есть курсор, который выбирает адрес для переданного идентификатора, основываясь на последней дате активности.

CURSOR get_address_upd_c (
id t1.id%TYPE
) IS

  SELECT   street_line1,
           street_line2,
           city,
           stat_code,
           zip,
           activity_date,
           atyp_code
    FROM   addr
   WHERE       addr_im = '1'
           AND status_ind IS NULL
           AND from_date < SYSDATE
           AND DECODE (TO_DATE, NULL, SYSDATE, TO_DATE) >= SYSDATE
ORDER BY   activity_date DESC;

Существуют определенные идентификаторы, которые имеют более 1 адреса для даты на основе кода atyp. Мой запрос должен получить адрес для определенного кода atyp ('AB'), если этот идентификатор не имеет адреса этого типа AB, тогда я должен получить адрес для другого кода atype, такого как 'SP'.

Я пытался отфильтровать мой вышеупомянутый результат курсора как операторы декодирования, но потерпеть неудачу, когда у моего идентификатора есть больше чем 1 код atyp.

Попытка ниже в предложении IN

SELECT   DECODE (
             DECODE (
                 (SELECT   atyp_code
                    FROM   addr a2
                   WHERE   a2.id = '1' AND a2.status_ind IS NULL
                           AND (TRUNC (SYSDATE) BETWEEN TRUNC(NVL (
                                                                  a2.from_date,
                                                                  NVL (
                                                                      a2.TO_DATE,
                                                                      SYSDATE)))
                                                    AND  TRUNC(NVL (
                                                                   a2.TO_DATE,
                                                                   SYSDATE)))
                           AND a2.atyp_code = 'AB'),
                 NULL,
                 (SELECT   atyp_code
                    FROM   addr a2
                   WHERE   a2.id = '1' AND a2.status_ind IS NULL
                           AND (TRUNC (SYSDATE) BETWEEN TRUNC(NVL (
                                                                  a2.addr_from_date,
                                                                  NVL (
                                                                      a2.TO_DATE,
                                                                      SYSDATE)))
                                                    AND  TRUNC(NVL (
                                                                   a2.TO_DATE,
                                                                   SYSDATE)))
                           AND a2.atyp_code = 'SP'),
                 'AB'),
             NULL,
             (SELECT   atyp_code
                FROM   addr a2
               WHERE   a2.id = '1' AND a2.status_ind IS NULL
                       AND (TRUNC (SYSDATE) BETWEEN TRUNC(NVL (
                                                              a2.from_date,
                                                              NVL (
                                                                  a2.TO_DATE,
                                                                  SYSDATE)))
                                                AND  TRUNC(NVL (a2.TO_DATE,
                                                                SYSDATE)))),
             'ar')
             AS t
  FROM   DUAL;

Мой запрос всегда идет к 'ar 'part, хотя у моего идентификатора есть записи типа' SP '

Пример данных:

ID  Street_1    City    State   Type_Code   Activity_Date
1   aaa         sds      MI      SM         23-Dec-19
1   bb          wew      TN      IN         23-Dec-19
1   ccc         fcvc     AR      SP         23-Dec-19
1   dd          ewe      NY      SL         23-Dec-19
2   eee         fff      TX      AB          5-Jan-20
3   gg          kkk      TX      SM         19-Sep-18

O / p должно быть таким же, как показано ниже, на основе значений кода типа.

ID  Street_1    City    State   Type_Code   Activity_Date
1   ccc         fcvc     AR      SP         23-Dec-19
2   eee         fff      TX      AB          5-Jan-20
3   gg          kkk      TX      SM         19-Sep-18

Создание и вставка сценариев для примера выше

CREATE TABLE addr (
    id              NUMBER(8, 0),
    street_1        VARCHAR2(100),
    city            VARCHAR2(100),
    state           VARCHAR2(5),
    type_code       VARCHAR2(10),
    activity_date   DATE,
    status_ind      VARCHAR2(5),
    addr_from_date  DATE,
    addr_to_date    DATE
);

INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(1,'aaa','sds','MI','SM','23-DEC-19',NULL,'01-SEP-15',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(1,'bb','wew','TN','IN','23-DEC-19',NULL,'01-JUN-18',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(1,'ccc','fcvc','AR','SP','23-DEC-19',NULL,'01-SEP-15',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(1,'dd','ewe','NY','SL','23-DEC-19',NULL,'01-SEP-18',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(2,'ee','fff','TX','AB','05-JAN-20',NULL,'01-MAY-17',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(3,'gg','kkk','TX','SM','19-SEP-18',NULL,'23-JUL-15',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(4,'aaa','sds','MI','PA','03-NOV-19',NULL,'01-MAR-15',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(4,'lll','mno','LA','PB','03-NOV-19',NULL,'01-SEP-15',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(4,'jjj','pqr','LA','SP','03-NOV-19',NULL,'01-SEP-15',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(5,'mmm','dee','NY','SM','03-MAR-19',NULL,'10-SEP-15',NULL);
 INSERT INTO addr (id,street_1,city,state,type_code,activity_date,status_ind,addr_from_date,addr_to_date) values(5,'ppp','aru','TX','SP','03-DEC-17',NULL,'01-SEP-15',NULL);

1 Ответ

1 голос
/ 08 марта 2020

То, что вы ищете, может быть выполнено намного проще без декодирования вообще. Я ненавижу декодирование, это было хорошо для своего времени, но это время закончилось 9i для меня, это просто слишком непонятно / сложно. Вместо этого используйте вариант. Далее задача сводится к следующему. Для данного идентификатора возвращаемый идентификатор возвращает активный тип адреса «AB». Если тип AB не существует, возвращает активный тип адреса 'SP'. Если ни один из них не существует, верните самый низкий тип_кода или, если ноль, тогда 'ar' Это можно сделать с помощью простого оператора case и поиграть с order by.

select nvl(type_code,'ar') type_cocde
  from (select type_code
          from addr  
         where id = &ID
           and status_ind is null 
           and trunc (sysdate) between trunc(coalesce(addr_from_date, sysdate))
                                   and trunc(coalesce(addr_to_date, sysdate))

         order by case when type_code = 'AB' then 1
                       when type_code = 'SP' then 2
                       else 3
                  end 
             , type_code
        ) tc
  where rownum<=1;

Примечание: если у вас Oracle 12 c или выше, вы можете использовать LIMIT вместо rownum.

Я изначально думал показать полную разбивку декодирования заявление, чтобы показать, где это пошло не так. Но сейчас нужно время, возможно, позже. -------- Обновление ------- Оценка декодирования (декодирование (.... Ранее я закончил свой ответ указанием показа эволюции исходного оператора декодирования. Ну, я думаю, что сейчас время почтить это. Но сначала пара комментирует сам пост. При сообщении дат используйте формат ISO. 'yyyy-mm-dd hh24.mi.ss' или просто 'yyyy-mm-dd' Если даты в любом другом формате, укажите формат с помощью to_date ('........', 'FORMAT'). В этом случае format = 'dd-mon-rr';

Даже после того, как вы разместили таблицу ddl и вставили свой запрос, НЕ совпали с таблицей. Столбцы запроса отличаются от столбцов таблицы. Ниже показаны запросы и фактические имена столбцов таблицы в виде query_name ==> table_name:

  • atyp_code ==> type_code
  • to_date ==> addr_to_date
  • from_date ==> addr_from_date

Убедитесь, что имена столбцов, используемые в запросах, действительно соответствуют именам столбцов таблицы . Так как же нам go решить проблемы в запросе? Ну часто в качестве первого шага разбиваются на легко идентифицируемые и независимые компоненты. Возможно, некоторые могут взглянуть на этот запрос и увидеть, что именно происходит. Я, однако, не одарен таким пониманием. Итак, как разбить это на управляемые куски размера байта. Структура декодирования - это декодирование (exp, сравнение, результат [, exp2, сравнение2, результат2, ....], по умолчанию), которое определяется как - Structure1. Если exp = сравнивать, тогда возвращаем результат: elsif exp2 = сравните2, затем возвращаем результат2 .. . иначе вернуть значение по умолчанию;

 Or if no default is specified
   -- Structure2
   If exp = compare then return result
   elsif exp2 = compare2 then return result2
   ...
   else return null;

Начиная с внутреннего декодера, разбейте его следующим образом: Пусть Ei будет утверждением

  select type_code
    from addr a2
   where a2.id = &ID and a2.status_ind is null
     and (trunc (sysdate) between trunc(nvl (a2.addr_from_date,
                                             nvl (a2.addr_to_date,
                                                  sysdate)))
                              and trunc(nvl (a2.addr_to_date,
                                            sysdate)))
     and a2.type_code = 'AB'

Пусть Ri будет утверждением

  select type_code
    from addr a2
   where a2.id = &ID and a2.status_ind is null
     and (trunc (sysdate) between trunc(nvl (a2.addr_from_date,
                                             nvl (a2.addr_to_date,
                                                  sysdate)))
                              and trunc(nvl (a2.addr_to_date,
                                            sysdate)))
     and a2.type_code = 'SP';

И внешним Пусть Ro будет оператором

  select type_code
    from addr a2
   where a2.id = &ID and a2.status_ind is null
     and (trunc (sysdate) between trunc(nvl (a2.addr_from_date ,
                                             nvl (a2.addr_to_date ,
                                                  sysdate)))
                               and trunc(nvl (a2.addr_to_date ,
                                               sysdate)))

Теперь мы можем подставить их в исходное выражение, получив выражение

decode ( decode (Ei,null,Ri)  ,null,Ro,'ar') )

Запустив каждое из вышеприведенных выражений, и вручную оценим общее выражение декодирования в Посмотрите, где это идет не так. Но даже до этого на ум приходит небольшой дополнительный анализ. Глядя на запросы Ei и Ri, вы можете сказать, что этот запрос имеет только 2 возможных результата: Ei возвращает AB или ноль Ri возвращает SP или ноль Теперь мы можем увидеть первую проблему с общим утверждением: Если Ei возвращает AB, то совпадение с Null не выполняется, поэтому Ri игнорируется, и, поскольку больше нет условий, он пытается вернуть значение по умолчанию. Однако это не по умолчанию, поэтому он возвращает ноль. (см. Структура2 выше).

Если Ei возвращает нулевое значение, которое соответствует нулевому (одно из или, возможно, единственное условие в Oracle, где нулевое значение соответствует нулевому), то выполняется выражение Ri. Теперь Ri возвращает либо SP, либо ноль. В этот момент оценка внутреннего декодирования завершается возвращением либо SP, либо null. Давайте назовем это Di (Decode inner). Теперь внешнее декодирование начинает оценку, уменьшив ее до:

   decode(Di, null, Ro, 'ar');

Если Di возвращает SP, мы получаем: SP не соответствует null, поэтому обходим Ro и, так как никакие дальнейшие вычисления не возвращают значение по умолчанию «ar».
Если Di возвращает ноль, то это соответствует нулю, поэтому оцените Ro и верните результат. (Примечание: если указанный идентификатор имеет 2 или более значений type_code, ни одно из которых не является SP, исключение Ro выдает исключение ORA-01427, однострочный подзапрос возвращает более одной строки). Теперь мы в починке исправим начальное утверждение: Так как Ei может возвращать AB, но он «переводится» в ноль, поскольку значения по умолчанию не существует, нам нужно использовать AB по умолчанию для внутреннего декодирования. Затем внешний декодер переводит все ненулевые возвраты из Di в значение по умолчанию 'ar', поэтому необходимо дополнительно сравнить / вернуть значения для AB и SP. В результате изменив исходный оператор на

decode (decode (Ei,null,Ri, 'AB'),null,Ro,'SP','SP','AB','AB,'ar')

Теперь вы можете заново подставить исходные запросы обратно в оператор. Обязательно документируйте это хорошо. Было бы не так, чтобы младший разработчик пришел позже и должен был его изменить. На самом деле я бы даже не хотел, чтобы старший разработчик пытался его изменить. Лучший вариант отказаться от декодирования , это не совсем другой запрос, как предложено, по крайней мере, в пользу выражения или регистра.

...