Чи є в PL / pgSQL простий спосіб перевірити, чи не повернувся запит?


16

Зараз я трохи експериментую з PL / pgSQL і хочу знати, чи є більш елегантний спосіб зробити щось подібне:

select c.data into data from doc c where c.doc_id = id and c.group_cur > group_cur order by c.id desc limit 1;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        select c.data into data from doc c where c.doc_id = id and c.global_cur > global_cur order by c.id desc limit 1;
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                RETURN NULL;

Відповіді:


21

Блоки винятку призначені для вилучення помилок, а не перевірки умов. Іншими словами, якщо якась умова може бути вирішена під час компіляції, вона не повинна бути схоплена як помилка, а вирішена звичайною логікою програми.

У розділі захоплення помилок документації PL / PgSQL ви можете знайти таку пораду:

Порада: Блок, що містить пункт EXCEPTION, для входу та виходу значно дорожчий, ніж блок без нього. Тому не використовуйте EXCEPTION без потреби.

Замість використання винятків (погано) або IF / THEN / ELSIF (краще), ви можете переписати це на один запит:

SELECT c.data into data
FROM  doc c
WHERE c.doc_id = id
  and (
    c.group_cur > group_cur
    or
    c.global_cur > global_cur
  )
ORDER BY
  -- this will make group always preferred over global
  case when c.group_cur > group_cur then 1 else 2 end ASC,
  -- and this is your normal ordering
  c.id DESC
limit 1;

Якщо ви дійсно хочете два запити, ви можете використовувати спеціальну змінну FOUND, щоб перевірити, чи попередній запит дав якийсь результат:

select c.data into data
from doc c
where c.doc_id = id and c.group_cur > group_cur
order by c.id desc limit 1;
if not found then
    select c.data into data
    from doc c
    where c.doc_id = id and c.global_cur > global_cur
    order by c.id desc limit 1;
    if not found then return null; end if;
end if;

Обов'язкові посилання RTFM нижче :-)

Дивіться це для опису FOUNDзмінної, і це для IF/ THENблоків.


13

Ви можете вивчити спеціальну змінну FOUND булевого типу. З документації:

FOUND починається помилковим у межах кожного виклику функції PL / pgSQL. Він встановлюється кожним із наступних типів висловлювань:

Оператор SELECT INTO встановлює значення FOUND true, якщо присвоюється рядок, false, якщо рядок не повертається.

Оператор PERFORM встановлює FOUND true, якщо він створює (і відкидає) один або декілька рядків, false, якщо жоден рядок не створюється.

Операції UPDATE, INSERT та DELETE встановлюють FOUND true, якщо принаймні один рядок зачіпається, false, якщо жодна рядок не впливає.

Оператор FETCH встановлює FOUND true, якщо він повертає рядок, false, якщо жоден рядок не повертається.

Оператор MOVE встановлює FOUND true, якщо він успішно переставляє курсор, в іншому випадку false.

Оператор FOR або FOREACH встановлює значення FOUND true, якщо воно повторюється один чи більше разів, в іншому випадку false. FOUND встановлюється таким чином, коли цикл виходить; всередині виконання циклу FOUND не змінюється оператором циклу, хоча це може бути змінено виконанням інших операторів у тілі циклу.

ЗВЕРНІТЬ ЗАПИТУВАННЯ та ВЗАЄМНУВАННЯ ЗАВДАННЯ ВИКОНАННЯ встановіть FOUND true, якщо запит повертає принаймні один рядок, false, якщо жоден рядок не повертається.

Інші заяви PL / pgSQL не змінюють стан FOUND. Зокрема, зауважте, що EXECUTE змінює вихід GET DIAGNOSTICS, але не змінює FOUND.

FOUND - локальна змінна в межах кожної функції PL / pgSQL; будь-які зміни в ньому впливають лише на поточну функцію.


але select intoте, що повертає ніякі дані, все одно призведе до виключення, правда?
Джек каже, спробуйте topanswers.xyz

3
як правило, ні, вони створюють винятки, лише якщо вказано пункт
STRICT

Ага так, мій поганий - хоча це не означає, що обробник винятків у прикладі ОП ніколи не запускатиметься? :-)
Джек каже спробувати topanswers.xyz

1
@JackDouglas: Жодні дані, як правило, не є причиною виключення (за винятком спеціальних випадків, таких як модифікатор STRICT, наведений вище). В ОП було там неправильне уявлення.
Ервін Брандстетер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.