postgres `order by` 参数类型

postgres `order by` argument type

Postgresql 中 order by 子句的参数类型是什么?

我遇到了一个非常奇怪的行为(使用 Postgresql 9.5)。即查询

select * from unnest(array[1,4,3,2]) as x order by 1;

按预期生成 1,2,3,4。但是查询

select * from unnest(array[1,4,3,2]) as x order by 1::int;

产生 1,4,3,2,这看起来很奇怪。同样,每当我用任何函数(例如 greatest(0,1))甚至 case 运算符替换 1::int 时,结果都是无序的(与 what I would expect 相反)。

那么 order by 的参数应该具有哪种类型,我如何获得预期的行为?

这是预期的 (and documented) 行为:

A sort_expression can also be the column label or number of an output column

所以表达式:

order by 1

按结果集的第一列排序(由 SQL 标准定义)

然而表达式:

order by 1::int

按常量值1排序,本质上等同于:

order by 'foo'

通过为 order by 使用常量值,所有行都具有相同的排序值,因此不会真正排序。

要按表达式排序,只需使用:

order by 
    case 
       when some_column = 'foo' then 1
       when some_column = 'bar' then 2
       else 3
    end

以上根据case表达式的结果对结果进行排序

Actually I have a function with an integer argument which indicates the column to be used in the order by clause.

在所有列都是同一类型的情况下,这可以工作::

SELECT ....
ORDER BY 
  CASE function_to_get_a_column_number()
    WHEN 1 THEN column1
    WHEN 2 THEN column2
    .....
    WHEN 1235 THEN column1235
END

如果列的类型不同,您可以尝试:

SELECT ....
ORDER BY 
  CASE function_to_get_a_column_number()
    WHEN 1 THEN column1::varchar
    WHEN 2 THEN column2::varchar
    .....
    WHEN 1235 THEN column1235::varchar
END

但是这些 "workarounds" 太可怕了。除了返回列号的函数之外,您还需要其他方法。 也许是动态 SQL ?

我会说动态 SQL(感谢@kordirko 和其他人的提示)是我最初想到的问题的最佳解决方案:

create temp table my_data (
  id serial,
  val text
);

insert into my_data(id, val)
values (default, 'a'), (default, 'c'), (default, 'd'), (default, 'b');

create function fetch_my_data(col text)
returns setof my_data as
$f$
begin
 return query execute $$
   select * from my_data
   order by $$|| quote_ident(col);
end
$f$ language plpgsql;

select * from fetch_my_data('val'); -- order by val
select * from fetch_my_data('id'); -- order by id

一开始我认为这可以在 order by 子句 - sort_expression 的参数中使用 case 表达式来实现。令我困惑的棘手部分来了:当sort_expression一种标识符(列名或列号)时,使用相应的列排序结果。但是当 sort_expression 某个值 时,我们实际上使用该值本身(为每一行计算)对结果进行排序。这是@a_horse_with_no_name 改写后的回答。

所以当我查询 ... order by 1::int 时,在某种程度上我已经为每一行分配了值 1,然后尝试对一个数组进行排序,这显然是无用的。

some workarounds without dynamic queries,但是需要写更多的代码,似乎没有什么明显的优势。