MySQL:当“0”时,如何仅将其中一个参数设置为null;关于删除“;

MySQL:当“0”时,如何仅将其中一个参数设置为null;关于删除“;,mysql,Mysql,我有一个数据库结构,其中的菜单选项可能指向某个页面,也可能不指向该页面: menu( 'en', 'anypage') => page( 'en', 'anypage') menu( 'en', NULL) => Nothing 底部的MySQL代码可以正确地用于创建和更新,但不能用于删除:我希望在删除页面时,menu.Link设置为NULL,menu.Language保持不变 FOREIGN KEY (Language, Link) REFERENCES page(Languag

我有一个数据库结构,其中的菜单选项可能指向某个页面,也可能不指向该页面:

menu( 'en', 'anypage') => page( 'en', 'anypage')
menu( 'en', NULL) => Nothing
底部的MySQL代码可以正确地用于创建和更新,但不能用于删除:我希望在删除页面时,menu.Link设置为NULL,menu.Language保持不变

FOREIGN KEY (Language, Link) REFERENCES page(Language, Link) 
    ON UPDATE CASCADE ON DELETE SET Link=NULL
但这当然行不通

所以我的问题是:如何只更新菜单。删除页面时链接到NULL?

-- DROP TABLE FOR MULTIPLE TESTS
DROP TABLE IF EXISTS menu;
DROP TABLE IF EXISTS page;
DROP TABLE IF EXISTS language;


-- CREATE TABLES
CREATE TABLE language(
    Id CHAR(2) PRIMARY KEY,
    Name VARCHAR(20)
) ENGINE=innoDB;

CREATE TABLE page(
    Language CHAR(2) NOT NULL,
    Link VARCHAR(30) NOT NULL,
    PRIMARY KEY (Language, Link),
    FOREIGN KEY (Language) REFERENCES language(Id) 
        ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE = InnoDB;

CREATE TABLE menu(
    Id INT PRIMARY KEY AUTO_INCREMENT,
    Language CHAR(2) NOT NULL,
    Link VARCHAR(30) DEFAULT NULL,
    FOREIGN KEY (Language) REFERENCES language(Id) 
        ON UPDATE CASCADE ON DELETE CASCADE
    /*, FOREIGN KEY (Language, Link) REFERENCES page(Language, Link) 
        ON UPDATE CASCADE ON DELETE SET LINK=NULL*/
) ENGINE=InnoDB;


-- INSERT FOR TESTS
INSERT INTO language (Id, Name) VALUES('en','English');
INSERT INTO page (Link, Language)VALUES('test', 'en');
INSERT INTO menu (Language, Link)VALUES('en', 'test');

UPDATE page SET Link='test2' WHERE Link='test';
DELETE FROM page WHERE Link='test2';
根据@Bohemian答案编辑的最终解决方案:

/* Page deletion*/
DELIMITER //
CREATE TRIGGER PAGE_DELETE
AFTER DELETE ON page
FOR EACH ROW
BEGIN
    UPDATE menu SET
    menu.Link = NULL
    WHERE menu.Link = OLD.Link AND menu.Language = OLD.Language;
END;//

/* Page update*/
CREATE TRIGGER PAGE_UPDATE
AFTER UPDATE ON page
FOR EACH ROW
BEGIN
    UPDATE menu SET
    menu.Link = NEW.Link
    WHERE menu.Link = OLD.Link AND menu.Language = OLD.Language;
END;//

/* Menu creation*/
CREATE TRIGGER MENU_INSERT
BEFORE INSERT ON menu
FOR EACH ROW
BEGIN
    declare rowCount int default 1;
    if (NEW.Link IS NOT NULL) then
        SELECT count(Id) into rowCount FROM page 
        WHERE NEW.Link=page.Link AND NEW.Language=page.Language;
    end if; 

    if (rowCount=0 ) then
        signal sqlstate '23000' set message_text = 'Error: menu.Link must point to an existant page.';
    end if;
END;//

/* Menu update*/
CREATE TRIGGER MENU_UPDATE
BEFORE UPDATE ON menu
FOR EACH ROW
BEGIN
    declare rowCount int default 1;
    if (NEW.Link IS NOT NULL) then
        SELECT count(Id) into rowCount FROM page 
        WHERE NEW.Link=page.Link AND NEW.Language=page.Language;
    end if; 

    if (rowCount=0 ) then
        signal sqlstate '23000' set message_text = 'Error: menu.Link must point to an existant page.';
    end if;
END;//
DELIMITER ;
创建触发器:

DELIMITER //
CREATE TRIGGER PAGE_DELETE
AFTER DELETE ON PAGE
FOR EACH ROW
BEGIN
    UPDATE MENU SET
    PAGE_ID = NULL
    WHERE PAGE_ID = OLD.ID;
END;//
DELIMITER ;

我是如何做到这一点的:

-- DROP TABLE FOR MULTIPLE TESTS
DROP TABLE IF EXISTS menu;
DROP TABLE IF EXISTS page;
DROP TABLE IF EXISTS language;


-- CREATE TABLES
CREATE TABLE language(
    Id CHAR(2) PRIMARY KEY,
    Name VARCHAR(20)
) ENGINE=innoDB;

CREATE TABLE page(
    Language CHAR(2) NOT NULL,
    Link VARCHAR(30) NOT NULL,
    PRIMARY KEY (Language, Link),
    FOREIGN KEY (Language) REFERENCES language(Id) 
        ON UPDATE CASCADE ON DELETE CASCADE,
    KEY page_link (Link)
) ENGINE = InnoDB;

CREATE TABLE menu(
    Id INT PRIMARY KEY AUTO_INCREMENT,
    Language CHAR(2) NOT NULL,
    Link VARCHAR(30) DEFAULT NULL,
    FOREIGN KEY (Language) REFERENCES language(Id) 
        ON UPDATE CASCADE ON DELETE CASCADE,
    FOREIGN KEY (Link) REFERENCES page(Link) 
        ON UPDATE CASCADE ON DELETE SET NULL
) ENGINE=InnoDB;


-- INSERT FOR TESTS
INSERT INTO language (Id, Name) VALUES('en','English');
INSERT INTO page (Link, Language)VALUES('test', 'en');
INSERT INTO menu (Language, Link)VALUES('en', 'test');

UPDATE page SET Link='test2' WHERE Link='test';
DELETE FROM page WHERE Link='test2';
SELECT * FROM menu;

您是否尝试过使用ON DELETE SET NULL而不是ON DELETE SET LINK=NULL?SET NULL不会编译,因为菜单。语言定义为not NULLE。我是否还需要创建一个触发器以进行更新?或者有一个“外键”在删除时不检查?您永远不应该更新表的键,并且没有执行此操作的有效要求,因此不-您不需要一个外键进行更新,因为您永远不会更新该键。为此,页面应该有一个名为ID和auto-incremented的代理键,外键应该使用它,通常称为“table”\u ID,比如
page\u ID
。好的,是和否:完整的sql代码有一个页面的auto-incremented ID(为了简化示例,我删除了很多代码)。但是菜单必须指向(lang+link)以允许更快的查询:大多数时候我只需要“link”,所以请求“menu”表就足够了,否则我还需要查询页面,它是一个巨大的表。听起来像是过早的优化。使设计正确且易于使用。您可能会发现,您试图实现的性能提升并不显著,但却会导致各种编程问题。您能否解释一下,添加键(Link)如何允许菜单外键“设为空”吗?哦,好的,我明白了,外键仅用于Link,但是如果两种语言有相同的链接会发生什么呢?在这里阅读这个答案:,似乎需要在字段上有索引,然后再放置外键。关于具有相同链接的语言,你总是可以这样做:更改表菜单添加唯一键lang_链接(语言,链接),但这意味着菜单(fr,test)可能指向页面(en,test)