#c #postgresql
#c #postgresql
Вопрос:
Если я создам таблицу с уникальным ограничением, например:
CREATE TABLE distributors (
did integer,
name varchar(40) UNIQUE
);
Что произойдет, если я попытаюсь ввести запись с именем, которое уже существует. Я попытался это сделать, и он просто вышел без отображения какого-либо сообщения об ошибке. Есть ли способ проверить, действительно ли была вставлена новая запись?
Комментарии:
1. Данные не будут вставлены, то, как ошибка интерпретируется из базы данных, зависит от того, что вы используете для доступа к ней? libpq / odbc и т.д.?
2. Я использую libpq, что бы это вернуло?
3. проверьте результат PQexec с помощью PQresultStatus ; postgresql.org/docs/8.0/static/libpq-exec.html
Ответ №1:
Если вставка завершилась неудачно, то где-то должен быть установлен код ошибки, читаемый каким-либо методом используемого вами интерфейса — более подробная информация определенно содержится в документации к вашей библиотеке / модулю access.
В качестве альтернативы вы можете изменить свою вставку на:
INSERT INTO distributors (did, name) values ( ... ) RETURNING did;
И если это ничего не вернуло — произошла ошибка.
Ответ №2:
Если вы попытаетесь вставить запись с именем, которое уже существует, вы получите сообщение об ошибке, подобное этому:
ERROR: duplicate key value violates unique constraint "distributors_name_key"
DETAIL: Key (name)=(aaa) already exists.
и запись не будет вставлена.
Если вы сделаете это с уровня allplcation, исключение будет выдано с сообщением, подобным этому. Программисту решать, как обрабатывать это исключение.
Если ваше поле ID генерируется автоматически (SERIAL или BIGSERIAL), и вы вставляете только name, если вы вставляете имя, которое уже существует, последовательность идентификаторов увеличится на 1, даже если вы не вставляли никакой записи.
Чтобы избежать этого, вы можете выполнить запрос «SELECT» перед ВСТАВКОЙ, чтобы проверить, существует ли эта запись. Возможно сделать все в одной транзакции, в псевдокоде:
BEGIN TRANSACTION;
int records = SELECT name FROM table WHERE name = 'aaa' FOR UPDATE; //FOR UPDATE to lock the row from being read by other user until transaction finishes.
if (records == 0)
INSERT INTO table VALUES (1, 'aaa');
else
MessageBox.Show("Record already exists");
COMMIT TRANSACCTION;
Комментарии:
1. @mj82: пожалуйста, обратите внимание, что приведенный выше код иногда также возвращает уникальное нарушение.
2. @depesz: можете ли вы точно сказать, в какой ситуации это может произойти?
3. @mj83: если кто-то вставляет строку после завершения выбора, но до выполнения ВСТАВКИ
4. @a_horse_with_no_name: вы правы, это моя ошибка. Как насчет исправленной версии: когда вы используете SELECT ДЛЯ ОБНОВЛЕНИЯ, postgres блокирует строку от чтения текущим пользователем до завершения транзакции и снятия блокировки.
5. @mj82: нет ничего не изменит: если есть строка, подлежащая блокировке, вы все равно не будете вставлять. Если count() возвращает ноль, в первую очередь нечего было блокировать. Единственным способом избежать этого была бы блокировка таблицы