【问题标题】:Execute a SELECT with dynamic ORDER BY expression inside a function在函数内使用动态 ORDER BY 表达式执行 SELECT
【发布时间】: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

标签: postgresql plpgsql common-table-expression dynamic-sql stored-functions


【解决方案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. 和:

    另外,要动态选择 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 调用函数时出现类似的麻烦:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-16
      • 1970-01-01
      • 2013-10-06
      • 2012-01-19
      • 1970-01-01
      • 2012-02-05
      • 1970-01-01
      相关资源
      最近更新 更多