在 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 表达式中。
我有一个游标,它根据最新的 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 表达式中。