使用多个替换函数调用进行查询-如何提高性能?

时间:2019-04-01 15:01:30

标签: sql oracle

我有一个查询,要求将表中的字符串除去特殊字符,然后才能将它们相互比较。我创建了一个函数,该函数接受一个字符串并在返回之前从该字符串中删除某些特殊字符。问题是由于查询进行了大量比较,我发现自己多次使用该函数。添加功能后,这会大大降低性能。

所以我有创建的这个功能:

create or replace FUNCTION F_REMOVE_SPECIAL_CHARACTERS 
(
  IN_PARAM_EMAIL_NAME IN VARCHAR2,
  IN_PARAM_NUMBER_FLAG IN VARCHAR2 DEFAULT 'N'
) RETURN VARCHAR2 AS 
BEGIN
  /* If flag is Y then remove all numbers too. Otherwise, keep numbers in the string */
  IF IN_PARAM_NUMBER_FLAG = 'Y' THEN
    RETURN replace(regexp_replace(IN_PARAM_EMAIL_NAME, '[-,._0-9]', ''), ' ', '');
  ELSE
    RETURN replace(regexp_replace(IN_PARAM_EMAIL_NAME, '[-,._]', ''), ' ', '');
  END IF;
END F_REMOVE_SPECIAL_CHARACTERS;

我也有这样的查询:

SELECT a.ID, LISTAGG(b.BUSINESS_EMAIL) WITHIN GROUP (ORDER BY a.ID)
FROM tableA a, tableB b
WHERE UPPER(F_REMOVE_SPECIAL_CHARACTERS(b.LAST_NAME)) IN (
  (SELECT UPPER(F_REMOVE_SPECIAL_CHARACTERS(a.NICK_NAME)) FROM tableC c
      WHERE UPPER(F_REMOVE_SPECIAL_CHARACTERS(a.NICK_NAME)) IN (
        (SELECT UPPER(F_REMOVE_SPECIAL_CHARACTERS(c.NAME)) FROM tableC c
           WHERE UPPER(F_REMOVE_SPECIAL_CHARACTERS(a.NICK_NAME)) = UPPER(F_REMOVE_SPECIAL_CHARACTERS(a.LAST_NAME))
           )
      )
  )
)

实际查询更大,更复杂,但要点是,我需要从某些列值中删除特殊字符,而这些字符恰巧在查询中重复多次。这意味着我需要多次使用该函数,但这会导致性能显着下降。

有人对在查询中使用多个函数调用时如何降低性能下降有想法吗?谢谢。

2 个答案:

答案 0 :(得分:3)

假设您需要将此功能用作函数(因为您在许多地方都使用了它),则可以像下面这样清理并简化它(并使它更有效):

create or replace function f_remove_special_characters 
(
  in_param_email_name in varchar2,
  in_param_number_flag in varchar2 default 'N'
) 
return varchar2
deterministic
as 
pragma udf;   -- if on Oracle 12.1 or higher, and function is only for SQL use
  /* If flag is Y then remove all numbers too. 
     Otherwise, keep numbers in the string
  */
  chars_to_remove varchar2(16) := 'z-,._ ' || 
                         case in_param_number_flag when 'Y' then '0123456789' end;
begin
  return translate(in_param_email_name, chars_to_remove, 'z');
end f_remove_special_characters;
/

translate(在第二个和第三个参数中)带有'z'的愚蠢把戏是由于Oracle对null的奇怪处理。在translate中,如果任何参数为null,则结果为null,这与Oracle在其他字符串操作中对null的对待相反。

答案 1 :(得分:2)

如果您的年龄在12c或以上,则可以使用WITH FUNCTION clause

作为快速解决方案 我记得这消除了PL/SQL<->SQL Context switches,因此您的查询应具有更好的性能。

我从未测试过,但是很有可能甚至会快30到50倍。 让我知道它将有多快,因为我很好奇

WITH FUNCTION F_REMOVE_SPECIAL_CHARACTERS 
(
  IN_PARAM_EMAIL_NAME IN VARCHAR2,
  IN_PARAM_NUMBER_FLAG IN VARCHAR2 DEFAULT 'N'
) RETURN VARCHAR2 AS 
BEGIN
  /* If flag is Y then remove all numbers too. Otherwise, keep numbers in the string */
  IF IN_PARAM_NUMBER_FLAG = 'Y' THEN
    RETURN replace(regexp_replace(IN_PARAM_EMAIL_NAME, '[-,._0-9]', ''), ' ', '');
  ELSE
    RETURN replace(regexp_replace(IN_PARAM_EMAIL_NAME, '[-,._]', ''), ' ', '');
  END IF;
END;
SELECT a.ID, LISTAGG(b.BUSINESS_EMAIL) WITHIN GROUP (ORDER BY a.ID)
FROM tableA a, tableB b
WHERE UPPER(F_REMOVE_SPECIAL_CHARACTERS(b.LAST_NAME)) IN (
  (SELECT UPPER(F_REMOVE_SPECIAL_CHARACTERS(a.NICK_NAME)) FROM tableC c
      WHERE UPPER(F_REMOVE_SPECIAL_CHARACTERS(a.NICK_NAME)) IN (
        (SELECT UPPER(F_REMOVE_SPECIAL_CHARACTERS(c.NAME)) FROM tableC c
           WHERE UPPER(F_REMOVE_SPECIAL_CHARACTERS(a.NICK_NAME)) = UPPER(F_REMOVE_SPECIAL_CHARACTERS(a.LAST_NAME))
           )
      )
  )
)