在 where 子句中嵌套解码

Nested decode in where clause

我有一个游标,它根据最新的 activity 日期为传递的 ID 获取地址。

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;

某些 ID 的日期地址超过 1 个非典型代码。我的查询应该获取特定 atyp 代码 ('AB') 的地址,如果该 id 没有此 AB 类型的地址,那么我应该获取另一个 atype 代码的地址,如 'SP'.

我试图将我上面的游标结果过滤为解码语句,但是当我的 id 有超过 1 个非典型代码时失败了。

在 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' 部分,即使我的 ID 有 '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);

无需解码即可轻松完成您正在寻找的内容。我讨厌解码,它在当时很好,但对我来说以 9i 结束的时间太 unclear/complicated。用例代替。进一步的任务减少到以下。对于给定的 id return id return 活动 'AB' 地址类型。如果类型 AB 不存在 return 活动 'SP' 地址类型。如果都不存在 return 最低的 type_code 或者如果为空则 '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 12c 或更高版本,您可以使用 LIMIT 代替 rownum。

我最初想展示解码语句的完整分解,以显示出错的地方。但现在时间紧迫,也许以后再说。

--------更新------ decode( decode( ....
的评估 之前,我以显示初始解码语句的演变的指示结束了我的回答。好吧,我想现在是时候兑现这一点了。

但首先要对 Post 本身发表一些评论。
交流日期时使用 ISO 格式。 'yyyy-mm-dd hh24.mi.ss' 或只是 'yyyy-mm-dd' 如果日期是任何其他格式,请使用 to_date('.......','FORMAT') 指定格式。在这种情况下格式 = 'dd-mon-rr';

即使您发布了 table ddl 并插入了您的查询,但仍然不匹配 table。查询列不同于 table 列。下面将查询和实际的 table 列名称显示为 query_name ==> table_name:

  • atyp_code ==> type_code
  • to_date ==> 地址_to_date
  • from_date ==> 地址_from_date

确保查询中使用的列名与 table 列名 实际匹配。

那么我们如何着手解决查询中的问题。通常作为第一步,分解成易于识别和独立的组件。也许有些人可以查看此查询并确切了解发生了什么。然而,我并没有这种洞察力。那么如何将其分解为可管理的字节大小的片段。

decode 的结构是 decode(exp, compare, result [exp2,compare2,result2 ,....], default) 最终结果为 -- 结构1 如果 exp = compare then return 结果 elsif exp2 = compare2 then return result2 ... 否则 return 默认值;

 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 returns AB 或 null
Ri returns SP 或 null
现在我们可以看到整体声明的第一个问题:
如果 Ei returns AB 则不满足与 Null 的匹配 所以 Ri 被绕过了,因为没有更多的条件,它会尝试 return 默认值。然而,这不是默认值,因此它 return 为空。 (见上面的结构 2)。

如果 Ei returns null 确实匹配到 null(Oracle 中 Null 匹配 Null 的条件之一或可能是唯一的条件),则执行表达式 Ri。现在 Ri returns 要么 SP 要么 null。
在这一点上,内部解码的评估完成 returning SP 或 null。让我们称之为 Di(内部解码)。

现在外部解码开始评估,已减少到:

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

如果 Di returns SP 我们得到: SP 不匹配 null,因此绕过 Ro,因为没有进一步评估 return 默认值 'ar'。

如果 Di returns null 那么它确实匹配 null 所以评估 Ro 和 return 结果。 (注意:如果指定的 ID 有 2 个或更多 type_code 值 它们都不是 SP Ro 抛出异常 ORA-01427 单行子查询 returns 多于一行)。

现在我们正在修复初始语句:
由于 Ei 可以 return AB 但是这会使 'translated' 变为 null 因为没有默认值我们需要一个默认的 AB 来进行内部解码。
然后外部解码将所有非空 return 从 Di 转换为默认值 'ar',因此需要为 AB 和 SP 添加额外的 compare/return 值。 导致将初始声明修改为

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

现在您可以将原始查询重新替换回语句中。一定要记录好这一点。以后让初级开发人员来修改它是不行的。事实上,我什至不希望高级开发人员尝试修改它。最佳选择abandon decode,它不会与提议的完全不同的query 至少在favor 或case 表达式中。