【问题标题】:Foreign key for Father to be male, Mother to be female and you can't be your own mother/father外键父亲为男性,母亲为女性,您不能成为自己的母亲/父亲
【发布时间】:2012-09-02 16:23:35
【问题描述】:

我有 3 张桌子:

CREATE TABLE "Names" (
"Name" TEXT(20) NOT NULL,
"Gender" TEXT(20) NOT NULL,
PRIMARY KEY ("Name", "Gender") 
);

CREATE TABLE "Snames" (
"Sname" TEXT(20) NOT NULL,
PRIMARY KEY ("Sname") 
);

CREATE TABLE "People" (
"ID" INTEGER NOT NULL,
"Name" TEXT(20) NOT NULL,
"Sname" TEXT(20) NOT NULL,
"Gender" TEXT(1) NOT NULL,
"FatherID" INTEGER,
"MotherID" INTEGER,
PRIMARY KEY ("ID") ,
CONSTRAINT "Father" FOREIGN KEY ("FatherID") REFERENCES "People" ("ID"),
CONSTRAINT "Mother" FOREIGN KEY ("MotherID") REFERENCES "People" ("ID"),
CONSTRAINT "Sname" FOREIGN KEY ("Sname") REFERENCES "Snames" ("Sname"),
CONSTRAINT "Name" FOREIGN KEY ("Name", "Gender") REFERENCES "Names" ("Name", "Gender")
);

我的问题是“FatherID”和“MotherID”的外键约束,它们引用了它们自己的表。是否可以只允许在“FatherID”的“性别”列中的“M”和“MotherID”的“F”中的外键?是否可以禁止母亲/父亲引用同一行?

基本上:父亲必须是男性。妈妈一定是女性。你不能做你自己的母亲/父亲。

【问题讨论】:

  • 为什么这三个表是分开的?
  • 如果一个人有两个相同性别的父母怎么办;)
  • 我不知道 sqllite,但想象CHECK ("ID" NOT IN ("FatherID", "MotherID")) 应该做最后一点(你不能成为你自己的母亲/父亲)。
  • @Ashley,由于您正在使用自己的名字查找姓氏,因此将其提取到自己的表中并没有真正获得任何好处。
  • @Arkain:您可以将 people.sname 中的值限制为 snames.sname 中的值。

标签: sql database sqlite foreign-keys constraints


【解决方案1】:

我认为 SQLite 不支持包含从其他行动态获取值的表达式的约束,外键除外。

您必须创建触发器来检查父亲和母亲的性别。

使用这个表定义:

CREATE TABLE "People" (
    "ID" INTEGER NOT NULL,
    "Name" TEXT(20) NOT NULL,
    "Sname" TEXT(20) NOT NULL,
    "Gender" TEXT(1) NOT NULL,
    "FatherID" INTEGER,
    "MotherID" INTEGER,
    PRIMARY KEY ("ID") ,
    CONSTRAINT "Father" FOREIGN KEY ("FatherID") REFERENCES "People" ("ID"),
    CONSTRAINT "Mother" FOREIGN KEY ("MotherID") REFERENCES "People" ("ID"),
    CHECK (Gender IN ('M', 'F')),
    CHECK ("ID" NOT IN ("FatherID", "MotherID")));

这可能是 INSERT 触发器(我会让你编写 UPDATE 触发器):

CREATE TRIGGER checkParentIdsOnInsert BEFORE INSERT ON People 
    WHEN new.FatherID IS NOT NULL OR new.MotherID IS NOT NULL
BEGIN
    SELECT CASE     
    WHEN ((SELECT Gender FROM People AS t1 WHERE t1.ID=new.FatherID) = 'F' 
            AND (SELECT Gender FROM People AS t2 WHERE t2.ID=new.MotherID) = 'M')
       THEN RAISE(ABORT, 'Father must be male and mother female') 
    WHEN ((SELECT Gender FROM People AS t3 WHERE t3.ID=new.FatherID) = 'F')
       THEN RAISE(ABORT, 'Father must be male') 
    WHEN ((SELECT Gender FROM People AS t4 WHERE t4.ID=new.MotherID) = 'M')
       THEN RAISE(ABORT, 'Mother must be female') 
    END; 
END;

一些简单的测试:

sqlite> pragma foreign_keys=on;
sqlite> INSERT INTO People (Name, SName, Gender, FatherID, MotherID) VALUES
   ...>     ("Jo", "Blo", "M", NULL, NULL);
sqlite> INSERT INTO People (Name, SName, Gender, FatherID, MotherID) VALUES
   ...>     ("Za", "Bla", "F", NULL, NULL);
sqlite> INSERT INTO People (Name, SName, Gender, FatherID, MotherID) VALUES
   ...>     ("Bad", "Kid", "M", 2, 1);
Error: Father must be male and mother female
sqlite> INSERT INTO People (Name, SName, Gender, FatherID, MotherID) VALUES
   ...>     ("Bad", "Kid", "M", 2, NULL);
Error: Father must be male
sqlite> INSERT INTO People (Name, SName, Gender, FatherID, MotherID) VALUES
   ...>     ("Bad", "Kid", "M", NULL, 1);
Error: Mother must be female
sqlite> INSERT INTO People (Name, SName, Gender, FatherID, MotherID) VALUES
   ...>     ("Good", "Kid", "M", 1, 2);
sqlite> .headers on
sqlite> .mode column
sqlite> SELECT * FROM People;
ID          Name        Sname       Gender      FatherID    MotherID  
----------  ----------  ----------  ----------  ----------  ----------
1           Jo          Blo         M                                 
2           Za          Bla         F                                 
3           Good        Kid         M           1           2         

【讨论】:

    【解决方案2】:

    以下内容应该可以工作,但需要几个多余的列作为外键 (SQL Fiddle)

    CREATE TABLE "People" (
    "ID" INTEGER NOT NULL,
    "Name" TEXT(20) NOT NULL,
    "Sname" TEXT(20) NOT NULL,
    "Gender" TEXT(1) NOT NULL,
    "FatherID" INTEGER NULL,
    "FatherGender" TEXT(1) NULL,
    "MotherID" INTEGER NULL,
    "MotherGender" TEXT(1) NULL,
    PRIMARY KEY ("ID") ,
    UNIQUE ("ID", "Gender"),
    CHECK ("ID" NOT IN ("FatherID", "MotherID")),
    CHECK ("FatherGender" = 'M'),
    CHECK ("MotherGender" = 'F'),
    CONSTRAINT "Father" FOREIGN KEY ("FatherID","FatherGender") REFERENCES "People" ("ID", "Gender"),
    CONSTRAINT "Mother" FOREIGN KEY ("MotherID","MotherGender") REFERENCES "People" ("ID", "Gender")
    );
    
    INSERT INTO "People"
    VALUES(1, 'Adam', '?', 'M', NULL, NULL, NULL, NULL);
    
    INSERT INTO "People"
    VALUES(2, 'Eve', '?', 'F', NULL, NULL, NULL, NULL);
    
    INSERT INTO "People"
    VALUES(3, 'Cain', '?', 'M', 1, 'M', 2, 'F');
    

    【讨论】:

    • 好把戏。它不是 100% 匹配这个问题,但在其他地方很有用。
    【解决方案3】:

    要应用这种类型的约束, 您需要使用 Triggers 来完成这项工作

    已编辑:SQLite 支持触发器。谢谢@catcall

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-16
      • 2021-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-06
      相关资源
      最近更新 更多