问题描述
此简单函数返回title
字符串的有序列表。
create or replace function testfunction1 ()
returns table (
id bigint,lang_code tlang_code,title varchar
)
stable language sql as $$
select
id,lang_code,title
from
testable
order by
title collate "es_ES";
$$;
select * from testfunction ();
id|lang_code|title |
--|---------|----------------|
12|DE |NOCH FESTZULEGEN|
16|DE |NOCH FESTZULEGEN|
8|DE |NOCH FESTZULEGEN|
14|ES |POR DETERMINAR |
6|ES |POR DETERMINAR |
10|ES |POR DETERMINAR |
5|EN |TO BE DETERMINED|
9|EN |TO BE DETERMINED|
13|EN |TO BE DETERMINED|
11|FR |À DÉTERMINER |
15|FR |À DÉTERMINER |
7|FR |À DÉTERMINER |
但是,当我尝试使用collate
引入排序规则时,我无法获得正确的语法来根据参数_lang_code
设置正确的排序顺序。
create or replace function testfunction2 (_lang_code tlang_code)
returns table (
id bigint,title varchar
)
stable language sql as $$
select
id,title
from
testable
where
lang_code = _lang_code
order by
title collate
case _lang_code
when 'EN' then "en_US"
when 'ES' then "es_ES"
when 'FR' then "fr_FR"
when 'DE' then "de_DE"
end asc;
$$;
错误是SQL Error [42601]: ERROR: syntax error at or near "case"
。
我在order by
子句中尝试将案例定位到任何地方都没有成功。也许“ en_US”不被视为标量值?
编辑
我在Laurenz Albe评论后添加了where lang_code = _lang_code
。从我的实际问题过渡到此简化示例时,这是一个缺少的子句。
然而,case
的问题仍然存在相同的SQL错误。
解决方案
正如@Lorenz Albe在评论中指出的,"en_US"
是一个标识符,而不是标量值。这样可以防止case-when
结构在其任何when
分支中返回它。因此,不存在任何SQL方法。
作为一种变通方法,来自@doctore的动态SQL或将大小写移动到整个句子都是对问题的微妙但实用的解决方案。
解决方法
考虑到您正在使用参数_lang_code
选择要过滤的“内部语言”。以下PL / SQL代码允许您动态更改最终查询中的collate
:
create or replace function testfunction2 (_lang_code varchar)
returns table (
id bigint,lang_code varchar,title varchar
)
language plpgsql
as $$
declare
final_collate varchar;
final_query varchar;
begin
if (_lang_code = 'EN') then
final_collate := 'en_US';
elsif (_lang_code = 'ES') then
final_collate := 'es_ES';
end if;
-- Include other use cases you need
final_query := 'select t.id,t.lang_code,t.title ' ||
'from test_table t ' ||
'where t.lang_code = ''' || _lang_code || ''' ' ||
'order by t.title collate "' || final_collate || '" asc';
--raise exception 'Final query: %',final_query;
return query
execute final_query;
end;$$
现在,您可以执行测试甚至取消对raise exception
的注释,以确保有关合适的“最终查询”:
select testfunction2('EN')
select testfunction2('ES')
PD:,因为我假设_lang_code
是自定义变量,所以我将lang_code
和varchar
的类型更改为tlang_code
。>
按如下所示为订单写案:
create or replace function testfunction2 (_lang_code tlang_code)
returns table (
id bigint,lang_code tlang_code,title varchar
)
stable language sql as $$
select
id,lang_code,title
from
testtable
order by
case _lang_code
when 'EN' then title collate "en_US"
when 'ES' then title collate "es_ES"
when 'FR' then title collate "fr_FR"
when 'DE' then title collate "de_DE"
end asc;
$$;
,
@doctore解决方案强制使用PL / PGSQL函数,当包含整个选择语句时,这另一种移动大小写的方法也会执行。他们俩都远非优雅,但证明这个问题有道理。
很遗憾,我没有在原始函数中找到语法错误的原因。
create or replace function testfunction3 (_lang_code char(2))
returns table (
id bigint,lang_code char(2),title varchar
)
stable language plpgsql as $$
begin
case _lang_code
when 'EN' then
return query
select *
from testtable t
where t.lang_code = _lang_code
order by t.title collate "en_US";
when 'ES' then
return query
select *
from testtable t
where t.lang_code = _lang_code
order by t.title collate "es_ES";
when 'FR' then
return query
select *
from testtable t
where t.lang_code = _lang_code
order by t.title collate "fr_FR";
when 'DE' then
return query
select *
from testtable t
where t.lang_code = _lang_code
order by t.title collate "de_DE";
end case;
end
$$;