首先 - 过去几天我一直在寻找一个没有运气的答案。意思是我已经看到了答案,尝试过它们并且仍然会出错。我注意到查看代码会让我感到恶心。所以非常感谢任何帮助。
我有三个表CLIENTS,PROJECTS和PROJECT_NOTES。项目只能分配给一个客户,但客户可以有多个项目。项目可以有多个注释,但该注释只能分配给一个项目。
我想要做的是,如果我'废弃'一个客户端,那么与该客户端相关的所有项目也会被“删除”。然后,那些刚被破坏的项目的所有项目说明也会被“删除”。
我也可以“删除”一个项目,该项目将“删除”所有相关的项目备注也将被“删除”。
我假设我需要使用外键和更新级联 - 我一直在尝试。我想我的主键设置方式搞砸了 - 但这对我来说是新的,所以我可能错了。
我可以毫无问题地创建表格。我可以将数据插入到所有表中而没有问题。但是,我在CLIENTS或PROJECTS表上运行更新查询,我不再能够将数据插入除CLIENTS之外的任何表中。
以下是用于创建表格的代码:
CREATE TABLE clients (
clientID INT UNSIGNED NOT NULL AUTO_INCREMENT,
companyName VARCHAR(128),
clientTrash TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (clientID, clientTrash),
INDEX (companyName)
)ENGINE=INNODB;
CREATE TABLE projects (
projectID INT UNSIGNED NOT NULL AUTO_INCREMENT,
clientID INT UNSIGNED NOT NULL,
projectTitle VARCHAR(128),
projectTrash TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (projectID, projectTrash),
INDEX (projectTitle),
FOREIGN KEY (clientID, projectTrash) REFERENCES clients (clientID, clientTrash)
ON DELETE CASCADE
ON UPDATE CASCADE
)ENGINE=INNODB;
CREATE TABLE project_notes (
projectNoteID INT UNSIGNED NOT NULL AUTO_INCREMENT,
projectID INT UNSIGNED NOT NULL,
note TEXT,
projectNoteTrash TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (projectNoteID, projectNoteTrash),
FOREIGN KEY (projectID, projectNoteTrash) REFERENCES projects (projectID, projectTrash)
ON DELETE CASCADE
ON UPDATE CASCADE
)ENGINE=INNODB;
(希望代码格式正确 - 这是我第一次在这里发帖)。
如果有比使用更新级联更好(更简单)的方法,请告诉我。再次感谢您的帮助。
忘记添加:运行更新查询后,在CLIENTS表中设置clientTrash = 1。我将尝试在PROJECTS中插入值,最终出现错误#1452:
无法添加或更新子行:外键约束失败(_clientmanage
。projects
,CONSTRAINT projects_ibfk_1
FOREIGN KEY(clientID
,projectTrash
)REFERENCES {{ 1}}(clients
,clientID
)ON DELETE CASCADE ON UPDATE CASCADE)
答案 0 :(得分:2)
我认为您对引用完整性(外键,级联更新和级联删除)的使用和适用性感到困惑。级联更新和删除是一种在相关表中维护参照完整性的方法,因此根/父主键上的更改将级联到依赖/子行,以便在父ID更改时不会破坏关系。顺便说一下,只有在MySQL中使用InnoDB引擎时才会强制执行引用完整性。
您真正想要的是一种强制执行业务规则的方法,该规则将实体的状态级联到依赖实体。在数据库级别使用的工具是一个触发器,它是一种特殊的存储例程,只要插入,更新或删除行,就会执行该例程。您可以在AFTER UPDATE
和clients
表格上设置projects
触发器,以级联垃圾状态。
从设计和架构的角度来看,这种行为通常是在业务逻辑代码上处理,而不是在数据库级别处理。
答案 1 :(得分:0)
将您的密钥设置为ID +垃圾意味着您可以拥有2个项目 - 其中一个ID为id + trashed,具有相同的ID + not_trashed。
从主键/外键中删除,这应该可以解决您的编辑问题。
我相信(这一点可能是错的)正在发生的事情是,当某些东西被破坏时,主键发生了变化,从而破坏了外键关系。
例:
A(id:4 trashed:0)连接到B(id:17,trashed:0,foreign(id:4:trashed:0))。
当A被“删除”时,它的主键现在是(id:4 trashed:1)
B仍然是(id:17,trashed:0,foreign(id:4:trashed:0))
B的外来(id:4:被删除:0)不再存在(或不再存在),这是不好的并且会导致问题。
执行此操作的另一种方法(可能更容易)是在实现中设置存储过程或事务,按照一系列较小的步骤执行您希望它执行的操作。
答案 2 :(得分:0)
我认为这就是你想要的,使用向下级联'trash = 1'设置的触发器:如果客户端被移动到垃圾箱,那么项目和笔记也是如此。但是,如果从垃圾箱中恢复客户端,则不会自动恢复注释,并且当客户端不是时,您可以将项目移动到trahs。
(这只处理更新 - 你仍然需要确保一个人不会为被破坏的客户端插入非破坏的项目。我仍然在努力解决这个问题。如果我找到它,我会发布更新。)< / p>
DELIMITER //
CREATE TRIGGER trgClientToTrash
AFTER UPDATE ON clients
FOR EACH ROW
BEGIN
IF NEW.clientTrash = 1 THEN
UPDATE projects SET projects.projectTrash = NEW.clientTrash
WHERE projects.clientID = NEW.clientID;
END IF;
END;
//
编辑添加:插入触发器
delimiter //
create trigger insProjectTrash
before insert on projects
for each row
begin
declare trash numeric;
select greatest(clientTrash, new.projectTrash) into trash
from clients where clientID = new.clientID;
set new.projectTrash = trash;
end;
//
它处理插入项目和更新客户端,将它们转换为插入项目注释和更新项目应该是类似的。