Как сделать подстановку переменных в plpgsql? - PullRequest
0 голосов
/ 10 мая 2018

У меня есть немного сложного кода SQL, который я преобразовываю из MSSql в Postgres (используя Entity Framework Core 2.1), чтобы справиться с потенциальными условиями гонки при вставке в таблицу с уникальным индексом. Вот глупая версия:

const string QUERY = @"
DO
$$
    BEGIN
        insert into Foo (Field1,Field2,Field3)
        values (@value1,@value2,@value3);
    EXCEPTION WHEN others THEN
        -- do nothing; it's a race condition
    END;
$$ LANGUAGE plpgsql;

select *
from Foo
where Field1 = @value1
and Field2 = @value2;
";

return DbContext.Foos
                .FromSql(QUERY,
                    new NpgsqlParameter("value1", value1),
                    new NpgsqlParameter("value2", value2),
                    new NpgsqlParameter("value3", value3))
                .First();

Другими словами, попробуйте вставить запись, но не создавайте исключение, если попытка вставить ее приводит к уникальному нарушению индекса (индекс на Field1 + Field2), и вернуть запись, была ли она создана мной или другой веткой.

Эта концепция прекрасно работала в MSSql, используя блок TRY..CATCH. Насколько я могу судить, способ обработки исключений Postgres такой же, как и я, в блоке plpgsql.

НО ...

Похоже, что подстановка переменных в блоках plpgsql не работает. Приведенный выше код завершается с ошибкой .First() (нет элементов в последовательности), и когда я закомментирую строку EXCEPTION, я вижу реальную проблему:

Npgsql.PostgresException: 42703: столбец "значение1" не существует

Когда я тестирую с использованием обычного Sql, то есть выполняю insert без использования блока plpgsql, это прекрасно работает.

Итак, как правильно заменить переменную в блоке plpgsql?

1 Ответ

0 голосов
/ 10 мая 2018

Причина, по которой это не работает, заключается в том, что тело оператора DO на самом деле является строкой, текстом. См. Ссылку

$$ - это просто еще один способ разделения текста в postgresql. Его также можно заменить на ' или $somestuff$.

Поскольку это строка, у Npgsql и Postgresql нет причин связываться с @value1.

* Решения 1015 *? Только очень уродливый, поэтому не используйте эту конструкцию, так как вы не можете передать ей какие-либо значения. Работа с конкатенацией строк ничем не отличается от конкатенации в C #.

Альтернативы ? Да!

Вам не нужно обрабатывать исключения в блоках plpgsql. Просто вставьте, используйте ON CONFLICT DO NOTHING и будьте в пути.

INSERT INTO Foo (Field1,Field2,Field3)
VALUES (@value1,@value2,@value3)
ON CONFLICT DO NOTHING;

select *
from Foo
where Field1 = @value1
and Field2 = @value2;

Или, если вы действительно хотите продолжать использовать plpgsql , вы можете просто создать временную таблицу , используя опцию ON COMMIT DROP, заполните ее эти параметры в виде одной строки, затем используйте их в операторе DO. Для этого весь ваш код должен выполняться как часть одной транзакции . Вы можете использовать один явно на всякий случай.

Единственный способ передать параметры в код plpgsql - использовать эти 2 метода:

  1. Объявление функции с последующим вызовом с аргументами
  2. Когда вы уже находитесь внутри блока plpgsql, вы можете позвонить:
    EXECUTE $$ INSERT ... VALUES ($1, $2, $3); $$ USING 3, 'text value', 5.234;




Конечные ноты: Как один из разработчиков T-SQL, который любил его свободу, но перешел на Postgresql, я должен сказать, что большая разница в том, что с одной стороны есть T-SQL, который дает мощность, а с другой стороны, это очень мощный Postgresql-ароматизированный SQL. plpgsql очень редко гарантируется. Фактически, в кодовой базе мегабайтов сложных SQL-программ я могу переписать практически каждый код plpgsql в SQL. Вот насколько он действительно мощен по сравнению с SQL на основе MSSQL. Нужно просто привыкнуть и подружиться с очень обширной документацией. Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...