是否有任何理由担心表中的列顺序?


84

我知道您可以使用FIRST和AFTER更改MySQL中的列顺序,但是为什么要麻烦呢?由于良好的查询会在插入数据时显式命名列,因此确实有任何理由要关心列在表中的顺序吗?

Answers:


94

列顺序对我已调优的某些数据库(包括Sql Server,Oracle和MySQL)产生了很大的性能影响。这篇文章有很好的经验法则

  • 主键列优先
  • 接下来是外键列。
  • 经常搜索的列
  • 以后经常更新的列
  • 空列最后。
  • 在更经常使用的可空列之后使用最少的可空列

性能差异的一个示例是索引查找。数据库引擎根据索引中的某些条件查找行,然后获取行地址。现在说您正在寻找SomeValue,它在此表中:

 SomeId int,
 SomeString varchar(100),
 SomeValue int

引擎必须猜测SomeValue从哪里开始,因为SomeString的长度未知。但是,如果将顺序更改为:

 SomeId int,
 SomeValue int,
 SomeString varchar(100)

现在,引擎知道可以在行开始后的4个字节处找到SomeValue。因此,列顺序可能会对性能产生重大影响。

编辑:Sql Server 2005在行的开头存储固定长度的字段。并且每一行都有对varchar开头的引用。这完全抵消了我上面列出的效果。因此,对于最近的数据库,列顺序不再有任何影响。


4
@TopBanana:不能使用varchars,这就是将它们区别于普通char列的原因。
Allain Lalonde

1
我认为表格中列的顺序没有任何区别-确实可以改变您可能创建的索引。
marc_s

4
@TopBanana:不知道您是否知道Oracle,但它不会为VARCHAR2(100)保留100个字节
Quassnoi

1
@Quassnoi:对SQL Server的最大影响是在具有许多可空varchar()列的表上。
安多玛尔

7
此答案中的URL不再有效,有人可以替代吗?
scunliffe

41

更新:

在中MySQL,可能有这样做的理由。

由于变量数据类型(如VARCHAR)以可变长度存储在中InnoDB,因此数据库引擎应遍历每一行中的所有先前列,以找出给定偏移量。

对色谱柱的影响可能高达17%20

有关更多详细信息,请参见我的博客中的此项:

在中Oracle,尾随的NULL列不占用空间,这就是为什么您应始终将它们放在表的末尾。

在行中Oracle和行中SQL Server,如果行较大,ROW CHAINING也可能会出现a。

ROW CHANING 正在将不适合一个块的行拆分,并将其跨越多个块,并与一个链表相连。

读取不适合第一个块的尾随列将需要遍历链接列表,这将导致额外的I/O操作。

请参阅此页面以获取ROW CHAININGin中的插图Oracle

因此,您应该将经常使用的列放到表的开头,将不经常使用的列放到表NULL的末尾。

重要的提示:

如果你喜欢这个答案,要选它,也请投给@Andomar的回答

他回答了同样的事情,但似乎无缘无故被否决了。


1
所以你说这会很慢:从tinyTable内部选择tinyTable.id,tblBIG.firstColumn,tblBIG.lastColumn在tinyTable.id = tblBIG.fkID上连接tblBIG如果tblBIG记录超过8KB(在这种情况下会发生一些行链接) )并且联接将是同步的...但是这将是快速的:从tinyTable的tinyTable内部联接tblBIG中选择tinyTable.id,tblBIG.firstColumn = tblBIG.fkID因为我不会在其他块中使用该列,所以没有需要遍历链表我正确了吗?
jfrobishow

1
我只得到6%,这是col1与其他任何列的对比。
里克·詹姆斯

6

在上一份工作的Oracle培训期间,我们的数据库管理员建议将所有不可为空的列放在可为空的列之前是有好处的……尽管TBH我不记得为什么要这样做了。还是只是那些可能会更新的内容应该在最后进行?(如果扩展,也许推迟了不必移动行)

通常,它不会有任何区别。如您所说,查询应始终自己指定列,而不要依赖“ select *”中的顺序。我不知道有任何数据库可以更改它们……好吧,直到您提到它,我才知道MySQL不允许它。


4
他说的没错,Oracle不会将尾随NULL列写入磁盘,从而节省了一些字节。请参阅dba-oracle.com/oracle_tips_ault_nulls_values.htm
Andomar,2009年

绝对,它可以在磁盘大小上产生很大的不同
亚历克斯(Alex)

那是你的意思链接吗?它与索引中的null的非索引而不是列顺序有关。
araqnid,2009年

链接错误,找不到原始文件。虽然您可以在Google上搜索它,例如tlingua.com/new/articles/Chapter2.html
Andomar

5

一些写得不好的应用程序可能依赖于列顺序/索引而不是列名。他们不应该这样,但确实如此。更改列的顺序会破坏此类应用程序。


3
使他们的代码依赖于表DESERVE中的列顺序的应用程序开发人员将其应用程序破坏。但是应用程序的用户不应该中断。
spencer7593

4

您必须输入时输出的可读性:

select * from <table>

在您的数据库管理软件中?

这是一个非常虚假的原因,但此刻我想不起其他任何事情了。


4

不,SQL数据库表中的列顺序完全无关紧要-除了显示/打印目的。对列进行重新排序没有任何意义-大多数系统甚至没有提供执行此操作的方法(除去旧表并以新的列顺序重新创建它)。

马克

编辑:从关系数据库上的Wikipedia条目中,以下是相关部分,对我而言,这清楚地表明了列顺序永远不会受到关注:

关系定义为一组n元组。在数学和关系数据库模型中,集合都是无序的项目集合,尽管某些DBMS对其数据强加了顺序。在数学中,元组具有顺序,并允许重复。EF Codd最初使用此数学定义定义了元组。后来,这是EF Codd的重要见解之一,在基于关系的计算机语言中,使用属性名而不是顺序会更方便(通常)。今天,这种见解仍在使用。


1
我已经亲眼目睹了柱差异对我的影响很大,所以我不敢相信这是正确的答案。即使投票放在首位。嗯
2009年

那将在什么SQL环境中?
marc_s

1
我所看到的最大影响是对Sql Server 2000的影响,其中向前移动外键将某些查询的速度提高了2到3倍。这些查询具有大表扫描(超过1M行),并带有外键条件。
安多玛尔

5
除非您关注性能,否则RDBMS并不依赖表顺序。对于列的顺序,不同的实现将有不同的性能损失。它可能很大,也可能很小,这取决于实现方式。元组是理论上的,RDBMS是实用的。
EstebanKüber'09

3
-1。我使用过的所有关系数据库在某种程度上都具有列顺序。如果从表中选择*,则不会倾向于随机返回列。现在,磁盘还是显示是另一番争论。引用数学理论来支持关于数据库实际实现的假设只是胡说八道。
DougW 2013年

2

我能想到的唯一原因是调试和消防。我们有一个表,该表的“名称”列在列表中排名第10。当您从(1,2,3)中的id的表中快速选择*,然后必须滚动查看名称时,这很痛苦。

就是这样。


1

通常,最大的因素是下一个必须在系统上工作的人。我尝试首先具有主键列,其次是外键列,然后按对系统重要性/重要性的降序排列其余列。


我们通常从“创建”最后一列开始(插入行的时间戳)。当然,对于较旧的表,它可以在其后添加几列...而且我们偶尔还会有一个表,其中复合主键更改为代理键,因此主键覆盖了几列。
araqnid

1

如果您将大量使用UNION,则如果您对它们的排序有约定,那么它会使匹配列更容易。


听起来您的数据库需要规范化!:)
James L

嘿! 拿回来,我没有说我的数据库。:)
艾伦·拉隆德


您能以2个表中的列顺序以不同顺序进行UNION吗?
Monica Heddneck '16

是的,您只需要在查询表时显式指定列。对于表A [a,b] B [b,a],这意味着(SELECT * FROM A)UNION(SELECT * FROM B)的(SELECT aa,ab from A)UNION(SELECT ba,bb FROM B)。
Allain Lalonde

1

如上所述,存在许多潜在的性能问题。我曾经在一个数据库上工作过,如果您没有在查询中引用这些列,则在最后放置很大的列可以提高性能。显然,如果一条记录跨越多个磁盘块,那么数据库引擎一旦获得了所需的所有列,就可以停止读取块。

当然,任何性能影响都不仅高度依赖于您所使用的制造商,而且还高度依赖于版本。几个月前,我注意到我们的Postgres无法使用索引进行“喜欢”比较。也就是说,如果您编写了“类似'M%'之类的列”,那么当它找到第一个N时,跳到M并退出就不够聪明。我打算更改一堆查询以使用“ between”。然后我们得到了新版本的Postgres,它可以智能地处理类似的东西。很高兴我从来没有绕过更改查询。显然这里没有直接关系,但我的意思是,您出于效率考虑而做的任何事情都可能在下一版本中过时。

列顺序几乎总是与我非常相关,因为我通常编写通用代码来读取数据库模式以创建屏幕。就像我的“编辑记录”屏幕一样,几乎总是通过读取模式以获取字段列表,然后按顺序显示它们来构建的。如果我更改了列的顺序,我的程序仍然可以运行,但是显示可能对用户来说很奇怪。就像,您希望看到名称/地址/城市/州/邮政编码,而不是城市/地址/邮政编码/名称/州。当然,我可以将列的显示顺序放在代码或控制文件之类的东西中,但是每次我们添加或删除列时,我们都必须记住要更新控制文件。我喜欢说一次。另外,当纯粹根据架构构建编辑屏幕时,添加新表可能意味着编写零行代码以为其创建编辑屏幕,这很酷。(嗯,好吧,实际上,通常我必须在菜单上添加一个条目才能调用通用编辑程序,并且由于存在太多例外,因此我通常放弃了通用“选择要更新的记录” )


1

除了明显的性能调整之外,我还遇到了一个极端的情况,即重新排序列会导致(以前起作用的)sql脚本失败。

从文档“ TIMESTAMP和DATETIME列没有自动属性,除非明确指定它们,但以下情况除外:默认情况下,如果未明确指定,则第一个TIMESTAMP列同时具有DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP” 。https://dev.mysql .com / doc / refman / 5.6 / en / timestamp-initialization.html

因此,ALTER TABLE table_name MODIFY field_name timestamp(6) NOT NULL;如果该字段是表中的第一个时间戳(或日期时间),则命令将起作用,但不是这样。

显然,您可以更正该alter命令以包括一个默认值,但是由于列重新排序而导致工作停止的查询使我很头疼。


0

您唯一需要担心列顺序的时间就是您的软件是否特别依赖该顺序。通常,这是由于开发人员变得懒惰并做了一个select *,然后在其结果中按索引而不是按名称引用了这些列这一事实。


0

通常,当您通过Management Studio更改列顺序时,在SQL Server中会发生以下情况:它会创建具有新结构的临时表,将数据从旧表移至该结构,删除旧表并重命名新表。您可能会想到,如果您有一张大桌子,这对于性能来说是一个非常糟糕的选择。我不知道My SQL是否做同样的事情,但这是我们许多人避免对列进行重新排序的原因之一。由于绝不能在生产系统中使用select *,因此对于设计良好的系统而言,在最后添加列并不是问题。表中的列顺序通常不应被弄乱。


0

2002年,Bill Thorsteinson在Hewlett Packard论坛上发布了他的建议,即通过对列进行重新排序来优化MySQL查询。此后,他的帖子被字面上照抄了至少在互联网上至少粘贴了一百遍,而且经常没有被引用。准确地引用他的话...

一般经验法则:

  • 首先是主键列。
  • 接下来是外键列。
  • 接下来是经常搜索的列。
  • 以后经常更新的列。
  • 空列最后。
  • 在更常用的可空列之后使用最少的可空列。
  • 自己表中的Blob和其他一些列。

资料来源:惠普论坛。

但是那个帖子是在2002年发布的! 该建议适用于MySQL 3.23版,比MySQL 5.1发布早了六年多。 并且没有参考或引用。所以,比尔对吗?在这个级别上,存储引擎如何工作?

  1. 是的,比尔是正确的。
  2. 一切都取决于链式行和内存块的问题。

引用Oracle认证的专家Martin Zahn在有关Oracle行链接和迁移的秘密的文章中...

链接的行对我们的影响不同。在这里,这取决于我们需要的数据。如果我们有两列的行分布在两个块中,则查询:

SELECT column1 FROM table

如果column1在块1中,则不会导致任何“表提取连续行”。实际上,它不必获取column2,也不会一直跟踪链接的行。另一方面,如果我们要求:

SELECT column2 FROM table

并且column2由于行链接而位于第2块中,那么实际上您会看到“表提取连续行”

本文的其余部分相当不错!但是,我在这里仅引用与我们即将提出的问题直接相关的部分。

18年多以后,我得说:谢谢,比尔!

将MySQL行映射到数据块

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.