0

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

对于基于日期的 na atyp 代码,某些 id 的地址超过 1 个。我的查询应该获取特定 atyp 代码('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);
4

1 回答 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。

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

-------- 更新 ------- 解码评估(解码(...
尊重这一点。

但首先要对帖子本身发表一些评论。
在交流日期时使用 ISO 格式。'yyyy-mm-dd hh24.mi.ss' 或只是 'yyyy-mm-dd' 如果日期是任何其他格式,用 to_date('........','FORMAT') 指定格式。在这种情况下 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) 最终结果为 -- Structure1 If exp = compare then return result elsif exp2 = compare2 then return 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,但由于没有默认值,因此我们需要将默认 AB 转换为内部解码。
然后外部解码将所有来自 Di 的非空返回转换为默认的 'ar',因此需要额外比较/返回 AB 和 SP 的值。导致将初始声明修改为

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

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

于 2020-03-07T23:56:41.187 回答