SELECT "быстрее", чем функция с вложенным INSERT? - PullRequest
1 голос
/ 19 апреля 2020

Я использую функцию, которая вставляет строку в таблицу, если она не существует, а затем возвращает идентификатор строки.

Всякий раз, когда я помещаю функцию в оператор SELECT, с значения, которых еще нет в таблице, например:

SELECT * FROM table WHERE id = function(123);

... возвращает пустую строку. Однако повторный запуск с теми же значениями вернет строку со значениями, которые я хочу увидеть.

Почему это происходит? INSERT работает на скорости SELECT? Или PostgreSQL кэширует таблицу, когда она не существует, и при следующем запуске отображает результат?

Вот готовый пример использования этой проблемы:

CREATE TABLE IF NOT EXISTS test_table(
id INTEGER,
tvalue boolean
);

CREATE OR REPLACE FUNCTION test_function(user_id INTEGER)
    RETURNS integer
    LANGUAGE 'plpgsql'
AS $$
DECLARE
    __user_id INTEGER;

BEGIN
    EXECUTE format('SELECT * FROM test_table WHERE id = $1')
    USING user_id
    INTO __user_id;

    IF __user_id IS NOT NULL THEN
        RETURN __user_id;

    ELSE
        INSERT INTO test_table(id, tvalue)
        VALUES (user_id, TRUE) 
        RETURNING id
        INTO __user_id;
        RETURN __user_id;
    END IF;
END;
$$;

Позвоните:

SELECT * FROM test_table WHERE id = test_function(4);

Чтобы воспроизвести проблему, передайте любое целое число, которого еще нет в таблице.

1 Ответ

1 голос
/ 19 апреля 2020

Пример разбит на несколько мест.

  • Нет необходимости в Dynami c SQL с EXECUTE.
  • SELECT * в функции неправильно.
  • В вашем определении таблицы должно быть ограничение UNIQUE или PRIMARY KEY для (id).
  • Самое важное, что последний оператор SELECT обречен на неудачу. Поскольку функция имеет значение VOLATILE (должно быть), она оценивается один раз для каждой существующей строки в таблице. Даже если бы это сработало, это было бы кошмаром производительности. Но это не так. Как прокомментировал @ user2864740, также проблема с видимостью. Postgres проверяет каждую существующую строку на результат функции, которая, в свою очередь, добавляет 1 или более строк, и эти строки еще не находятся в моментальном снимке, над которым работает SELECT.

    SELECT * FROM test_table WHERE id = test_function(4);

Это будет работать (но см. Ниже!):

CREATE TABLE test_table (
  id     int PRIMARY KEY  --!
, tvalue bool
);

CREATE OR REPLACE FUNCTION test_function(_user_id int)
  RETURNS test_table LANGUAGE sql AS
$func$
   WITH ins AS (
      INSERT INTO test_table(id, tvalue)
      VALUES (_user_id, TRUE) 
      ON CONFLICT DO NOTHING
      RETURNING *
      )
   TABLE ins
   UNION ALL
   SELECT * FROM test_table WHERE id = _user_id
   LIMIT 1
$func$;

И замените ваш SELECT просто :

SELECT * FROM test_function(1);

дБ <> скрипка здесь

Похожие:

Все еще существует условие гонки для одновременных звонков. Если это может произойти, подумайте:

...