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

Sql 使用多个替换函数调用进行查询-如何提高性能?,sql,oracle,Sql,Oracle,我有一个查询,要求表中的字符串在相互比较之前去掉特殊字符。我创建了一个函数,该函数接收字符串并在返回字符串之前从字符串中删除某些特殊字符。问题是我发现自己多次使用该函数,因为查询进行了大量比较。这在添加功能后显著降低了性能 所以我创建了这个函数: create or replace FUNCTION F_REMOVE_SPECIAL_CHARACTERS ( IN_PARAM_EMAIL_NAME IN VARCHAR2, IN_PARAM_NUMBER_FLAG IN VARCHAR

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

所以我创建了这个函数:

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))
           )
      )
  )
)
实际的查询更大、更复杂,但关键是我需要从某些列值中删除特殊字符,这些列值在查询中会重复多次。这意味着我需要多次使用该函数,但这会导致性能显著降低


在查询中使用多个函数调用时,有人知道如何减少性能下降吗?谢谢。

如果您使用的是12c或更高版本,那么您可以使用

我记得这消除了查询,所以您应该执行得更好。

我从未测试过,但它很可能会更快,甚至是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))
           )
      )
  )
)

假设您需要将其作为一个函数(因为您在许多地方使用它),您可以将其清理并简化(并使其更高效),如下所示:


translate
(在第二个和第三个参数中)中使用“z”的愚蠢伎俩是由于Oracle对
null
的奇怪处理。在
translate
中,如果任何参数为
null
,则结果为
null
,这与Oracle在其他字符串操作中处理
null
不同。

向表中添加列并预计算所有剥离的字符串。然后可以对预先计算的值执行常规SQL操作。预先计算所有列的时间开销很大,但将来的查询速度会快得多。谢谢你的建议。我肯定会研究这个解决方案,这样我们就有了一个明确的问题陈述(与您目前的解决方案不同):给您一个(可能很长的)字符串(总是VARCHAR2还是CLOB?),一个要删除的字符列表:破折号、逗号、句点、下划线和空格,还有一个输入参数,告诉您是否也应该删除数字。对的如果是这样,就不需要编写函数,也不需要正则表达式。而且,在您当前的解决方案中,您可以在内部函数的
[…]
中包含空格,不需要额外的步骤。@mathguy,是的,这是正确的。该函数的所有输入均为varchar2。我创建了这个函数,因此它可以配置,因为我会多次剥离字符串。你有没有建议我可以做些什么来提高绩效?如果我不使用正则表达式,这会提高性能吗?我给出了完整的答案。对于您的最后一个问题,如果您避免使用regexp,那么性能很可能会得到提高,可能会显著提高,但这也取决于是什么导致查询速度变慢。(例如,如果SQL和PL/SQL之间的许多上下文切换是瓶颈,那么regexp和not就没那么重要了;在这种情况下,其他东西,如Oracle 12.1或更高版本中的
pragma udf
,将带来最大的改进)。谢谢。在with子句中嵌入函数确实比调用传统的PL/SQL函数快得多(尽管30-50倍可能是例外,而不是正常情况)。无论如何,Oracle 12.1还带来了在PL/SQL函数中声明的
pragma udf
,这实际上比在WITH子句中声明函数更快。
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;
/