使用函数内部的动态ORDER BY表达式执行SELECT

时间:2018-11-12 15:24:50

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

我正在尝试执行一些在函数内使用的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中却失败了。我应该更改什么或应该怎么做?

2 个答案:

答案 0 :(得分: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;

请注意,除非您在查询中使用order byLIMIT,否则在对最终查询进行排序时,CTE中的distinct on ()几乎没有用。


如果对动态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;

答案 1 :(得分:0)

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调用函数时遇到类似的麻烦: