PostgreSQL 函数不适用于 WHERE 子句
Posted
技术标签:
【中文标题】PostgreSQL 函数不适用于 WHERE 子句【英文标题】:PostgreSQL Function not working with WHERE clause 【发布时间】:2020-04-20 16:18:28 【问题描述】:我正在尝试在 Postgres 中创建一个函数,以使我的查询比 Django ORM 更快。但是我面临的问题是,当查询中没有WHERE
子句时,结果就会出现。
这是产生 0 行的函数及其调用:
CREATE OR REPLACE FUNCTION public.standard_search(search_term text, similarity_to integer)
RETURNS TABLE(obj_id integer, app_num integer, app_for text, similarity integer)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
similarity integer;
BEGIN
RETURN QUERY
SELECT mark.id, mark.app_num, mark.app_for::text, levenshtein($1, focusword.word) AS similarity
FROM mark
INNER JOIN focusword ON (mark.id = focusword.mark_id)
WHERE similarity <= $2
ORDER BY similarity, mark.app_for, mark.app_num;
END
$BODY$;
select * from public.standard_search('millennium', 4)
这是给我结果的函数及其调用,但由于在函数调用中完成过滤,所以速度很慢:
CREATE OR REPLACE FUNCTION public.standard_search(search_term text, similarity_to integer)
RETURNS TABLE(obj_id integer, app_num integer, app_for text, similarity integer)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
similarity integer;
BEGIN
RETURN QUERY
SELECT mark.id, mark.app_num, mark.app_for::text, levenshtein($1, focusword.word) AS similarity
FROM mark
INNER JOIN focusword ON (mark.id = focusword.trademark_id)
ORDER BY similarity, trad.app_for, mark.app_num;
END
$BODY$;
select * from public.standard_search('millennium', 4) where similarity <= 4
谁能解释一下这里到底出了什么问题?在此之后,我可以进行性能改进。
我无法通过 VIEWS 执行此操作,因为它至少需要一个参数,即 search_term
才能传递给 levenshtein()
函数。
我还面临将tuple
作为函数中的参数传递的问题,该函数将再次在 where 子句中使用,例如:
WHERE mark.class in (1,2,3,4,5)
我之前是通过 Django ORM 的 RawSQL 功能执行此操作的,但由于性能改进的提升而尝试在这里执行此操作。
【问题讨论】:
【参考方案1】:标识符similarity
以各种令人困惑的方式使用。并非所有都有意义...
CREATE OR REPLACE FUNCTION public.standard_search(search_term text, similarity_to integer)
RETURNS TABLE(obj_id integer, app_num integer, app_for text, similarity integer) -- ①
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
similarity integer; -- ②
BEGIN
RETURN QUERY
SELECT mark.id, mark.app_num, mark.app_for::text, levenshtein($1, focusword.word) AS similarity -- ③
FROM mark
INNER JOIN focusword ON (mark.id = focusword.mark_id)
WHERE similarity <= $2 -- ④
ORDER BY similarity, mark.app_for, mark.app_num; -- ⑤
END
$BODY$;
① ... 作为RETURNS TABLE
定义的返回类型中的列 - 实际上是OUT
参数。
② ... 作为变量 - 推翻 OUT
参数的可见性。但为什么呢?
③ ... 作为SELECT
列表中的列别名。
④ ...在WHERE
子句中,这是没有意义的。它不是指③(就像您似乎假设的那样)。输出名称在 WHERE
子句中不可见。在那里您只能参考 input 列名 - 或变量和参数。由于变量(隐藏参数,但在这里没有区别)为 NULL,因此不返回任何行。永远。
⑤ ... in ORDER BY
,解析为③中定义的列别名
这太疯狂了。甚至我也很难弄清楚哪个是可见的,我对此有一些经验。避免这样的命名冲突。遵循一些命名约定,并为参数、变量、列名和别名使用不同的名称!
相关:
PostgreSQL does not accept column alias in WHERE clause Postgres Function NULL value for row that references NEW How to return result of a SELECT inside a function in PostgreSQL?可能的解决方案
这会更有意义:
CREATE OR REPLACE FUNCTION public.standard_search(_search_term text, _similarity_to integer)
RETURNS TABLE(obj_id integer, app_num integer, app_for text, similarity integer) AS
$func$
SELECT m.id, m.app_num, m.app_for::text, levenshtein(_search_term, f.word) AS sim
FROM mark m
JOIN focusword f ON m.id = f.mark_id
WHERE levenshtein($1, f.word) <= _similarity_to
ORDER BY sim, m.app_for, m.app_num;
$func$ LANGUAGE sql;
【讨论】:
非常感谢您的洞察力。我以前从未使用过函数,并试图从我找到的文档和教程中制作一个。我一定会尝试这个解决方案。以上是关于PostgreSQL 函数不适用于 WHERE 子句的主要内容,如果未能解决你的问题,请参考以下文章
PreparedStatement 不适用于 Java 中的 Sybase IQ