3

我有 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”的外键约束,它们引用了自己的表。是否可以只允许“M”在“FatherID”的性别列中的“M”和“F”用于“MotherID”的外键?是否可以禁止母亲/父亲引用同一行?

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

4

3 回答 3

3

我相信 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         
于 2012-09-02T16:44:51.820 回答
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');
于 2012-09-02T16:58:48.247 回答
1

要应用这种类型的约束,您需要使用可以完成工作的触发器

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

于 2012-09-02T16:52:10.773 回答