开发者

MySQL - on update cascade (multiple tables)

First - I've been looking for an answer on this for the past few days with no luck. Meaning I've seen answers, tried them and still get errors. I'm to the point that l开发者_JS百科ooking at the code makes me sick. So any help is greatly appreciated.

I have three tables CLIENTS, PROJECTS and PROJECT_NOTES. A project can only be assigned to one client, but clients can have multiple projects. A project can have multiple notes but that note can only be assigned to one project.

What I'm looking to do is if I 'trash' a client then all projects associated with that client get 'trashed' as well. Then all project notes for those projects that were just trashed get 'trashed' as well.

I can also just 'trash' a project which will 'trash' all associated project notes to be 'trashed' as well.

I assume I need to use foreign keys and on update cascade - which I've been trying. I think I'm screwing something up with the way my primary keys are set up - but this is new to me so I could be wrong.

I can create the tables with no problem. I can insert data into all tables without and problem. However, one I run an update query on either the CLIENTS or PROJECTS table I'm not longer able to insert data into any table except for CLIENTS.

Here's the code used to create the tables:

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;

(Hopefully that code was formatted correctly - It's my first time posting here).

If there's a better (easier) method than using on update cascade please let me know. And again thank you for any and all help.

Forgot to add: After running an update query to set clientTrash = 1 in the CLIENTS table. I'll try to insert values into PROJECTS and end up with error #1452: Cannot add or update a child row: a foreign key constraint fails (_clientmanage.projects, CONSTRAINT projects_ibfk_1 FOREIGN KEY (clientID, projectTrash) REFERENCES clients (clientID, clientTrash) ON DELETE CASCADE ON UPDATE CASCADE)


I think you are confused about the use and applicability of referential integrity (foreign keys, cascade updates and cascade deletes). Cascade updates and deletes are a way to maintain referential integrity in your related tables, so changes on the root/parent primary keys are cascaded down to the dependent/child rows, so that the relation is not broken if the parent Id changes. By the way, referential integrity is only enforced when using InnoDB engine in MySQL.

What you really want is a way to enforce a business rule, that cascades the state of an entity down to the dependent entities. The tool to use at the database level is a trigger, which is a special kind of stored routine that executes whenever a row is inserted, updated, or deleted. You can setup an AFTER UPDATE trigger on the clients and projects tables to cascade the trash status.

From a design and architecture point of view, this kind of behavior is typically handled on the business logic code, not at the database level.


Setting up your keys as id+trash means that you can have 2 of an item - one with id+trashed one with the same id+not_trashed.

Get the trashed out of the primary/foreign keys and that should fix your issues of editing.

I believe (could be wrong about this bit) what is happening is that when something gets trashed the primary key changes which breaks the foreign key relationship.

Example:
A (id:4 trashed:0) is connected to B (id:17, trashed:0, foreign(id:4: trashed:0)).
When A is "trashed" it's primary key is now (id:4 trashed:1).
B is still (id:17, trashed:0, foreign(id:4: trashed:0)).
B's foreign(id:4: trashed:0) no longer exists (or would no longer exist) which is bad and causes problems.

Another way of doing this (which may be easier) is to either set up a stored procedure or transaction in implementation that does what you want it to do in a series of smaller steps.


I think this is what you want, using triggers that cascade the 'trash=1' setting downward: if a client is moved to trash, so are the projects and the notes. However, if you resurrect a client from the trash, the notes are not automatically restored, and you can move a project to the trahs when a client is not.

(This only handles the update - you still need to ensure that one doesn't insert non-trashed projects for trashed clients. I am still struggling with that one. I'll post an update if I find it.)

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;
 //

Edited to add: the insert trigger

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; 
//

This handles inserting projects and updating clients, converting them to inserting project notes and updating projects should be similar.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜