我有一个功能可以将STR_A
的值与STR_B
进行比较,'dream'是STR_A
和STR_B
之间的任何匹配字符都将从{{STR_A
移除1}}。
例如; STR_A = 'LTD'
和STR_B = 'LIMITED'
因此结果为null
。
订单很重要,因此如果STR_A = 'LDT'
和STR_B = 'LIMITED'
结果为'T'
。
另一个例子; STR_A = 'AUSTIN'
和STR_B = 'ADVERTISING'
结果为'UT'
请注意; STR_A
中的字符数始终小于或等于STR_B
。
此外,字符只能使用一次,因此如果STR_A = 'LLTD'
和STR_B = 'LIMITED'
,结果将为'L'
。
我的功能如下;
create or replace FUNCTION CP_RDN_REMSTR(
S1 VARCHAR2,
S2 VARCHAR2)
RETURN VARCHAR2
IS
LEN INTEGER := NVL(LENGTH(S2),0);
OUTSTR VARCHAR2(32767) := S1;
POS INTEGER := 1;
IND INTEGER := POS;
BEGIN
FOR I IN 1..LEN
LOOP
POS := INSTR(SUBSTR(OUTSTR,POS),SUBSTR(S2,I,1));
IF POS > 0 THEN
OUTSTR := SUBSTR(OUTSTR,1,POS-IND)||SUBSTR(OUTSTR,POS+IND);
END IF;
END LOOP;
RETURN OUTSTR;
END;
然而,使用上述内容并没有给我预期的结果;
SELECT CP_RDN_REMSTR('LTD','LIMITED') AS STR_A FROM DUAL
UNION ALL
SELECT CP_RDN_REMSTR('LDT','LIMITED') AS STR_A FROM DUAL
UNION ALL
SELECT CP_RDN_REMSTR('AUSTIN','ADVERTISING') AS STR_A FROM DUAL
UNION ALL
SELECT CP_RDN_REMSTR('ADP','ADVANCED') AS STR_A FROM DUAL
结果如下;
('LTD','LIMITED') = NULL
('LDT','LIMITED') = 'D'
('AUSTIN','ADVERTISING') = NULL
('ADP','ADVANCED') = 'P'
预期结果是;
('LTD','LIMITED') = NULL
('LDT','LIMITED') = 'T'
('AUSTIN','ADVERTISING') = 'UT'
('ADP','ADVANCED') = 'P'
一如既往,非常感谢。
答案 0 :(得分:2)
这是一个使用递归因子子查询(递归WITH子句)的解决方案,自Oracle 11.2起可用。如果您的Oracle版本较旧,则可以使用PL / SQL代码攻击类似的内容。
我们检查LHS字符串中的字母,一次一个。我们使用REGEXP_LIKE(以及以NULL开头的递归模式)检查是否仍然可以找到匹配项。在每一步,如果找到匹配,我们扩展模式;如果未找到匹配项,我们将展开UNM字符串(“不匹配”)。
没有要求LHS短于RHS,它没有任何区别。该解决方案还在两侧正确处理NULL(我添加了数据以测试它)。
请注意在第一个因子子查询中创建的测试数据 - 它仅用于测试,它不是解决方案的一部分。
with
inputs ( lhs, rhs ) as (
select 'LTD' ,'LIMITED' from dual union all
select 'LDT' ,'LIMITED' from dual union all
select 'AUSTIN','ADVERTISING' from dual union all
select 'ADP' ,'ADVANCED' from dual union all
select 'ALPHA' , null from dual union all
select null ,'BETA' from dual
),
r ( lvl, lhs, rhs, unm, pattrn, next_letter ) as (
select 1, lhs, rhs, null, null, substr(lhs, 1, 1)
from inputs
union all
select lvl + 1, lhs, rhs,
unm || case when regexp_like(rhs, pattrn || '.*' || next_letter)
then null else next_letter end,
pattrn || case when regexp_like(rhs, pattrn || '.*' || next_letter)
then '.*' || next_letter end,
substr(lhs, lvl + 1, 1)
from r
where next_letter is not null
)
cycle lvl set cycle to 1 default 0
select lhs, rhs, unm
from r
where next_letter is null
;
输出(来自模拟输入):
LHS RHS UNM
------ ----------- -----
BETA
LTD LIMITED
LDT LIMITED T
ADP ADVANCED P
ALPHA ALPHA
AUSTIN ADVERTISING UT
注意:这是查看递归查询功能的一种方法。在解决方案的最后,我只是从r
中选择了我需要的列和行。相反,用
select *
from r
order by lhs, lvl
;
然后看看那个输出。
修改强>
有趣的问题。这是一个应该比第一个版本快一点的解决方案。它仅使用标准字符串函数(INSTR和SUBSTR),它们比正则表达式快得多。此外,从左到右工作,已经检查过的字母不需要继续进行(导致更长和更复杂的匹配模式);相反,可以一次只检查一个字母,并在每一步中从RHS字符串中删除初始段,使每次搜索更短。
with
inputs ( lhs, rhs ) as (
select 'LTD' ,'LIMITED' from dual union all
select 'LDT' ,'LIMITED' from dual union all
select 'AUSTIN','ADVERTISING' from dual union all
select 'ADP' ,'ADVANCED' from dual union all
select 'ALPHA' , null from dual union all
select null ,'BETA' from dual
),
r ( lhs, rhs, next_letter, unm, new_lhs, new_rhs ) as (
select lhs, rhs, substr(lhs, 1, 1), null, lhs, rhs
from inputs
union all
select lhs, rhs, substr(new_lhs, 2, 1),
unm || case when nvl(instr(new_rhs, next_letter), 0) = 0
then next_letter end,
substr(new_lhs, 2),
substr(new_rhs, nvl(instr(new_rhs, next_letter), 0) + 1)
from r
where next_letter is not null
)
cycle new_lhs set cycle to 1 default 0
select lhs, rhs, unm
from r
where next_letter is null
;
答案 1 :(得分:1)
根据您对订单重要性的评论,听起来您想要遍历S2
中的每个字符,只有S1
才会将其从S1
移除,如果它是CREATE OR REPLACE FUNCTION CP_RDN_REMSTR (S1 VARCHAR2, S2 VARCHAR2) RETURN VARCHAR2
IS
LEN INTEGER := NVL (LENGTH (S2), 0);
OUTSTR VARCHAR2 (32767) := S1;
POS INTEGER := 1;
IND INTEGER := POS;
BEGIN
FOR I IN 1 .. LEN
LOOP
POS := 0;
POS := INSTR (SUBSTR (OUTSTR, POS), SUBSTR (S2, I, 1));
IF POS = 1 THEN
OUTSTR := SUBSTR (OUTSTR, 1, POS - IND) || SUBSTR (OUTSTR, POS + IND);
END IF;
END LOOP;
RETURN OUTSTR;
END CP_RDN_REMSTR;
的主要字符。
以下内容满足您的第一,第二和第四个示例,但导致第三个示例没有意义:
('LTD','LIMITED') = NULL
('LDT','LIMITED') = 'T'
('AUSTIN','ADVERTISING') = 'USTIN'
('ADP','ADVANCED') = 'P'
输出:
CREATE OR REPLACE FUNCTION checkUser(pUserName VARCHAR2, pCountry VARCHAR2)
RETURN NUMBER
IS
vUsername VARCHAR2(100);
vCountry VARCHAR2(100);
BEGIN
SELECT Person.Username INTO vUsername
FROM Person
WHERE Person.Username = pUserName;
EXCEPTION WHEN NO_DATA_FOUND THEN
vUsername := NULL;
SELECT Person.Country INTO vCountry
FROM Person
WHERE Person.Country = pCountry;
EXCEPTION WHEN NO_DATA_FOUND THEN
vCountry := NULL;
IF vUsername IS NULL OR vCountry IS NULL THEN
RETURN False;
ELSE
RETURN True;
END IF;
END checkUser;