Сравнение значения строки с текстовыми полями со строкой нетипизированных строковых литералов в CASE - PullRequest
0 голосов
/ 26 ноября 2018

Я нашел аккуратный способ в стеке потока обработки многопеременной инструкции case с использованием значений ROW.Здорово, как чисто это выглядит ...

Однако я получаю ошибки, непосредственно сравнивая значение строки из 2 столбцов таблицы типа text со строкой из строковых литералов.

IЯ использовал обходной путь с функцией spTup('Deposit', '' ), которая работает, но может быть медленнее.Другой способ, который работает, это явное приведение строковых литералов к text, но это создает много визуальных помех.

Вопросы:

  1. ПочемуPostgres не может сделать вывод, что строковые литералы должны обрабатываться как text тип?
  2. Почему Postgres может выводить тип строкового литерала в строке одного элемента, но не в строке с 2 элементами?
  3. Мне показалось, что я справляюсь с обработкой типов в Postgres, но я не совсем понимаю этот сценарий, кто-нибудь может объяснить?
  4. Есть ли другой подход, который минимизирует визуальный беспорядок?

Я использую Postgres 10.1 на локальном хосте и 9.6.6 на тестовом и производственном сервере.

Тестовая настройка:

create table if not exists tblTest ( SeqID serial, EventType text, EventResult text, Amt decimal );
truncate table tblTest;
insert into tblTest( EventType, EventResult, Amt )
values ( 'Withdrawal', '', 1.11 ), ('Deposit', '', 2.22 ), ('Deposit', 'succeeded', 3.33 ), ('Deposit', 'failed', 4.44 );

create or replace function spTup( p_1 text, p_2 text ) 
returns record as $func$
    select ( p_1, p_2 );
$func$ LANGUAGE sql IMMUTABLE; 


-- Runs without error (using single element tuple)
select SeqID, EventType, case ( EventType ) when ( 'Deposit' ) then Amt else 9.999 end
from tblTest;

-- ERROR: cannot compare dissimilar column types text and unknown at record column 1
select SeqID, EventType, EventResult, case ( EventType, EventResult ) 
when ( 'Deposit', '' ) then Amt else 9.999 end
from tblTest;

-- Runs without error -- visually the cleanest apart from using spTup function
select SeqID, EventType, EventResult, case ( EventType, EventResult )::text 
when ( 'Deposit', '' )::text then Amt else 9.999 end
from tblTest;

-- Runs without error
select SeqID, EventType, EventResult, case ( EventType, EventResult ) 
when ( 'Deposit'::text, ''::text ) then Amt else 9.999 end
from tblTest;

select SeqID, EventType, EventResult, case ( EventType, EventResult ) 
when spTup( 'Deposit', '' ) then Amt else 9.999 end
from tblTest;

-- ERROR: input of anonymous composite types is not implemented
select SeqID, EventType, EventResult, case ( EventType, EventResult ) 
when '( "Deposit", "" )' then Amt else 9.999 end
from tblTest;

-- Just out of interest
select ( 'Deposit', '' ), ( 'Deposit'::text, ''::text );
/**
    row             row
    (Deposit,"")    (Deposit,"")
**/

select SeqID, EventType, EventResult, ( EventType, EventResult )
from tblTest;
/** 
    seqid   eventtype   eventresult     row
    1       Withdrawal                  (Withdrawal,"")
    2       Deposit                     (Deposit,"")
    3       Deposit     succeeded       (Deposit,succeeded)
    4       Deposit     failed          (Deposit,failed)
**/

1 Ответ

0 голосов
/ 27 ноября 2018

Кажется, это ограничение "простого" или "переключенного" CASE, которое вы используете.
Альтернативный вариант синтаксиса CASE работает безявное приведение:

select SeqID, EventType, EventResult
     , <b>CASE WHEN (EventType, EventResult) = ('Deposit', '') THEN amt ELSE 9.999 END</b>
from tblTest;

Пока у вас есть один тест для проверки, этот вариант даже "минимизирует визуальный беспорядок" .Два дополнительных символа, но легче читаемых (ИМХО).Однако для нескольких случаев «переключаемый» вариант представляется предпочтительным.

Различное поведение, очевидно, является результатом другого рабочего процесса в «простом» CASE. Руководство:

Первый expression вычисляется, затем сравнивается с каждым из value выражений в предложениях WHEN до тех пор, пока не будет найдено одно значение, равное ему.

Путь к коду для простого выражения - значение Сравнение менее сложно разрешить типы данных - и не удается для анонимных значений строк .По ощущениям недостаток в реализации.Можно ожидать одного и того же поведения для обоих вариантов - и подать отчет об ошибке.

Но поведение было таким, по крайней мере, после Postgres 8.4 (и такое же в pg 11):

db <> fiddle здесь

Вероятно, очень немногие люди имели подобные идеи с нетипизированными значениями строк в переключенном CASE до сих пор.


Оставьте свой вопрос:

Почему Postgres может определить тип строкового литерала в строке с одним элементом, а не строку с 2 элементами?

Ответ: поскольку значения строк с одним элементом ((foo)) упрощаются до их одного элемента (foo) при оценке выражения почти везде в Postgres.Итак, это:

CASE (eventtype) WHEN ('Deposit') THEN ...

эффективно упрощается до:

CASE  eventtype  WHEN  'Deposit'  THEN ...
...