假设我有两列:一张表中的
GroupName
,另一张表中的 Username
。如何限制这些列的值,以便添加到一列的任何值在两列中都是唯一的?最好以“不混乱”的方式。
您提到的结构没有简单的方法。当然,有有实现触发器的方法,但是比较麻烦。
替代数据模型解决了这个问题:
create table things (
thingsId int auto_increment primary key,
name text,
type text,
. . .
check (type in ('group', 'user')),
unique (name)
);
这会将每种类型的事物放入单个列中,然后施加唯一约束。
您还可以使用触发器和
SELECT RAISE(FAIL, ...)
来执行此操作:
例如,假设我有一个
Shelf
表,我希望它包含 Book
和 Magazine
。我也不希望 Book
和 Magazine
在书架上有相同的标题。我真的很想对 (Shelf.ShelfID, Book.BookID OR Magazine.MagazineID)
有一个独特的约束,但该语言没有提供这一点。让我们:
创建表(注意:在现实世界的实现中,文档建议在外键上建立索引以提高性能):
CREATE TABLE Shelf (
ShelfID INTEGER PRIMARY KEY,
Title TEXT NOT NULL,
UNIQUE(Title)
) STRICT;
CREATE TABLE Book (
BookID INTEGER PRIMARY KEY,
ShelfID INTEGER NOT NULL,
Title TEXT NOT NULL,
FOREIGN KEY (ShelfID) REFERENCES Shelf(ShelfID) ON DELETE CASCADE,
UNIQUE(Title)
) STRICT;
CREATE TABLE Magazine (
MagazineID INTEGER PRIMARY KEY,
ShelfID INTEGER NOT NULL,
Title TEXT NOT NULL,
FOREIGN KEY (ShelfID) REFERENCES Shelf(ShelfID) ON DELETE CASCADE,
UNIQUE(Title)
) STRICT;
创建视图和触发器(这是神奇的部分):
CREATE VIEW vw_Book_Magazine_Shelf_AllTitles AS
SELECT s.ShelfID, b.Title
FROM Shelf s JOIN Book b ON s.ShelfID = b.ShelfID
UNION ALL
SELECT s.ShelfID, m.Title
FROM Shelf s JOIN Magazine m ON s.ShelfID = m.ShelfID
;
CREATE TRIGGER tr_Book_Insert_CheckUniqueTitle
BEFORE INSERT ON Book
FOR EACH ROW
BEGIN
SELECT RAISE(FAIL, 'title already exists in Shelf')
FROM vw_Book_Magazine_Shelf_AllTitles
WHERE ShelfID = NEW.ShelfID AND Title = NEW.Title;
END;
CREATE TRIGGER tr_Book_Update_CheckUniqueTitle
BEFORE UPDATE ON Book
FOR EACH ROW
BEGIN
SELECT RAISE(FAIL, 'title already exists in Shelf')
FROM vw_Book_Magazine_Shelf_AllTitles
WHERE ShelfID = NEW.ShelfID AND Title = NEW.Title;
END;
CREATE TRIGGER tr_Magazine_Insert_CheckUniqueTitle
BEFORE INSERT ON Magazine
FOR EACH ROW
BEGIN
SELECT RAISE(FAIL, 'title already exists in Shelf')
FROM vw_Book_Magazine_Shelf_AllTitles
WHERE ShelfID = NEW.ShelfID AND Title = NEW.Title;
END;
CREATE TRIGGER tr_Magazine_Update_CheckUniqueTitle
BEFORE UPDATE ON Magazine
FOR EACH ROW
BEGIN
SELECT RAISE(FAIL, 'title already exists in Shelf')
FROM vw_Book_Magazine_Shelf_AllTitles
WHERE ShelfID = NEW.ShelfID AND Title = NEW.Title;
END;
最后,让我们测试一下:
INSERT INTO Shelf(ShelfID, Title) Values(1, 'Shelf Title');
INSERT INTO Book(BookID, ShelfID, Title) VALUES (1, 1, 'Book Title');
INSERT INTO Magazine(MagazineID, ShelfID, Title) VALUES (1, 1, 'Mag Title');
-- this fails
UPDATE Magazine SET Title = 'Book Title' WHERE Title = 'Mag Title';