我应该复制数据还是使用复杂的查询

时间:2017-07-12 10:51:04

标签: mysql sql database-design

我正在尝试从头开始创建一个论坛,主要是为了练习,以后可能会重复使用 我目前正在研究数据库,有一件事让我烦恼。我有一个表格线程,必须包含第一篇文章,作者等的引用...但我不知道如何正确地做到这一点。
(1)我的第一个想法是“让更少的dupplicates成为可能”,所以我想我会有一个基本的Thread表

CREATE TABLE Thread(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    title VARCHAR(40) NOT NULL,
    category VARCHAR(40) NOT NULL,
);

和表格帖子

CREATE TABLE Post(
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    author VARCHAR(40) NOT NULL,
    thread INT NOT NULL,
    creationDate DATETIME NOT NULL,
    content TEXT NOT NULL,
    PRIMARY KEY (id),
    CONSTRAINT fk_Post-author_UserProfile-name FOREIGN KEY (author)
    REFERENCES UserProfile(name) ON UPDATE CASCADE,
    CONSTRAINT fk_Post-thread_Thread-id FOREIGN KEY (thread) 
    REFERENCES Thread(id)
);

所以,当我想知道一个帖子的第一篇文章时,我会检索该日期中按日期排序的所有帖子并取第一篇。 EZ钱!但后来我想,如果我想得到一个线程,它的作者和创作日期(这对我来说似乎非常合理)知道它的标题,那将是一个带有连接的巨大查询,可能会非常缓慢和繁琐。

SELECT Thread.title, posts.creator, posts.creationDate
FROM Thread
JOIN (
    SELECT Post.thread as thread, Post.creationDate, UserProfile.name AS creator
    FROM Post
    JOIN UserProfile
        ON Post.author = UserProfile.id
) AS posts
    ON Thread.id = posts.thread
WHERE Thread.title = 'Boy, that''s one hell of a query you''ve got here !'
ORDER BY creationDate
LIMIT 1;

(我不确定这个问题,我是新手)

(2)“好吧那么!只需将'firstPost'列添加到Thread表中”你会告诉我,但那么为什么不复制作者参考?这会停在哪里?
我的猜测是选项(1)太重了,也许和选项(2)只是对第一篇文章的引用可能是更好的选择,但不能直接将作者引用复制到Thread表上。如果我没有误会,应该给出类似下面的内容,但是添加了一个列。

SELECT Thread.title, UserProfile.name, Post.creationDate
FROM Thread
JOIN Post
    ON Thread.firstPost = Post.id
JOIN UserProfile
    ON Post.author = UserProfile.id
WHERE Thread.title = 'Boy, that''s one hell of a query you''ve got here !';

您如何看待它,请记住,作为一个教学项目,我不仅希望它能够发挥作用,而且还要“干净”?

2 个答案:

答案 0 :(得分:1)

你的初始直觉是正确的 - 不要复制数据,除非你真的需要。你的查询真的不复杂。并且,维护重复数据非常复杂。

您的第一个查询应该是:

SELECT t.title, up.name as creator, p.creationDate
FROM Thread t JOIN 
     Post p
     ON p.thread = t.id JOIN
     UserProfile up
     ON p.author = up.id
WHERE t.title = 'Boy, that''s one hell of a query you''ve got here !'
ORDER BY p.creationDate
LIMIT 1;

注意:

  • 在MySQL中,除非您需要,否则不要在FROM子句中使用子查询。你并不需要。
  • 表别名使查询更易于编写和阅读。
  • 另外,如果您正在学习SQL,我建议您为主键和外键指定相同的名称。因此,threadId代替idthread

答案 1 :(得分:1)

我理解你的想法。是的,一个帖子由一系列帖子组成,但第一篇帖子与其他帖子不同,因为这是主题的内容,而其他帖子仅仅是对第一篇帖子的回复。

从这个角度来看,我们可以争辩说,不仅帖子属于一个帖子(因此一个帖子表必须包含线程ID),而且该帖子也是一个(第一个)帖子,因此应该包含帖子ID。这是一个可能的模型,但导致了一个鸡蛋和鸡蛋问题:每个帖子必须属于一个现有的帖子,每个帖子必须引用一个现有的帖子。可以通过编写没有post引用的线程来解决这个问题,然后编写第一篇文章,然后更新线程。哪个会起作用,但不是一个优雅的解决方案。另一种选择是在一个事务中写入两个记录(已经包含帖子ID的线程;包含线程ID的帖子),并且约束仅适用于提交。这称为延迟约束,仅在少数DBMS中可用。我很确定,MySQL不支持这些。

但是当然你可以保持简单,说一个帖子和数据模型中的另一个帖子一样好,一个恰好是第一个。这就是你已经拥有的并没有错。这是一个有效的模型。但是,我建议使用带有coumpound键和帖子号而不是ID的模型:

  • 主题( thread_id ,title,category_id)
  • 发布( thread_id,post_num ,author_id,creation_time,内容)

其中主键为粗体。第一篇文章当然是post_num 1,第二篇文章是#2,依此类推。这将使得检索线程的第一个帖子变得非常简单。

但是还有另一件事想到了。在许多论坛中,线程不包含帖子的,而是帖子的。这意味着:除第一个帖子外,帖子总是指其回复的另一个帖子。这可能导致以下模型:

  • 主题( thread_id ,title,category_id)
  • 发布( thread_id,post_num ,author_id,creation_time,content,parent_post_num)

因此,post表中的记录可以引用post表中的另一条记录(它回复的帖子)。复合键带来了另一个优势:post_numparant_post_num只与thread_id结合使用。所以约束FOREIGN KEY (thread_id, parent_post_num) REFERENCES (thread_id, post_num)会很好用,因为帖子在外部线程中没有父帖子(它可以使用非复合键)。