#postgresql #sql-order-by #case-when #postgresql-12 #collate
#postgresql #sql-order-by #case-когда #postgresql-12 #сопоставлять
Вопрос:
Эта простая функция возвращает упорядоченный список 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,
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;
$$;
Ошибка SQL Error [42601]: ERROR: syntax error at or near "case"
.
Я безуспешно пытался разместить case везде в order by
предложении. Может быть, «en_US» не считается скалярным значением?
РЕДАКТИРОВАТЬ, которое я добавил where lang_code = _lang_code
после комментария Лоренца Альбе. Это было отсутствующее предложение при переходе от моей реальной проблемы к этому упрощенному примеру.
Однако проблема с case
остается с той же ошибкой SQL.
РЕШЕНИЕ
Как указал @Lorenz Albe в комментарии, "en_US"
является идентификатором, а не скалярным значением. Это не позволяет case-when
структуре возвращать его в любой из своих when
ветвей. Таким образом, способа SQL не существует.
В качестве обходного пути, динамический SQL от @doctore или перемещение регистра, чтобы охватить все предложение, являются неэлегантными, но функциональными решениями проблемы.
Комментарии:
1. Вы пытаетесь смешивать разные
collate
значения для одного и того же процесса упорядочения, что в основном является причиной того, что ваш «первый запрос» работает (у него есть только один), но не следующие попытки. Вам нужно изменить подход и использовать подходящий,collate
учитываяlang_code
значение, и «смешивать разные фрагменты» так, как вы хотите (по одному «фрагменту» для каждого языка).2. Я не уверен, что понимаю ваш комментарий. Очевидно, что я не хочу смешивать порядки сортировки, я просто хочу выбрать тот или иной в зависимости от
_lang_code
параметра. Очевидно, что это функция,collate
представленная в предложении Postgre12 (?). Не уверен, что вы подразумеваете под «фрагментами»3. Я мог бы переключить все предложение select с самого начала, но потерять преимущество наличия функции SQL.
4. Подход Ахилеша Мишры приводит к «смешиванию» или установке в два раза большего порядка сортировки, но это всего лишь артефакт из-за синтаксической ошибки.
5. Хорошо, теперь я понимаю «весь поток». Именно по этой причине я включил ответ, чтобы позволить вам разобраться с вашим вариантом использования
Ответ №1:
Принимая во внимание, что вы используете параметр _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
что это пользовательский.
Комментарии:
1. Спасибо доктору. Извините, я должен был заменить
tlang_code
наchar(2)
. На самом деле это домен типа char(2) с проверками. К сожалению, это решение является PL / PGSQL, эквивалентным выполнению переключения с самого начала.2. Если нет способа использовать
pl/sql
, тогда мы находимся «в начале» наших дебатов, и, как сказал вам @LaurenzAlbe, вероятно, то, что вы пытаетесь сделать, невозможно.3. Что ж, Лоренц описал мою логическую ошибку, которая была исправлена, но не приблизилась к исходной синтаксической ошибке в структуре case-when.
4. Вы не можете включить «что-либо» между предложениями
order by
иcase
, однако, как вы обнаружили в ответе @AkhileshMishra, если вы удалите свойtitle collate
PostgreSQL, он уведомит вас о другой ошибке, что в основном означает,you cannot use more than one collate value in the order by clause for the same column
5. Я выбрал ваше решение в качестве ответа на вопрос, однако
where lang_code = _lang_code
необходимо добавить кfinal_query
. Я попытался отредактировать, но очередь заполнена. Спасибо
Ответ №2:
Напишите свой случай для заказа, как показано ниже:
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;
$$;
Комментарии:
1. Не работает. Как я уже сказал, я протестировал (почти) все 🙂 Ошибка теперь такова
SQL Error [42P21]: ERROR: collation mismatch between explicit collations "en_US" and "es_ES"
2. Это другая проблема. Приведенное выше решение предназначено только для синтаксической ошибки, о которой вы упомянули в вопросе.
3. Это та же проблема. Анализатор ожидает скаляр после
when
, а не часть предложения4. Смотрите postgresql.org/docs/12/… «Типы данных всех результирующих выражений должны быть преобразованы в один тип вывода».
5. Я даже пытался заключить
title collate "en_US"
в круглые скобки
Ответ №3:
Решение @doctore запускает функцию PL / PGSQL, поэтому это другой подход к перемещению случая, когда для охвата всего предложения select. Они оба далеки от элегантности, но доказывают, что вопрос имел смысл.
К сожалению, я не нашел причину синтаксической ошибки в моей исходной функции.
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
$$;