Postgres 在查询中使用函数

Postgres using functions inside queries

我有一个 table,其中包含用于匹配品牌的常用词值 - 所以当有人输入 "coke" 时,我想匹配与其关联的任何可能的品牌名称以及原始术语。

CREATE TABLE word_association ( commonterm TEXT, assocterm TEXT);

INSERT INTO word_association ('coke', 'coca-cola'), ('coke', 'cocacola'), ('coke', 'coca-cola');

我有一个函数可以在用于模式匹配的管道分隔符字符串中创建这些值的列表:

CREATE OR REPLACE FUNCTION usp_get_search_terms(userterm text)
  RETURNS text AS
$BODY$DECLARE
    returnstr TEXT DEFAULT '';

BEGIN
    SET DATESTYLE TO DMY;

    returnstr := userterm;

    IF EXISTS (SELECT 1 FROM word_association WHERE LOWER(commonterm) = LOWER(userterm)) THEN
        SELECT  returnstr || '|' || string_agg(assocterm, '|') INTO returnstr
        FROM    word_association
        WHERE   commonterm = userterm;

    END IF;

    RETURN returnstr;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION usp_get_search_terms(text)
  OWNER TO customer_role;

如果你调用 SELECT * FROM usp_get_search_terms('coke') 你最终会得到

coke|coca-cola|cocacola|coca cola

编辑:这个函数 运行s <100ms 所以它工作正常。

我想 运行 插入此文本的查询,例如

SELECT X.article_number, X.online_description
FROM articles X
WHERE LOWER(X.online_description) % usp_get_search_terms ('coke');

相对于我的 table 约 50 万条记录,这需要大约 56 秒才能 运行。

如果我获取原始文本并在查询中使用它,则需要大约 300 毫秒,例如

SELECT X.article_number, X.online_description
FROM articles X
WHERE X.online_description % '(coke|coca-cola|cocacola|coca cola)';

结果集相同。

我试过将函数的输出字符串修改为例如将其括在引号和括号中,但似乎没有什么不同。

有人能告诉我为什么这里有区别吗?是数据类型还是有关在查询中调用函数的内容?谢谢。

您是否尝试过删除 IF EXISTS() 并简单地使用:

SELECT  returnstr || '|' || string_agg(assocterm, '|') INTO returnstr
FROM    word_association
WHERE   LOWER(commonterm) = LOWER(userterm)

您的函数可能需要 100 毫秒,但它不会调用您的函数一次;它调用了 500,000 次。

这是因为您的函数已声明 VOLATILE。这告诉 Postgres 在查询中多次调用函数 returns 不同的值(如 clock_timestamp()random()),或者它以某种方式改变数据库的状态(例如例如,通过插入记录)。

如果你的函数只包含 SELECTs,没有 INSERTs,调用其他 VOLATILE 函数,或其他副作用,那么你可以声明它 STABLE 代替。这告诉规划器它可以只调用一次函数并重用结果而不影响查询结果。

但是由于 SET DATESTYLE 语句,您的函数 有副作用,它会在会话的其余部分生效。然而,我怀疑这是故意的。您可以删除它,因为它看起来不像日期格式与其中的任何内容相关。但如果有必要,正确的做法是使用 SET clause of the CREATE FUNCTION statement 仅在函数调用期间更改它:

...
$BODY$
  LANGUAGE plpgsql STABLE
  SET DATESTYLE TO DMY
  COST 100;

查询的慢版本的另一个问题是对 LOWER(X.online_description) 的调用,这将阻止查询利用索引(因为 online_description 已建立索引,但 LOWER(online_description)不是)。

通过这些更改,两个查询的性能相同;看到这个 SQLFiddle.

不是为每一行调用一次函数,而是调用一次:

select x.article_number, x.online_description
from
    woolworths.articles x
    cross join
    woolworths.usp_get_search_terms ('coke') c (s)
where lower(x.online_description) % s

所以我今天早上黎明时得到了答案 - CTE 来拯救!

特别是因为这是一个非常大的查询的 "simple" 版本,它有助于单独定义一次,然后对其进行匹配。另一种方法(假设我是从 NodeJS 平台调用它的)是让一个请求检索术语字符串,然后发出另一个请求以将字符串传回。不优雅。

WITH matches AS
    (   SELECT * FROM usp_get_search_terms('coke')  )
, main AS 
    (   SELECT X.article_number, X.online_description
        FROM articles X
        JOIN matches M ON X.online_description % M.usp_get_search_terms )
SELECT * FROM main

执行时间约为 300-500 毫秒,具体取决于搜索的字词和返回的文章。

感谢您提供的所有信息 - 我已经了解了一些关于 PostGres 的知识,我的 MS-SQL 背景不一定让我做好准备:)