Ant*_*neG 9 sql sqlite postgresql database-design many-to-many
我们正在建立一个介绍博客.到数据库课程项目.
在我们的博客中,我们希望能够Labels开启Posts.它Labels本身不存在,只有与a相关时才会存在Posts.这样,Labels任何不使用的都不Posts应该留在数据库中.
不止一个Label可以属于单个Post,而且多个Post可以使用一个Label.
我们正在使用SQLite3(本地/测试)和PostgreSQL(部署).
下面是我们用来创建这两个表的SQL(SQLite3风格)以及关系表:
CREATE TABLE IF NOT EXISTS Posts(
id INTEGER PRIMARY KEY AUTOINCREMENT,
authorId INTEGER,
title VARCHAR(255),
content TEXT,
imageURL VARCHAR(255),
date DATETIME,
FOREIGN KEY (authorId) REFERENCES Authors(id) ON DELETE SET NULL
)
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE IF NOT EXISTS Labels(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) UNIQUE,
-- This is not working:
FOREIGN KEY (id) REFERENCES LabelPosts(labelId) ON DELETE CASCADE
)
Run Code Online (Sandbox Code Playgroud)
LabelPosts(Post[1 ..*] - *之间的关系Label)
CREATE TABLE IF NOT EXISTS LabelPosts(
postId INTEGER,
labelId INTEGER,
PRIMARY KEY (postId, labelId),
FOREIGN KEY (postId) REFERENCES Posts(id) ON DELETE CASCADE
)
Run Code Online (Sandbox Code Playgroud)
Labels当我从LabelPosts表中删除对它的所有引用时,不会从数据库中删除使用SQLite3 .我认为由于Postgres给出的理由,尽管SQLite在没有警告的情况下接受了这个表.
PostgreSQL抱怨labelId内部不是唯一的LabelPosts,这是真的,也是必需的,因为它是多对多的:
pq:S:"ERROR"R:"transformFkeyCheckAttrs"L:"6511"C:"42830"F:"tablecmds.c"
M:"没有唯一约束匹配给定引用表的键""labelposts \""
所以我明白我的约束是错误的.但是我不知道如何正确地做到这一点.
Erw*_*ter 22
我们正在使用SQLite3(本地/测试)和PostgreSQL(部署).
这是在乞求麻烦.您将继续遇到轻微的不兼容性.或者甚至在很久以后,当损坏完成时才注意到它们.不要这样做.也可以在本地使用PostgreSQL.它可以免费用于大多数操作系统.对于参与"数据库课程项目"的人来说,这是一个令人惊讶的愚蠢行为.
在PostgreSQL中使用serial列而不是SQLite AUTOINCREMENT.
使用timestamp(或timestamptz)代替datetime.
不要使用非描述性列名id.永远.这是由半智能中间件和ORM引入的反模式.当您加入几个表时,您最终会得到多个名称列id.那是积极的伤害.
有许多命名样式,但大多数人认为最好将单数术语作为表名.它更短,至少是直观/合乎逻辑的.label不是labels.
正如@Priidu在评论中提到的,您的外键约束是倒退的.这不是辩论,它们完全错了.
一切都放在一起,它可能看起来像这样:
CREATE TABLE IF NOT EXISTS post (
post_id serial PRIMARY KEY
,author_id integer
,title text
,content text
,image_url text
,date timestamp
);
CREATE TABLE IF NOT EXISTS label (
label_id serial PRIMARY KEY
,name text UNIQUE
);
CREATE TABLE IF NOT EXISTS label_post(
post_id integer REFERENCES post(post_id)
ON UPDATE CASCADE ON DELETE CASCADE
,label_id integer REFERENCES label(label_id)
ON UPDATE CASCADE ON DELETE CASCADE
,PRIMARY KEY (post_id, label_id)
);
Run Code Online (Sandbox Code Playgroud)
CREATE OR REPLACE FUNCTION f_trg_kill_orphaned_label()
RETURNS TRIGGER AS
$func$
BEGIN
DELETE FROM label
WHERE label_id = OLD.label_id
AND NOT EXISTS (
SELECT 1 FROM label_post
WHERE label_id = OLD.label_id
);
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
必须在触发前创建触发器功能.
一个简单的DELETE命令可以完成这项工作.不需要第二次查询 - 特别是没有count(*).EXISTS更便宜.
周围没有单引号plpgsql.这是一个标识符,而不是一个值!
CREATE TRIGGER label_post_delaft_kill_orphaned_label
AFTER DELETE ON label_post
FOR EACH ROW EXECUTE PROCEDURE f_trg_kill_orphaned_label();
Run Code Online (Sandbox Code Playgroud)
有没有CREATE OR REPLACE TRIGGER在PostgreSQL的,但.只是CREATE TRIGGER.