Добавление ограничений в базу данных MySQL, чтобы гарантировать уникальность

0

Моя таблица SQL имеет следующий DDL

CREATE TABLE 'new_table' (
  'id' int(11) NOT NULL AUTO_INCREMENT,
  'family_id' int(11) NOT NULL,
  'name' varchar(45) NOT NULL,
  PRIMARY KEY ('id')
  ) 

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

{
  "family_id" : 1,
  "names": ["name1", "name2"]
}

Идентификатор генерируется посредством автоматического увеличения из MySQL.

Таким образом, вышеупомянутый JSON, наконец, запускает два оператора insert:

  • insert (family_id, name) значения (1, name1)
  • insert (family_id, name) значения (1, name2)

Проблема возникает, когда новый запрос приходит с family_id, который существует в таблице. Это не должно быть разрешено, и я делаю запрос для поиска, существует ли family_id или нет. Если он существует, возникает исключение. Как я могу избежать этого запроса? При необходимости схема таблицы может быть изменена. Было бы хорошо, если бы он мог добавить что-то вроде "идентификатора запроса" или руководства, чтобы установить уникальность для каждого запроса?

Все данные должны быть в одной таблице.

Ниже пример таблицы с некоторыми данными

Изображение 174551

(из комментария) Я не могу создать вторую таблицу. Все должно храниться в одном столе.

  • 0
    Вы написали, что не можете разделить это на две таблицы. Вы можете использовать триггеры? Для этого есть изящное решение.
  • 0
    @NB, да можно использовать триггеры.
Теги:
database

4 ответа

6
Лучший ответ

Вы должны нормализовать свою схему и использовать две таблицы.

Семья и (я предполагаю) Личность. Затем вы можете использовать ограничение UNIQUE для family_id и добавить family_id как внешний ключ в таблицу Person.

  • 0
    Я не могу создать вторую таблицу. Все должно храниться в одном столе. Извините, я должен упомянуть об этом в вопросе. Однако я понял вашу точку зрения.
  • 2
    @cateof в этом случае вы не можете использовать традиционное ограничение и не можете избежать проверки перед вставкой, если идентификатор семейства уже существует.
Показать ещё 4 комментария
3

Вам нужны две таблицы.

CREATE TABLE Families (
    family_id MEDIUMINT UNSIGNED  AUTO_INCREMENT,
    ...
    PRIMARY KEY(family_id)
    );

CREATE TABLE FamilyNames (
    family_id MEDIUMINT UNSIGNED,   -- not auto-inc here
    name VARCHAR(66) NOT NULL,
    ...
    PRIMARY KEY(family_id, name)    -- note "composite"
    );

PRIMARY KEY - UNIQUE KEY - KEY.

Вы говорите, что вы не можете добавить вторую таблицу. Но почему? Вы упомянули о необходимости создания конкретного JSON? Не может ли это быть сделано просто через JOIN из двух таблиц, если это необходимо?

2

Если вы не можете создать вторую таблицу для правильного моделирования ограничения, тогда вам придется прибегать к сериализации вставок:

  1. LOCK TABLES new_table WRITE;
  2. Используйте SELECT чтобы проверить, существует ли идентификатор семейства в таблице.
  3. Если идентификатор семьи отсутствует, INSERT свои новые данные.
  4. UNLOCK TABLES;

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

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

0

Это обходной путь для проблемы с дизайном, но я решил, что могу опубликовать его. Вы можете создать такой запрос, как:

INSERT INTO 'new_table' ('family_id', 'name')
SELECT * FROM (
    SELECT 1, 'name1'
    UNION ALL
    SELECT 1, 'name2'
) x
LEFT JOIN 'new_table' n ON n.family_id = 1
WHERE n.family_id IS NULL

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню