我有一个有趣的问题,我想知道oracle是否有内置函数来执行此操作,或者我需要找到一种快速方法在plsql中执行此操作。
取2个字符串:
s1 = 'abc def hijk'
s2 = 'abc def iosk'
该函数需要返回abc def
,因为字符串在此之前完全相同。
另一个例子:
s1 = 'abc def hijk www'
s2 = 'abc def iosk www'
该函数需要返回abc def
。
我能想到的唯一方法就是循环遍历string1并将每个字符与substr()再次比较为字符串2的substr。
只是想知道Oracle是否内置了一些东西。表现非常重要。
答案 0 :(得分:1)
我怀疑有一些内置的SQL函数,但只能使用正则表达式在SQL中完成:
with cte1 as (
select 1 id, 'abc def hijk www' str from dual
union all
select 2 id, 'abc def iosk www' str from dual
), cte2 as (
SELECT distinct id, trim(regexp_substr(str, '[^ ]+', 1, level)) str
FROM cte1 t
CONNECT BY instr(str, ' ', 1, level - 1) > 0
)
select distinct t1.str
from cte2 t1
join cte2 t2 on (t1.str = t2.str and t1.id != t2.id)
我没有做过任何性能测试,但我的经验告诉我,这很可能比任何pl / sql解决方案都快,因为你完全避免了上下文切换。
答案 1 :(得分:1)
重新阅读你的问题之后,这就是你真正想要的:
with cte1 as (
select 1 id, 'abc def hijk www' str from dual
union all
select 2 id, 'abc def iosk www' str from dual
), num_gen as (
-- a number generator up to the minimum length of the strings
SELECT level num
FROM dual t
CONNECT BY level <= (select min(length(str)) from cte1)
), cte2 as (
-- build substrings of increasing length
select id, num_gen.num, substr(cte1.str, 1, num_gen.num) sub
from cte1
cross join num_gen
), cte3 as (
-- self join to check if the substrings are equal
select x1.num, x1.sub sub1, x2.sub sub2
from cte2 x1
join cte2 x2 on (x1.num = x2.num and x1.id != x2.id)
), cte4 as (
-- select maximum string length
select max(num) max_num
from cte3
where sub1 = sub2
)
-- finally, get the substring with the max length
select cte3.sub1
from cte3
join cte4 on (cte4.max_num = cte3.num)
where rownum = 1
基本上,这就是你在pl / sql中要做的事情:构建增加长度的子串,并在它们不再匹配的位置停止。
答案 2 :(得分:1)
您应该检查软件包UTL_MATCH以获得类似的功能,但是您必须自己编写自己的函数。
公共子字符串长度的二进制搜索为长字符串提供了良好的性能。
create or replace function ident_pfx(str1 varchar2, str2 varchar2) return varchar2
as
len_beg PLS_INTEGER;
len_end PLS_INTEGER;
len_mid PLS_INTEGER;
len_result PLS_INTEGER;
begin
if str1 is null or str2 is null then return null; end if;
--
len_result := 0;
len_beg := 0;
len_end := least(length(str1),length(str2));
LOOP
BEGIN
-- use binary search for the common substring length
len_mid := ceil((len_beg + len_end) / 2);
IF (substr(str1,1,len_mid) = substr(str2,1,len_mid))
THEN
len_beg := len_mid; len_result := len_mid;
ELSE
len_end := len_mid;
END IF;
END;
IF (len_end - len_beg) <= 1 THEN
-- check last character
IF (substr(str1,1,len_end) = substr(str2,1,len_end))
THEN
len_result := len_end;
END IF;
EXIT ;
END IF;
END LOOP;
return substr(str1,1,len_result);
end;
/
select ident_pfx('abc def hijk www','abc def iosk www') ident_pfx from dual;
abc def
答案 3 :(得分:0)
另一种可能的解决方案是使用XOR。 如果将两个字符串放在一起,结果应该有两个字符串匹配的NUL字节。
XOR不是本机运算符,但我很确定其中一个库中支持它。
答案 4 :(得分:0)
如果&#34;表现非常重要&#34;,你应该避免&#34;循环&#34;在子串上。
这是使用XOR的替代方案(由@EvilTeach提出)。
with string_transform as (
select 'abc def hijk www' str1, 'abc def iosk www' str2 from dual
),
str as (
select
str1, str2,
-- add suffix to handle nulls and identical strings
-- calculate XOR
utl_raw.bit_xor(utl_raw.cast_to_raw(str1||'X'),utl_raw.cast_to_raw(str2||'Y')) str1_xor_str2
from string_transform
), str2 as (
select
str1, str2,
str1_xor_str2,
-- replace all non-identical characters (not 00) with 2D = '-'
utl_raw.translate(str1_xor_str2,
utl_raw.translate(str1_xor_str2,'00','01'),
utl_raw.copies('2D',length(str1_xor_str2))) xor1
from str
), str3 as (
select
str1, str2,
-- replace all identical characters (00) with 2B (= '+') and cast back to string
utl_raw.cast_to_varchar2(utl_raw.translate(xor1,'00','2B')) diff
-- diff = ++++++++---+++++ (+ means identical position; - difference)
from str2
)
select str1, str2,
-- remove the appended suffix character
substr(diff,1,length(diff)-1) diff,
-- calculate the length of the identical prefix
instr(diff,'-')-1 same_prf_length
from str3
;
基本上两个字符串首先转换为RAW格式。 XOR将相同的字节(字符)设置为00.使用转换时,相同的字节将转换为&#39; +&#39;,所有其他字节转换为&#39; - &#39;。 相同的前缀长度是第一个&#39; - &#39;的位置。在字符串减1。 从技术上讲,一个(不同的)sufix字符被添加到两个字符串中以处理NULL和相同的字符串。
请注意,如果字符串长于2000,则必须添加一些额外的处理 由于UTL_RAW.CAST_TO_VARCHAR2的限制。