where 子句中的嵌套解码

Posted

技术标签:

【中文标题】where 子句中的嵌套解码【英文标题】:Nested decode in where clause 【发布时间】:2020-03-06 00:49:08 【问题描述】:

我有一个游标,它根据最新的活动日期获取传递的 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;

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

我试图将上面的光标结果过滤为解码语句,但是当我的 id 有超过 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;

即使我的 id 有 'SP' 类型的记录,我的查询总是转到 'ar' 部分

样本数据:

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);

【问题讨论】:

你能告诉我们预期的结果是什么吗? @Mihawk :编辑了预期的结果。欢迎评论 是不是decode的顺序不对? 我稍后会检查我认为我的评论不正确 请重新发布您的示例数据以对应查询中提到的列和值。您的数据包含 activity_date,但不包含 from_date 或 to_date。查询引用列Status_Ind,但也在样本数据中的NOT;您的查询硬编码 id 为“1330056”,但该值不在示例数据中。最后,您的查询按结构化返回 1(派生)列。所以它不能产生预期的输出。查询需要匹配问题,这个不需要。 【参考方案1】:

您正在寻找的东西可以更容易地完成而无需解码。我讨厌解码,这对它的时代来说是件好事,但对我来说,它以 9i 结束,这太不清楚/太复杂了。改为使用案例。进一步的任务减少到以下。对于给定的 id,返回 id 返回活动的“AB”地址类型。如果类型 AB 不存在,则返回活动的“SP”地址类型。如果两者都不存在,则返回最低的 type_code,或者如果为 null,则返回 '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 本身的几个 cmets。 交流日期时使用 ISO 格式。 'yyyy-mm-dd hh24.mi.ss' 或只是 'yyyy-mm-dd' 如果日期是任何其他格式,请使用 to_date('........','FORMAT') 指定格式。在这种情况下,格式 = 'dd-mon-rr';

即使在您发布表 ddl 并插入您的查询 DID NOT 匹配表之后。查询列与表列不同。下面将查询和实际表列名称显示为 query_name ==> table_name:

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

确保查询中使用的列名实际上与表列名匹配。 那么我们如何解决查询中的问题。通常作为第一步分解成易于识别和独立的组件。也许有些人可以查看此查询并确切了解正在发生的事情。然而,我没有这样的洞察力。那么如何将其分解为可管理的字节大小片段。 decode 的结构是 decode(exp, compare, result [,exp2,compare2,result2 ,....], default) 最终结果为 -- 结构1 如果 exp = compare 则返回结果 elsif exp2 = compare2 然后返回 result2 ... 否则返回默认值;

 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 或 null Ri 返回 SP 或 null 现在我们可以看到整体陈述的第一个问题: 如果 Ei 返回 AB 则不满足与 Null 的匹配 所以 Ri 被绕过了,因为没有更多的条件,它试图返回默认值。但是不是默认值所以它返回null。 (参见上面的结构 2)。

如果 Ei 返回与 null 匹配的 null(Oracle 中 Null 匹配 Null 的条件之一或可能唯一的条件),则执行表达式 Ri。现在 Ri 返回 SP 或 null。 此时,内部解码的评估完成,返回 SP 或 null。让我们称之为 Di(内部解码)。 现在外部解码开始评估已简化为:

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

如果 Di 返回 SP,我们得到: SP 不匹配 null 所以绕过 Ro 并且因为没有进一步的评估返回默认的 'ar'。 如果 Di 返回 null 那么它确实匹配 null 所以评估 Ro 并返回结果。 (注意:如果指定的 ID 有 2 个或多个 type_code 值 它们都不是 SP Ro 抛出异常 ORA-01427 单行子查询返回多于一行)。 现在我们在 postilion 中修复初始语句: 由于 Ei 可以返回 AB,但由于没有默认值,因此会被“转换”为 null,因此我们需要将默认 AB 用于内部解码。 然后外部解码将来自 Di 的所有非空返回转换为默认的 'ar',因此需要额外比较/返回 AB 和 SP 的值。 导致将初始语句修改为

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

现在您可以将原始查询重新替换回语句中。请务必记录好这一点。让初级开发人员稍后出现并且必须对其进行修改是不行的。事实上,我什至不希望高级开发人员尝试修改它。最佳选项放弃解码,它与至少在赞成或案例表达中提出的完全不同的查询不同。

【讨论】:

我使用的是 order by 而不是 decode 谢谢,它成功了!!

以上是关于where 子句中的嵌套解码的主要内容,如果未能解决你的问题,请参考以下文章

在 where 子句中使用解码,并带有“in”

在 Haskell 中使用嵌套的 `where` 子句

PostgreSQL unnest,嵌套字段特定元素的 WHERE 子句

Rails:使用 where 子句查找深度嵌套的关联

sequelize 嵌套包含 where 子句

在嵌套结构中使用 where 子句