什么时候可以不使用PRIMARY KEY?

时间:2016-03-17 11:40:07

标签: mysql database database-design

假设我想创建一个简单的数据库,让用户可以创建播放列表并添加多首歌曲。我只是希望能够找到在特定播放列表中添加的歌曲。

歌曲表:

`song_id` INT AUTO_INCREMENT PRIMARY KEY, `song_title` VARCHAR

播放列表表格:

`playlist_id` INT AUTO_INCREMENT PRIMARY KEY, `playlist_title` VARCHAR

什么是最好的选择呢?

  1. 将另一列添加到playlist表,并将逗号分隔的歌曲ID插入该列。我认为这不是一个合适的关系方式,但是做到了。
    1. 创建单独的表格,以便将歌曲ID与其所属的播放列表ID存储在一起。与playlist_id INT, song_id INT类似,其中两列都是外键。
    2. 现在,如果第二个选项更好,我应该添加另一个列作为主键吗?auto_increment知道它在任何地方都没用吗?因为我在线阅读了一些文章,其中许多文章表明没有表格的主键会以负面的方式显着影响其表现。

6 个答案:

答案 0 :(得分:4)

你应该强烈倾向于选项二,即创建一个将播放列表ID与歌曲ID相关联的表格。在这种情况下,您实际上可以创建一个主键,它是播放列表和歌曲ID的组合。

CREATE TABLE playlist_songs (
    song_id INT,
    playlist_id INT,
    PRIMARY KEY (song_id, playlist_id)
)

至于您是否需要playlist_songs上的自动增量列,这取决于您的具体情况。从业务逻辑的角度来看,您可能不需要它,因为您可能正在使用已存在的两列来操作表。

答案 1 :(得分:2)

你的问题有两个方面 - 抽象的,哲学的观点和实际意义。

哲学上,我们决定数据库设计是否“好”的方式是看它是否正常化。 您的设计中有两个实体 - 歌曲和播放列表。你有两种关系 - 一首歌可以属于0..n播放列表,一个播放列表包含0..n首歌曲。 您希望单独存储这些事实,而不是将它们捆绑在一起。这意味着桥接表是“最佳”的,因为它存储单个事实(歌曲x属于播放列表y),与歌曲或播放列表的存在无关。 替代设计将几个事实存储在一行中 - “存在播放列表,并且具有以下歌曲”。

第二个哲学问题是“我如何唯一地识别事实?”。在你的桥接表中,独特的事实是“歌曲x属于播放列表y”。它只能属于该播放列表一次(实际上,这可能不是真的 - 您可能需要一列来指示歌曲出现的顺序)。 这意味着您的业务领域中有一个自然的复合键。从哲学上讲,这是您想要用来识别这些记录的内容,因此这应该是您的主要密钥。

从实际角度来看,第一个问题(选项一或选项二)取决于您的应用程序如何工作和发展。 如果你必须回答“这首歌出现在哪个播放列表中”这个问题,那么选项2要好得多 - 选项一需要一个where子句,如'where playlist.songs like'%songid,&',这将非常慢。 如果您必须删除一首歌曲,并确保删除所有引用 - 选项2要好得多。选项一会很慢找到,更新逗号分隔列表的代码会很糟糕。 如果您必须在播放列表中间插入歌曲,则选项2要好得多。

关于“我如何分配我的主键”的问题 - 我想你可能误解了这些文章。主键是逻辑概念,不需要是自动递增的整数。只要你有好的索引(并且索引与主键不同),你的表现就会很好。

答案 2 :(得分:1)

第二个选项是FAR更可取。

对于额外的主键,虽然没有必要,但我倾向于使用一个,即使只是为了更容易处理该表中的行。

例如,假设您要删除十几行,可以使用 IN(逗号分隔的ID列表),而不是检查行中每对字段的where子句。

顺便说一句,第二种选择更可取的原因有很多: -

  • 如果您希望逗号分隔列表中的项目多于该字段中的项目,会发生什么?
  • 当您想要搜索该列表中的值时会发生什么?您 无法索引该列表中途的值。
  • 当您想要从项目中挂起另一个值时会发生什么 播放列表?例如,曲目的次数 在该播放列表上播放?

答案 3 :(得分:1)

我会说选项二对你最有利。然后,您将拥有如下表格:

  

playlist_items表

     

pi_id INT AUTO_INCREMENT PRIMARY KEY

     

pi_song_id INT

     

pi_playlist_id INT

有了这个,您可以在将来添加功能,如果需要,例如:

  

pi_dateadded DATETIME

答案 4 :(得分:1)

在InnoDB中请记住,您可以通过按逻辑顺序遍历主键索引来访问行,因此您需要询问如何查找行。遍历索引是O(log(N))复杂度,但如果您使用的是二级索引,那么您将执行两次。

通常在InnoDB中使用单列pkey更好,但可能有例外。

答案 5 :(得分:-1)

playlist_table

`playlist_id` INT AUTO_INCREMENT PRIMARY KEY, `playlist_title` VARCHAR

songs_table

`song_id` INT AUTO_INCREMENT PRIMARY KEY, `song_title` VARCHAR,playlist_id INT FOREIGN KEY (playlist_id) REFERENCES playlist_table(playlist_id)

如果您想要搜索,请使用联接来查找歌曲

select * from songs_table left join playlist_table on(songs_table.playlist_id=playlist_table.playlist_id)