This simple function returns an ordered list of title
strings.
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 |
However, when I try to introduce a collation order with collate
I am not being able to get the syntax right to set the proper collation order based on the parameter _lang_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
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;
$$;
The error is SQL Error [42601]: ERROR: syntax error at or near "case"
.
I have unsuccessfully tried positioning the case everywhere in the order by
clause. Maybe the "en_US" is not considered a scalar value?
EDIT
I've added where lang_code = _lang_code
after Laurenz Albe comment. This was a missing clause when traslating from my real problem to this simplified example.
However the issue with the case
remains with the same SQL error.
As @Lorenz Albe pointed in a comment, "en_US"
is an identifier, not a scalar value. This prevents the case-when
structure from returning it in any of its when
branches. So no SQL way happens to exist.
As a work around, dynamic SQL from @doctore or moving the case to embrace the whole sentence, are both inelegant but functional solutions to the problem.
Taking into account you are using the parameter _lang_code
to choose the "internal language" to filter. The following PL/SQL code allow you change the collate
in the final query dynamically:
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;$$
Now you can execute your tests or even uncomment the raise exception
to be sure about the suitable "final query":
select testfunction2('EN')
select testfunction2('ES')
PD: I have changed the type of _lang_code
and lang_code
to varchar
because I assume tlang_code
is a custom one.