[бд] вставить уникальную запись с проверкой

Josy

Пусть имеется таблица в базе данных. Пусть [для простоты] она содержит единственный столбец типа int, для которого задано ограничение уникальности. В неё забито какое-то множество записей (т.е. различных чисел).
Дано очередное число. Нужно проверить, есть ли уже такая запись в таблице, если есть - выдать какое-то сообщение, если её нет - добавить, и выдать другое сообщение. Вопрос такой - как это правильно сделать? У меня получилось родить 2 варианта:
1. Сначала select'ом проверяем, есть ли такое значение в таблице. Если нет, делаем insert.
2. Делаем сразу insert, если возникает ошибка нарушения ограничения - значит, такое число уже было в таблице.
Вариант 2, видимо, во всех смыслах правильнее (быстрее, действие атомарно и т.д.). Кроме того, он и удобнее - например, если моё приложение работает с таблицей в базе через что-нибудь типа ADO Recordset, не надо плодить какие-то лишние сущности для выполнения проверочного селекта.
Проблема в том, что непонятно, как сколько-нибудь перененосимым способом при нарушении ограничения эту ошибку перехватывать. Разные СУБД реагируют на нарушение ограничения по-разному, а когда это пролезает по всем уровням обёрток до ADO, то вообще получается не пойми что. То есть фактически можно лишь только написать обработчик, который переватывает ВСЕ ошибки, что плохо. Можно с этим как-нибудь разумно забороться?

otvertka07

1-й способ надо использовать

stm5981037

тогда нужно блокировать таблицу на запись

gopnik1994

надо второе
в принципе можно давать констрейнтам разумные имена и искать их в тексте ошибки
большинство СУБД в тексте ошибки пишет название нарушенного констрейнта.

Josy

>надо второе
Ну собственно, о чём и речь.
>в принципе можно давать констрейнтам разумные имена и искать их в тексте ошибки
>большинство СУБД в тексте ошибки пишет название нарушенного констрейнта.
Этот грязный хак мне уже несколько неместных отцов предлагали.
Неужели всё так запущено?

anton7805

таблица будет автоматически блокироваться на запись,если другая транзакция использует оператор insert

gopnik1994

так меньше всего гемора и приемлемое качество

Dasar

Лучше первый способ через транзакцию.
второй способ - это хак, и последствия будут долго аукаться.

kruzer25

и последствия будут долго аукаться
Почему?

Dasar

> Почему?
Именно, потому что это сильно не стандартный способ.
И изменения, которое вроде бы даже не связанное с этим (смена базы, смена провайдера, смена языка и т.д.) - будут ронять в том числе и это место.
Такое стоит делать - если ты померил, и выяснил, что это действительно дает выигрыш в два раза - и тебе действительно это жизненно необходимо.

kruzer25

И изменения, которое вроде бы даже не связанное с этим (смена базы, смена провайдера, смена языка и т.д.) - будут ронять в том числе и это место.
Но, грубо говоря - мы пытаемся вставить строку, нам говорят "не получилось"... как на это повлияет "смена базы, смена провайдера, смена языка и т.д."? Если при этом то поле всё равно будет уникальным...
И вообще, какой тогда смысл делать уникальным то поле - если мы всё равно ручками каждый раз это проверять будем?

Dasar

> мы пытаемся вставить строку, нам говорят "не получилось"... как на это повлияет "смена базы, смена провайдера, смена языка и т.д."?
потому что вот это "не получилось" - сильно зависит от базы, провайдера, языка
> И вообще, какой тогда смысл делать уникальным то поле - если мы всё равно ручками каждый раз это проверять будем?
только для того, чтобы иметь гарантию - что в базе лежат всегда хорошие данные.
ps
Использовать вот такие эффекты в программах - это все равно, что идти по краю обрыва ориентируясь на то, чувствует плечо ограду или нет (а не на реальный край).

kruzer25

потому что вот это "не получилось" - сильно зависит от базы, провайдера, языка
Но "получилось" ведь нам в любом случае не скажут?

Dasar

Скажут тебе - вместо того, что ты ожидал, например, упал коннект до базы - твои действия?

kruzer25

понял

gopnik1994

И изменения, которое вроде бы даже не связанное с этим (смена базы, смена провайдера, смена языка и т.д.) - будут ронять в том числе и это место.
и часто приходится переходить?
а даже если пришлось, то "это место" должно быть одно и тогда проблем особых не возникнет.
Обозвать его типа "транслятор ошибок" и положить во внешнем файле наподобии словаря с регэкспами - тогда переход на новую СУБД обойдется даже без пересобирания исходников.

gopnik1994

а открывать лишние блокирующие транзакции - это может сильно помешать, если пользователей будет много...

Dasar

если пишется - диплом, то да - можно так сделать, это даже интересно.
если пишется - промышленное ПО (в котором и так обычно хватает подводных камней то ну его нафиг.

gopnik1994

я как раз про промышленное ПО
в дипломном проекте можно генитально относится к работе под нагрузкой.

Dasar

и ты в промышленном ПО - готов идти на такой геморрой ради, например, прироста в 10% на редко дергаемой таблицы из 100 записей?
ps
Систему в целом - стоит далее по максимуму прозрачную, опирающуюся на стандартные технологии, и стандартное поведение.
и только далее, когда есть уже четкое понимание, что именно не устраивает, можно вставлять оптимизирующие хаки, при чем обязательно с profiler-ом в руках.
pps
скорость можно купить за деньги
надежность, гибкость, прозрачность - чисто за деньги уже не купишь.

gopnik1994

где гемор-то? я не пойму...

gopnik1994

гемор - это вставлять селекты перед каждой проверкой уникальности (читай дублирование структуры СУБД)

Dasar

Вот ты готов, например, сходу ответить, как этот запрос с ошибкой про-upgrade-ить до группового запроса (когда сразу хочеться вставить несколко записей)?
Вот это и есть гибкость/прозрачность.

gopnik1994

этот запрос с ошибкой про-upgrade-ить до группового запроса (когда сразу хочеться вставить несколко записей)?
поясни
в чем вопрос? какая разница сколько записей ты вставлял, когда произошла ошибка?

Dasar

> гемор - это вставлять селекты перед каждой проверкой уникальности (читай дублирование структуры СУБД)
гемор (вернее, ошибка архитектуры) - это использовать в качестве идентификатора последовательный счетчик.

gopnik1994

гемор (вернее, ошибка архитектуры) - это использовать в качестве идентификатора последовательный счетчик.
 
аргументируй.
GUID лучше?

FRider

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

gopnik1994

тока не все субды умеет по нему правильно индексировать

Dasar

> GUID лучше?
конечно, лучше. т.к. он все-таки глобально уникальный, что позволяет использовать в том числе и децентрализованные(слабоцентрализованную) архитектуру и механизмы (например, отложенная загрузка)

Dasar

> в чем вопрос? какая разница сколько записей ты вставлял, когда произошла ошибка?
по оригинальным условиям задачи - предлагалось вставлять по одной записи.
на это ты предложил решение - которое ловит ошибку.
Что будет, если захочеться вставлять в базу не по одной записи, а, например, сразу скопом?
придется менять полностью решение? возвращаясь к транзакционности, select-у и т.д.?

gopnik1994

> Что будет, если захочеться вставлять в базу не по одной записи, а, например, сразу скопом?
Ничего не будет. А почему должно что-то измениться? Инсерт выдаст эксепшен на одной из строк, откатит изменения и вернет имя нарушенного ограничения. Задача выяснить, какая именно запись не вставилась, тут стоять не может потому что либо ты вставляешь под одной и тогда знаешь, которая упала, либо ты проверяешь все скопом и втавляешь все скопом.
Если стоит задача найти существующие записи - то это совсем другая история и без select'а не обойтись, но вопрос автора треда звучит совсем по-другому.

kamputer

Решение номер 3
insert into my_table (select @x where not exists (select id from my_table where id = @x
Оставить комментарий
Имя или ник:
Комментарий: