Как это сделать с помощью внешних ключей? - PullRequest
0 голосов
/ 17 октября 2018

У меня есть таблица с именем categories, со следующими полями:

  • id (первичный ключ, идентифицирует категории)
  • user_id (внешний ключ, users.id,создатель текущей категории)
  • category_id (внешний ключ, categories.id, идентификатор идентификатора родительской категории)

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

Для этих записей поля user_id и category_id имеют значение NULL.

Кроме того, пользователи могут создавать свои собственные записи (где *Поле 1022 * не NULL) , но каждый пользователь может получить доступ только к тем, которые он сам создал.

Каждая запись должна соответствовать следующим условиям:

  • user_id значения должны быть действительными
  • , если задано category_id, то это должно быть допустимым categories.id, и данная запись должна быть создана данным пользователем
  • значение category_idможет быть categoies.id, где user_id равно NULL

Как я могу это сделать?Я думаю, что мне нужно больше внешних ключей для этого, но не знаю, как это сделать.

Некоторые примеры, которые могут помочь вам понять мою проблему:

Допустим, у меня есть следующеезаписи в таблице categories:

+--------+-------------+-----------------+
|   id   |   user_id   |   category_id   |
+--------+-------------+-----------------+
|   1    |     NULL    |      NULL       |
+--------+-------------+-----------------+
|   2    |     NULL    |      NULL       |
+--------+-------------+-----------------+
|   3    |      1      |        1        |
+--------+-------------+-----------------+
|   4    |      2      |        1        |
+--------+-------------+-----------------+

и, скажем, я хочу вставить эти записи:

+-------------+-----------------+----------------------------------------------------------------+
|   user_id   |   category_id   | is it insertable?                        
+-------------+-----------------+----------------------------------------------------------------+
|   1         |     NULL        | yes, because the value of the user_id is valid id              |
+-------------+-----------------+----------------------------------------------------------------+
|   1         |     1           | yes, because the record with id of 1 is created by NULL        |
+-------------+-----------------+----------------------------------------------------------------+
|   1         |     3           | yes, because the record with id of 3 is created by user #1     |
+-------------+-----------------+----------------------------------------------------------------+
|   1         |     4           | no, because the record with id of 4 is created by another user |
+-------------+-----------------+----------------------------------------------------------------+

Таблица категорий:

CREATE TABLE `categories` (
  `id` int(11) NOT NULL,
  `user_id` int(11) DEFAULT NULL,
  `category_id` int(11) DEFAULT NULL,
  `name` varchar(150) NOT NULL,
  `type` enum('income','expense') NOT NULL DEFAULT 'income',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `categories`
  ADD PRIMARY KEY (`id`),
  ADD UNIQUE KEY `category_id` (`category_id`,`user_id`,`name`),
  ADD KEY `user_id` (`user_id`);

ALTER TABLE `categories`
  ADD CONSTRAINT `categories_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  ADD CONSTRAINT `categories_ibfk_2` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

1 Ответ

0 голосов
/ 17 октября 2018

К сожалению, внешние ключи MySQL могут зайти так далеко.Это немного продвинутой логики.Могу ли я порекомендовать использовать триггер MySQL?

DELIMITER $$
CREATE PROCEDURE `check_categories_user_id`(IN p_category_id INT(11), IN p_user_id INT(11))
BEGIN
    IF (p_category_id IS NOT NULL) THEN

        SET @other_user_id = (SELECT user_id
           FROM categories
           WHERE id = p_category_id);              

        IF (p_user_id <> @other_user_id) THEN
            SIGNAL SQLSTATE '45000'
                SET MESSAGE_TEXT = 'check constraint on categories.user_id failed';
        END IF;
   END IF;
END$$

CREATE TRIGGER `categories_before_update` BEFORE UPDATE ON `categories`
FOR EACH ROW
BEGIN
    CALL check_categories_user_id(new.category_id, new.user_id);
END$$


CREATE TRIGGER `categories_before_insert` BEFORE INSERT ON `categories`
FOR EACH ROW
BEGIN
    CALL check_categories_user_id(new.category_id, new.user_id);
END$$   
DELIMITER ;

См. Dbfiddle здесь .(Если вы удалите последний запрос INSERT, запрос SELECT будет работать как положено)

Кроме того, чтобы избежать некоторых издержек (запрос SELECT в триггере), вы можете просто принудительно установить NULL для user_id, когда category_id IS NOT NULL (Использование триггеров, как указано выше).Если category_id НЕ НЕДЕЙСТВИТЕЛЕН, вы просто извлечете user_id из родительской строки (или из корневой строки (родительская строка родителя ... и т. Д.), Если она вложена).

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