在函数内使用动态 ORDER BY 表达式执行 SELECT
Posted
技术标签:
【中文标题】在函数内使用动态 ORDER BY 表达式执行 SELECT【英文标题】:Execute a SELECT with dynamic ORDER BY expression inside a function 【发布时间】:2018-11-12 15:24:49 【问题描述】:我正在尝试执行一些 SELECT 以在函数中使用,我的代码是这样的:
DECLARE
result_one record;
BEGIN
EXECUTE 'WITH Q1 AS
(
SELECT id
FROM table_two
INNER JOINs, WHERE, etc, ORDER BY... DESC
)
SELECT Q1.id
FROM Q1
WHERE, ORDER BY...DESC';
RETURN final_result;
END;
我知道如何在 mysql 中做到这一点,但在 PostgreSQL 中我失败了。我应该改变什么或者我应该怎么做?
【问题讨论】:
您使用EXECUTE
的原因并不明显(至少对我而言),或者您期望从该查询中得到什么结果。上面的代码甚至没有理由出现在 plpgsql 中——你可以只使用 SQL 函数
我知道,选择是示例,我需要知道在这种情况下如何使用执行。那些select有动态参数,有些人会通过CLI编辑参数,长话短说。
那么如果你想让人们弄清楚你的问题是什么,你就需要给出一个更准确的例子。该代码只是没有返回任何结果,我不明白您为什么会期望它。您已经阅读了手册的相关部分 (postgresql.org/docs/11/…) 不是吗?
请注意,functions 与 procedures 不同,因此“存储过程”(SP) 在提及函数时是一个误导性术语。见:dba.stackexchange.com/a/194811/3684
【参考方案1】:
要使函数能够返回多行,它必须声明为returns table()
(或returns setof
)
要从 PL/pgSQL 函数中实际返回结果,您需要使用 return query
(如 documented in the manual)
要在 Postgres 中构建动态 SQL,强烈建议使用 format()
函数来正确处理标识符(并使源代码更易于阅读)。
所以你需要这样的东西:
create or replace function get_data(p_sort_column text)
returns table (id integer)
as
$$
begin
return query execute
format(
'with q1 as (
select id
from table_two
join table_three on ...
)
select q1.id
from q1
order by %I desc', p_sort_column);
end;
$$
language plpgsql;
请注意,如果您对最终查询进行排序,除非您在查询中使用 LIMIT
或 distinct on ()
,否则 CTE 中的 order by
几乎没有用处。
如果您对动态 SQL 使用另一个级别的美元报价,您可以让您的生活更加轻松:
create or replace function get_data(p_sort_column text)
returns table (id integer)
as
$$
begin
return query execute
format(
$query$
with q1 as (
select id
from table_two
join table_three on ...
)
select q1.id
from q1
order by %I desc
$query$, p_sort_column);
end;
$$
language plpgsql;
【讨论】:
【参考方案2】:What a_horse said. 和:
How to return result of a SELECT inside a function in PostgreSQL?另外,要动态选择 ORDER BY
的列,您必须将该列添加到 CTE 的 SELECT
列表中,如果该列可以重复(例如传递 'id') ...
更好的是,完全删除 CTE。无论如何,您的问题中没有任何内容可以保证使用它。 (仅在 Postgres 中需要时使用 CTE,它们通常比等效子查询或简单查询慢。)
CREATE OR REPLACE FUNCTION get_data(p_sort_column text)
RETURNS TABLE (id integer) AS
$func$
BEGIN
RETURN QUERY EXECUTE format(
$q$
SELECT t2.id -- assuming you meant t2?
FROM table_two t2
JOIN table_three t3 on ...
ORDER BY t2.%I DESC NULL LAST -- see below!
$q$, $1);
END
$func$ LANGUAGE plpgsql;
我附加了NULLS LAST
- 你可能也想要那个:
如果p_sort_column
一直来自同一个表,则在ORDER BY
子句中对该表名/别名进行硬编码。否则,请单独传递表名/别名并分别自动引用它们以确保安全:
我建议在具有多个连接的更大查询中对所有列名进行表限定(t2.id
不仅仅是id
)。避免各种令人惊讶的结果/混淆/滥用。
您可能希望对表名 (myschema.table_two
) 进行模式限定,以避免在使用不同的 search_path
调用函数时出现类似的麻烦:
【讨论】:
以上是关于在函数内使用动态 ORDER BY 表达式执行 SELECT的主要内容,如果未能解决你的问题,请参考以下文章
ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效
Lambda 表达式 order by 和 take issue
mybatis动态order by 排序问题 2021-09-17