PL/pgSQL проверяет, существует ли строка

Я пишу функцию на PL/pgSQL и ищу самый простой способ проверить, существует ли строка.
Прямо сейчас я выбираю integer в boolean, что на самом деле не работает. У меня недостаточно опыта работы с PL/pgSQL, чтобы знать, как это сделать лучше всего.

Вот часть моей функции:

DECLARE person_exists boolean;
BEGIN

person_exists := FALSE;

SELECT "person_id" INTO person_exists
  FROM "people" p
WHERE p.person_id = my_person_id
LIMIT 1;

IF person_exists THEN
  -- Do something
END IF;

END; $$ LANGUAGE plpgsql;

Обновление. Сейчас я делаю что-то вроде этого:

DECLARE person_exists integer;
BEGIN

person_exists := 0;

SELECT count("person_id") INTO person_exists
  FROM "people" p
WHERE p.person_id = my_person_id
LIMIT 1;

IF person_exists < 1 THEN
  -- Do something
END IF;

person nnyby    schedule 09.08.2012    source источник


Ответы (2)


Проще, короче, быстрее: EXISTS.

IF EXISTS (SELECT 1 FROM people p WHERE p.person_id = my_person_id) THEN
  -- do something
END IF;

Планировщик запросов может останавливаться на первой найденной строке, в отличие от count(), который будет сканировать все (совпадающие) строки независимо от того. Имеет значение с большими столами. Разница невелика для условия уникального столбца: подходит только одна строка, и есть индекс для быстрого поиска.

Улучшено с помощью ввода из @ a_horse_with_no_name в комментариях.

Вы можете просто использовать пустой список SELECT:

IF EXISTS (SELECT FROM people p WHERE p.person_id = my_person_id) THEN ...

Список SELECT не влияет на результат EXISTS. Имеет значение только наличие хотя бы одной подходящей строки.

person Erwin Brandstetter    schedule 09.08.2012
comment
Хорошая точка зрения! (Хотя person_id, вероятно, является первичным ключом, поэтому он будет сканировать только одну таблицу, используя поиск по индексу). - person a_horse_with_no_name; 10.08.2012
comment
@a_horse_with_no_name: на данный момент (Postgres 9.1) count() всегда запускает последовательное сканирование. Попробуйте EXPLAIN ANALYZE SELECT count(id) from tbl с любой таблицей. Подробнее о медленном подсчете в Postgres Wiki. Предполагается, что новое сканирование индекса Postgres 9.2 улучшит ситуацию, потому что оно может (при некоторых условиях) использовать сканирование индекса для count(id) - хотя мне еще предстоит попробовать и увидеть это самому. - person Erwin Brandstetter; 10.08.2012
comment
count(*) с условием (особенно не в столбце PK) не запускает последовательное сканирование. - person a_horse_with_no_name; 10.08.2012
comment
@a_horse_with_no_name: Конечно, ты прав! Я думал о простом счете - как вы можете видеть из моего примера в комментарии. Я улучшил свой ответ с вашим вкладом, спасибо. - person Erwin Brandstetter; 10.08.2012
comment
Если вы хотите использовать это вне функции, синтаксис для этого приведен здесь: stackoverflow.com/a/20957691/908677< /а> - person Elijah Lofgren; 14.12.2015
comment
@EugenKonkov: Я лидирую в этом: Simpler, shorter, faster. - person Erwin Brandstetter; 20.02.2019
comment
@ErwinBrandstetter: я использую PostgreSQL 11.5, и я думаю, что у нас должна быть скобка для покрытия EXISTS. Без фигурной скобки код все еще работает, но мы не можем получить True. Таким образом, заявление должно быть IF (EXISTS (SELECT FROM people WHERE person_id = my_person_id)) THEN ... - person Thinh Phan; 31.08.2019
comment
@ThinhPhan: это должно быть недоразумение. Внешние скобки не обязательны. См. db‹›fiddle здесь. - person Erwin Brandstetter; 01.09.2019
comment
@ErwinBrandstetter Извини, мой плохой. Ты прав. Теперь работает. Большое спасибо. - person Thinh Phan; 02.09.2019

Используйте количество (*)

declare 
   cnt integer;
begin
  SELECT count(*) INTO cnt
  FROM people
  WHERE person_id = my_person_id;

IF cnt > 0 THEN
  -- Do something
END IF;

Изменить (для тех, кто не читал заявление, и других, кто может делать что-то подобное)

Решение эффективно только, потому что в столбце есть предложение where (и имя столбца предполагает, что это первичный ключ, поэтому предложение where очень эффективно)

Из-за этого предложения where нет необходимости использовать LIMIT или что-то еще для проверки наличия строки, которая идентифицируется ее первичным ключом. Это это эффективный способ проверить это.

person a_horse_with_no_name    schedule 09.08.2012
comment
Не используйте COUNT для этой цели - это проблема производительности - или вы должны использовать производную таблицу SELECT COUNT(*) FROM (SELECT * FROM people LIMIT 1) x - person Pavel Stehule; 10.08.2012
comment
@PavelStehule: даже если для первичного ключа есть условие where? Я не могу себе представить, как это может быть намного медленнее, чем ваше заявление. План выполнения практически идентичен для обоих решений. - person a_horse_with_no_name; 10.08.2012
comment
@a_horse_with_no_name, когда он фильтрует PK, тогда все в порядке на 99%. В этом случае PL/pgSQL должен оценивать 2 SELECT вместо одного. Но этот шаблон просто рискован. Некоторые люди не выполняют проверку, поэтому фильтровать нужно PK - person Pavel Stehule; 10.08.2012
comment
@PavelStehule: я добавил объяснение этому. Я сравнил планы для решения Эрвина и моего решения, и нет никакой (реальной) разницы. Из-за where в индексированном столбце это эффективно. - person a_horse_with_no_name; 10.08.2012
comment
план должен быть таким же - но PL/pgSQL просто клей для SQL - со счетчиком plpgsql оценивает два SELECT - 1) SELECT INTO var, 2) SELECT var › 0 - person Pavel Stehule; 10.08.2012
comment
@PavelStehule: вы говорите, что if cnt > 0 на самом деле выполняет оператор select в фоновом режиме? И если это так, не будет ли это иметь место с любым решением, включая встроенный выбор от Erwin? - person a_horse_with_no_name; 10.08.2012
comment
@a_horse_with_no_name, точно - это тривиальный SELECT (примерно в 10 раз быстрее, чем обычный SELECT), но это все еще SELECT. если вы хотите увидеть реальное лицо кода plpgsql, используйте #option dump -- см. первый список кодов из статьи postgres.cz/wiki/PL/pgSQL_efektivn%C4%9B (извините, статья на чешском, но образцы на английском) - translate.google.com/ - person Pavel Stehule; 10.08.2012