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

postgresql 在 where 子句中使用 json 子元素

为啥递归联合不适用于 PostgreSQL 中的复合类型

使用内部连接更新 Postgresql

Laravel4:块函数不适用于模型保存()

自动回滚不适用于 Postgresql 上的 liquibase