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 子句中的嵌套解码的主要内容,如果未能解决你的问题,请参考以下文章