我有一个名为categories
的表,其中包含以下字段:
users.id
,当前类别的创建者)categories.id
,父类别ID的ID)在此表中,我有一些可供所有人访问的记录。
对于这些记录,user_id
和category_id
字段均为NULL
。
此外,用户可以创建自己的记录(其中user_id
字段不是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;
答案 0 :(得分:1)
不幸的是,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 here。 (如果删除最后一个INSERT查询,则SELECT查询将按预期工作)
此外,为避免一些开销(触发器中的SELECT查询),当category_id不为NULL时,您可以对user_id强制执行NULL(使用上述触发器)。如果category_id不为NULL,则只需从父行(或根行(父级的父行...等),如果嵌套)中获取user_id。