Как избежать дублирования идентификаторов в базе данных - PullRequest
1 голос
/ 14 октября 2010

ВНИМАНИЕ: Оцените все ответы, спасибо, но у нас уже есть последовательность ... и мы не можем использовать УНИКАЛЬНЫЕ ограничения, потому что некоторые элементы должны иметь дубликаты. Мне нужно как-то обрабатывать это с помощью PLSQL, поэтому на основе некоторых критериев(используя оператор if) мне нужно убедиться, что для этого нет дубликатов .. И просто для подтверждения, эти идентификаторы очень настраиваются с разными типами строк. И для каждого набора строк у нас есть только число, которое подсчитываетсядля этой строки (STR-STR - ####), и у нас есть, как сотни этих комбинаций STR-STR, и для каждой комбинации у нас есть ####, который считает ... И поверх этих некоторых STR-STRкомбинации могут иметь дубликаты. Таким образом, мы НЕ МОЖЕМ использовать УНИКАЛЬНЫЕ ОГРАНИЧЕНИЯ, не можем использовать ПЕРВИЧНЫЙ КЛЮЧ, так как это не простое число, и помимо этого у нас есть ПЕРВИЧНЫЙ КЛЮЧ, назначенный каждому элементу. Эти идентификаторыдля пользователей, а не для управления базой данных.

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

Теперь с этим есть некоторые проблемы, между получением следующего номера и фиксацией в базе данных существует около 1000 строк кодов.,Проблема заключается в том, что когда 2 человека создают элемент с одинаковыми критериями в течение нескольких секунд, иногда выдается одно и то же число.

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

Несмотря на то, что этот код уменьшил шансы на дублирование, если я сохраню 2 элемента одновременно, я все равно сохранюполучить дубликат числа ..

Кто-нибудь знает любой способ избежать дубликатов и снизить шансы дубликата числа до 0?

РЕДАКТИРОВАТЬ1: у нас уже есть первичный ключ .. Этот идентификаторспециальная строка, и очень настраиваемая, поэтому нельзя просто использовать число, которое считается от 0

РЕДАКТИРОВАТЬ2: Есть также некоторые случаи, когда нам нужны дубликаты ... это очень незначительно (может быть,около 10 различных треков предметов используют дубликаты), поэтому перед тем, как я проверю дубликаты, перед коммитом с инструкцией if, так что если элемент сохраняетсяЗатем на одной из систем, которая должна иметь дубликаты, я пропускаю проверку ...

РЕДАКТИРОВАТЬ 3: Мы используем PL / SQL здесь

РЕДАКТИРОВАТЬ 4: Я думаю, эта проблемабыл очень конкретным, и я не совсем выразил это так. Хотя было много ответов, никто из них действительно не охватил мою проблему .. Несмотря на это, я решил проблему и добавил ниже как мой ответ ..

Ответы [ 13 ]

5 голосов
/ 14 октября 2010

Найдите команду CREATE SEQUENCE. Oracle может обрабатывать уникальные номера для вас. (Фактически) В каждой базе данных есть способ решения этой проблемы, хотя они несколько отличаются в том, как она реализована.

[В ответ на новые требования, отредактированные в вопросе]

Вы все еще можете использовать ПОСЛЕДОВАТЕЛЬНОСТЬ для создания счетной части, затем объединить ее с префиксом и сохранить обратно в базу данных. И в тех редких случаях, когда вам нужен дубликат ID, не получайте номер из ПОСЛЕДОВАТЕЛЬНОСТИ, просто используйте тот, который у вас уже есть. Но SEQUENCE решит вашу проблему создания «колодца», из которого вы сможете извлечь гарантированный уникальный номер при необходимости

5 голосов
/ 14 октября 2010

Похоже, вы денормализовали 3 куска данных в 1 поле. Вот что в настоящее время содержит ваше строковое поле:

  • StringField char (12): STR-STR - ####

Это то, что вам действительно нужно (например, только имена полей; это помогло бы дать эти значимые имена, но я не знаю, что представляют ваши данные):

  • Str1 char (3): STR
  • Str2 char (3): STR
  • ID int: ####

Теперь вы можете использовать последовательность в поле идентификатора.

Когда вы хотите вернуть исходную строку, вы объединяете содержимое полей Str, Str2 и ID (с разделительными дефисами).

Короче говоря: ваш дизайн базы данных поврежден, и теперь вы платите за него. Мой совет - исправить конструкцию, нормализуя поле идентификатора в 3 отдельных поля. В качестве альтернативы вы обнаружите, что тратите часы на воссоздание встроенных функций базы данных и в итоге получаете решение с ошибками и ужасными проблемами с условиями гонки.


В качестве альтернативы, если PL / SQL разрешает все эти функции:

Создайте таблицу, содержащую следующие поля:

  • Str1 char (3)
  • Str2 char (3)
  • CurrentID int

Тогда:

  • Для каждой возможной комбинации идентификаторов STR-STR добавьте запись в базу данных со значением «CurrentID», равным 0.
  • Напишите хранимую процедуру для получения следующего идентификатора. Он заблокирует соответствующую строку на основе переданной пары STR-STR, получит значение в CurrentID, увеличит значение, разблокирует строку и вернет увеличенное значение.
  • Вызывайте процедуру всякий раз, когда вам нужно сгенерировать новый идентификатор.

У вас не будет проблем с параллелизмом, поскольку каждая попытка получить ID будет ждать окончания любых других попыток. У каждой пары STR-STR будет свой счетчик.

1 голос
/ 20 октября 2010

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

Где-то вы, вероятно, запускаете такой SQL-код:

SELECT MAX(DENORMALIZED_FIELD)
INTO   BADLY_NAMED_VARIABLE
FROM   POORLY_ORGANIZED_TABLE
WHERE  DENORMALIZED_FIELD LIKE 'ABC-XYZ%';

Затем вы, вероятно, разделите переменную с помощью SUBSTR, проанализируйте вторую часть с шагом NUMBERэто, и построить новую переменную с новым кодом.

Что вы можете сделать, это добавить предложение FOR UPDATE в оператор SELECT, установив блокировку для рассматриваемых записей.Конечно, вы на самом деле не обновляете их, но по вашему определению вы должны сериализовать операцию.Это неэффективный, не масштабируемый и грязный способ получить то, что вы хотите, но он должен работать.Проверьте документы Oracle, чтобы увидеть все последствия.

1 голос
/ 14 октября 2010

Это можно сделать с помощью уникального индекса на основе функции. Сначала вам нужно добавить столбец в таблицу, чтобы указать, должен ли идентификатор быть уникальным для каждой строки:

alter table ... add (identifier_must_be_unique varchar2(1)
    check (identifier_must_be_unique='Y'));

Теперь создайте уникальный ФБР, в котором содержатся только идентификаторы, которые должны быть уникальными:

create unique index xxx on yyy 
(case when identifier_must_be_unique='Y' then identifier end);

Наконец, в вашей логике генерации идентификатора, когда вам нужно, чтобы идентификатор был уникальным, установите identifier_must_be_unique = 'Y'; иначе оставьте это как нуль). ФБР затем реализует условное ограничение.

1 голос
/ 14 октября 2010

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

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

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

1 голос
/ 14 октября 2010

Теперь есть некоторые проблемы с этим, между получением следующего числа и фиксацией в базе данных существует около 1000 строк кодов.Проблема в том, что 2 человека в течение нескольких секунд создают элемент с одинаковыми критериями, иногда выдается одно и то же число.

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

1 голос
/ 14 октября 2010

Используйте УНИКАЛЬНЫЙ ИНДЕКС для этого столбца или ПЕРВИЧНЫЙ КЛЮЧ!

0 голосов
/ 15 октября 2010

Ну, ни один из ответов на самом деле не решил проблему, в основном потому, что я не передал всю ситуацию должным образом ..

Но в основном я помещаю свою проверку непосредственно перед фиксацией в цикл, обновляю и фиксирую и проверяю снова в том же цикле ... и если он все еще существует, цикл запускается снова, и в этом случае проверка происходит .. Это довольно сложно объяснить, но вероятность дубликата крайне мала (цикл имеет ограничение 100)

0 голосов
/ 14 октября 2010

Возможно, я упускаю что-то очевидное, но я бы предложил нечто похожее на то, что кто-то еще предлагал ранее. Создайте уникальное ограничение для столбца таким образом, чтобы при попытке сохранить запись произошел сбой, если какой-то другой процесс уже использовал этот пользовательский идентификатор. Затем вы можете перехватить это исключение в PL / SQL и реализовать некоторую логику повторных попыток, пока не сможете успешно вставить уникальный сгенерированный идентификатор в базу данных.

0 голосов
/ 14 октября 2010

У вас неверная модель данных.Но я предполагаю, что исправить это не жизнеспособный вариант.Если вашей самой большой проблемой являются дубликаты ключей, когда вы запускаете два быстрых выбора для таблицы, содержащей значения последовательности (вы должны использовать последовательности здесь ... но вы это знаете) ... тогда вы можете попробовать использовать "select ... для обновления ", который заблокирует строку для сеанса, получающего к нему доступ.Будьте осторожны, эта логика может привести к задержке в приложении.

...