postgresql 中函数中的 "order by" 在调用时略有不同

The "order by" in a function in postgresql is reversed when called slightly different

我有一个 select 来自存储交易的 table 的功能。该功能更长,所以我制作了一个简洁的版本来尝试说明我遇到的问题。原始函数有一个“联合所有”来获得总和,除法求和,最后是我遗漏的乘积。输出将转换为 PDF 文件。我看到的行为似乎不是函数本身引起的,而是当我以两种方式调用函数时引起的。

我使用组织 ID 加入客户 table。一些客户使用相同的组织 ID 但不同的客户 ID 注册了不止一次。有一些我无法控制的原因导致客户可以多次注册。因此,为了不重复行,我对这些情况进行了豁免-table。

然后我使用案例“排序”,因为我需要按特定顺序显示产品。第一次调用时,产品按需要排序,但后者排序相反。

这是在 ubuntu 18.04 LTS 和 postgresql 10.12 上。

create or replace function f(quarter integer, year integer, cid text)
  returns table (cid text, name text, product text, quantity numeric, vat text, amount numeric)
as
$body$
select
 cid,
 name,
 internal_product,
 sum(quantity) as quantity,
 vat,
 sum(amount) as amount
from transaction
where
 extract('quarter' from created_at) =  and extract('year' from created_at) = 
group by
 cid,
 name,
 internal_product,
 vat) as VAT

where cid in ()

order by
 name desc,
 case internal_product
  when 'product A' then 1
  when 'extra B' then 2
  when 'large C' then 3
  when 'small D' then 4
  when '' then 99
 end
 $body$
language sql;

这是我想要的排序方式。

select
 c.cid, c.cidtext, f.*
from
 f(2,2020,'958935420') f
join
 customer c
on
 f.cid = c.cid;

添加豁免项 table 会反转排序。

select
 c.cid, c.cidtext, f.*
from
 f(2,2020,'958935420') f
join
 customer c
on
 f.cid = c.cid
where c.cid not in (select cid from exempt_cid);

一个 SELECT 语句只有在它有一个 ORDER BY 子句时才有明确的顺序。由于调用该函数的语句没有 ORDER BY,您没有那个保证。

实际上,这取决于执行计划(使用EXPLAIN)。该函数本身将 return 值按所需顺序排列,如果执行计划的其余部分没有任何内容会扰乱该顺序,您就可以了。

我的建议是 永远不要ORDER BY 子句添加到视图定义或函数结果中,但始终将它放在它所属的位置,在调用查询中。 PostgreSQL 不会优化此类 ORDER BY 子句,您最终可能会付出某种代价而没有任何好处。

如果您在函数中排序以简化代码维护,以防将来必须更改排序并且您不想更新多个调用查询:

create or replace function f(quarter integer, year integer, cid text)
  returns table (cid text, name text, product text, quantity numeric, vat text, amount numeric)
as
$body$
select
 cid,
 name,
 internal_product,
 sum(quantity) as quantity,
 vat,
 sum(amount) as amount,
 row_number() 
    over (order by name desc,
                   case internal_product
                     when 'product A' then 1
                     when 'extra B' then 2
                     when 'large C' then 3
                     when 'small D' then 4
                     when '' then 99
                   end) as sort_key
from transaction
where
 extract('quarter' from created_at) =  and extract('year' from created_at) = 
group by
 cid,
 name,
 internal_product,
 vat) as VAT

where cid in ()
 
 $body$
language sql;

那么您的通话将如下所示:

select
 c.cid, c.cidtext, f.cid, f.name, f.internal_product, f.quantity, f.vat, f.amount
from
 f(2,2020,'958935420') f
join
 customer c
on
 f.cid = c.cid
order by sort_key;