如何创建一个表,其行引用 2 个现有表中的 1 个(且仅 1 个)?

Posted

技术标签:

【中文标题】如何创建一个表,其行引用 2 个现有表中的 1 个(且仅 1 个)?【英文标题】:How do I create a table whose rows reference 1 (and only 1) of 2 existing tables? 【发布时间】:2016-01-13 08:09:32 【问题描述】:

这是我的情况:我有两个用

创建的表
CREATE DATABASE JsPracticeDb; 
/* Create tables corresponding to the problems, solutions to 
   problems, and ratings of problems or solutions */
CREATE TABLE Problems ( 
    id INT PRIMARY KEY NOT NULL, 
    prompt_code VARCHAR(3000),
    test_func_code VARCHAR(3000),
    test_input_code VARCHAR(3000)
);
CREATE TABLE Solutions (
   id INT PRIMARY KEY NOT NULL, 
   problem_id INT,
   solver_name VARCHAR(50),
   code VARCHAR(3000),
   FOREIGN KEY (problem_id) REFERENCES Problems(id) ON DELETE CASCADE,
);

我正在考虑为Solutions 创建一个评级表,我将其写为

CREATE TABLE Ratings (
    id INT PRIMARY KEY NOT NULL, 
    solution_id INT,
    stars TINYINT,
    FOREIGN KEY (solution_id) REFERENCES Solutions(id) ON DELETE CASCADE
);

但后来我意识到我实际上可能也想给Problems 评分。在我看来,“蛮力”解决方案是

CREATE TABLE SolutionRatings (
    id INT PRIMARY KEY NOT NULL, 
    solution_id INT,
    stars TINYINT,
    FOREIGN KEY (solution_id) REFERENCES Solutions(id) ON DELETE CASCADE
);
CREATE TABLE ProblemRatings (
    id INT PRIMARY KEY NOT NULL, 
    problem_id INT,
    stars TINYINT,
    FOREIGN KEY (problem_id) REFERENCES Problems(id) ON DELETE CASCADE
);

但我的编程直觉表明,我使用复制粘贴来编写几乎相同的两段代码这一事实存在问题。但是,我想不出任何使用交集表或类似也允许我进行级联删除的替代解决方案。例如,我知道我可以做到

CREATE TABLE RatedTables (
    id TINYINT PRIMARY KEY NOT NULL,
    table_name VARCHAR(9)
);
INSERT INTO RatedTables (table_name) VALUES ('Problems','Solutions');
CREATE TABLE Ratings (
    id INT PRIMARY KEY NOT NULL,
    rated_table_id TINYINT NOT NULL, 
    stars TINYINT,
    FOREIGN KEY (rated_table_id) REFERENCES RatedTables(id)
);

但是我将如何做到这一点,如果Solution 和相应的Ratings 被删除,那么这些评级也会太?????

【问题讨论】:

如何使用外键约束 FOREIGN KEY(problem_id) REFERENCES Problems(id) ON DELETE CASCADE 将另一列添加到 Ratings 表 problem_id int 【参考方案1】:

您基本上有两个选择,但这是返回并查看您的数据库结构的好机会。

第一种选择是这样做:

CREATE TABLE potential_link1 (
    id int primary key,
    ...
);
CREATE TABLE potential_link2 (
    id int primary key,
    ....
);
CREATE TABLE ratings (
    id int primary key,
    potential_link1 int references potential_link1(id) on delete cascade,
    potential_link2 int references potential_link2(id) on delete cascade,
    ....
    check(potential_link1 is null or potential_link2 is null),
    check(potential_link2 is not null or potential_link1 is not null)
);

这可行,但您可以看到它有点复杂。

第二种可能性是,由于存在明显的情况 a 依赖于 b 和 c 的联合,那么您可能会考虑是否可以重构您的数据库结构以反映这一点,因此您只需要一个表来链接。

【讨论】:

在 OP 的情况下,他们已经在 potential_link1potential_link2 之间有一个带有 on delete cascade 的 FK 引用。所以我认为你不能用on delete cascaderatings 中创建这些外键(因为SQL Server 的over-eager 循环检测器)【参考方案2】:

两张看起来如此相似的桌子并没有错。它们包含不同的内容,无论是针对问题还是解决方案,您都不想选择所有三星级评级 - 您将始终使用解决方案评级问题评级。


但是在一个表中同时包含两个评分也没有错,当您希望评分表现相同时,无论是在问题还是解决方案上(例如,两者都应该有 1 到 5 星,两者都可以有),这也是一个好主意评论不再超过 200 个字符,...)。

这可以通过简单地为评级表提供一个问题_id 和一个解决方案_id 以及表上的外键并始终填充一个或另一个来完成。使用自然键,感觉会更加自然:

problem(problem_no, data) solution(problem_no, solution_no, data) rating(problem_no, solution_no, data)

在两个父表上都有rating.solution_no 可为空和外键。

【讨论】:

以上是关于如何创建一个表,其行引用 2 个现有表中的 1 个(且仅 1 个)?的主要内容,如果未能解决你的问题,请参考以下文章

如何将现有表中的 7000 条记录中的前 1000 条记录复制到其他新表中

SQL Server:交叉引用一个表中的多个列与另一个表中的多个列

将数据透视结果附加(插入)到现有 SQL 表中

汇总SQL中2个不同表中的3列

数据库表中的循环引用

在包含记录的现有表中,如何创建一个新的 datetime2(2) 列并使用基于另一列的值填充它?