我有一张常用字词值表可以与品牌匹配 - 所以当有人输入"焦炭"我希望匹配与之相关的任何可能的品牌名称以及原始术语。
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|coca-cola|cocacola|coca cola
编辑:此函数运行<100毫秒,因此工作正常。
我想运行一个插入此文本的查询,例如
SELECT X.article_number, X.online_description
FROM articles X
WHERE LOWER(X.online_description) % usp_get_search_terms ('coke');
这需要大约56秒来运行我的~500K记录表。
如果我得到原始文本并在查询中使用它需要约300毫秒,例如
SELECT X.article_number, X.online_description
FROM articles X
WHERE X.online_description % '(coke|coca-cola|cocacola|coca cola)';
结果集完全相同。
我已经尝试修改函数的输出字符串到例如将它括在引号和括号中,但它似乎没有什么区别。
有人可以告诉我这里有什么区别吗?它是关于在查询中调用函数的数据类型吗?感谢。
答案 0 :(得分:4)
您的功能可能需要100毫秒,但它不会调用您的功能一次;它叫它500,000次。
这是因为你的函数被声明为VOLATILE
。这告诉Postgres在查询中多次调用函数时返回不同的值(如clock_timestamp()
或random()
),或者它以某种方式改变数据库的状态(例如,通过插入记录)。
如果您的功能仅包含SELECT
s,没有INSERT
s,调用其他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。
答案 1 :(得分:1)
所以今天早上我的答案就出现了 - CTE救援!
特别是因为这是一个非常大的查询的“简单”版本,它有助于将此定义一次隔离,然后对其进行匹配。替代方案(假设我从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背景并不一定能帮助我:)
答案 2 :(得分:0)
您是否尝试删除IF EXISTS()并使用:
SELECT returnstr || '|' || string_agg(assocterm, '|') INTO returnstr
FROM word_association
WHERE LOWER(commonterm) = LOWER(userterm)
答案 3 :(得分:0)
而不是为每一行调用函数只调用一次:
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