在函数内使用动态 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/…) 不是吗? 请注意,functionsprocedures 不同,因此“存储过程”(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;

请注意,如果您对最终查询进行排序,除非您在查询中使用 LIMITdistinct 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 - 你可能也想要那个:

PostgreSQL sort by datetime asc, null first?

如果p_sort_column 一直来自同一个表,则在ORDER BY 子句中对该表名/别名进行硬编码。否则,请单独传递表名/别名并分别自动引用它们以确保安全:

Define table and column names as arguments in a plpgsql function?

我建议在具有多个连接的更大查询中对所有列名进行表限定(t2.id 不仅仅是id)。避免各种令人惊讶的结果/混淆/滥用。

您可能希望对表名 (myschema.table_two) 进行模式限定,以避免在使用不同的 search_path 调用函数时出现类似的麻烦:

How does the search_path influence identifier resolution and the "current schema"

【讨论】:

以上是关于在函数内使用动态 ORDER BY 表达式执行 SELECT的主要内容,如果未能解决你的问题,请参考以下文章

ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效

Lambda 表达式 order by 和 take issue

mybatis动态order by 排序问题 2021-09-17

MySQL 在执行“order by”时停留在“使用文件排序”上

order by 和 group by 的区别?

sql中order by和group by的区别