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

时间:2009-05-21 18:59:36

标签: mysql sql database-table

我知道您可以使用FIRST和AFTER更改MySQL中的列顺序,但为什么要打扰?由于好的查询在插入数据时明确地命名列,是否真的有理由关心列中的列的顺序?

14 个答案:

答案 0 :(得分:88)

列顺序对我调整过的一些数据库有很大的性能影响,包括Sql Server,Oracle和MySQL。这篇文章有good rules of thumb

  • 首先是主键列
  • 接下来是外键列。
  • 下一个经常搜索的列
  • 以后经常更新的专栏
  • Nullable列最后。
  • 在更频繁使用的可为空的列之后使用最少的可空列

性能差异的一个例子是索引查找。数据库引擎根据索引中的某些条件查找行,并返回行地址。现在说你正在寻找SomeValue,它就在这张表中:

 SomeId int,
 SomeString varchar(100),
 SomeValue int

引擎必须猜测SomeValue的起始位置,因为SomeString的长度未知。但是,如果您将订单更改为:

 SomeId int,
 SomeValue int,
 SomeString varchar(100)

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

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

答案 1 :(得分:39)

<强>更新

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

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

20列的影响可能会大到 17%

有关详细信息,请参阅我的博客中的此条目:

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

同样在OracleSQL Server中,如果行数较大,可能会出现ROW CHAINING

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

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

有关ROW CHAININGOracle的说明,请参阅this page

这就是为什么你应该将经常使用的列放在表的开头,将经常不使用的列或者往往是NULL的列放到表的末尾。

重要提示:

如果您喜欢这个答案并想投票,请同时投票给@Andomar's answer

他回答了同样的事情,但似乎无缘无故地被投票。

答案 2 :(得分:6)

在上一份工作的Oracle培训期间,我们的DBA建议将所有非可空列放在可空的列之前是有利的......虽然TBH我不记得原因的细节。或者也许只是那些可能会更新的应该到最后? (如果扩展,可能会推迟行)

一般来说,它应该没有任何区别。正如您所说,查询应始终指定列本身,而不是依赖于“select *”的排序。我不知道有任何DB允许它们被更改......好吧,我不知道MySQL允许它直到你提到它。

答案 3 :(得分:5)

不,SQL数据库表中列的顺序完全不相关 - 除了显示/打印目的。重新排序列没有意义 - 大多数系统甚至没有提供这样做的方法(除了删除旧表并使用新的列顺序重新创建它)。

马克

编辑:关于关系数据库的维基百科条目,这里的相关部分清楚地表明列顺序从不值得关注:

关系被定义为一组n元组。在数学和关系数据库模型中,集合是一个无序项集合,尽管有些DBMS对其数据强加了一个顺序。在数学中,元组有一个顺序,并允许重复。 E.F. Codd最初使用这个数学定义来定义元组。后来,这是E.F. Codd的一个很好的见解,即在基于关系的计算机语言中使用属性名称而不是排序将更加方便(通常)。今天仍在使用这种见解。

答案 4 :(得分:5)

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

答案 5 :(得分:4)

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

select * from <table>

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

这是一个非常虚假的原因,但目前我无法想到其他任何事情。

答案 6 :(得分:2)

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

但那是关于它的。

答案 7 :(得分:1)

除了显而易见的性能调优之外,我只是遇到了一个角落案例,其中重新排序列导致(以前正常运行的)sql脚本失败。

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

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

显然,您可以更正该alter命令以包含默认值,但由于列重新排序而导致工作停止工作的查询这一事实让我头疼。

答案 8 :(得分:1)

通常情况下,最重要的因素是下一个必须在系统上工作的人。我尝试先将主键列,第二个外键列,然后按重要性/重要性的降序排列其余列。

答案 9 :(得分:1)

如果你要使用UNION很多,如果你有关于它们的排序的约定,它会使匹配列更容易。

答案 10 :(得分:0)

如果您的软件特别依赖于该订单,那么您唯一需要担心列顺序的时间。通常这是因为开发人员变得懒惰并执行了select *,然后通过索引而不是结果中的名称来引用列。

答案 11 :(得分:0)

一般情况下,当您通过Management Studio更改列顺序时,SQL Server中发生的情况是它使用新结构创建临时表,将数据从旧表移动到该结构,删除旧表并重命名新表一。正如您可能想象的那样,如果您有一个大表,这对性能来说是一个非常糟糕的选择。我不知道My SQL是否也这样做,但这也是我们许多人避免重新排序列的原因之一。由于select *永远不应该在生产系统中使用,因此在最终添加列对于设计良好的系统来说并不是问题。表格中的列顺序应该不会被混淆。

答案 12 :(得分:0)

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

一般经验法则:

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

来源:HP Forums.

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

  1. 是的,比尔是对的。
  2. 这全都取决于链接的行和内存块。

an Oracle-certified professional ...上的一篇文章中引用The Secrets of Oracle Row Chaining and Migration的Martin Zahn ...

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

SELECT column1 FROM table

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

SELECT column2 FROM table

并且由于行链接的原因,column2在块2中,所以实际上您会看到«表获取连续行»

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

超过18年后,我要说:谢谢,比尔!

Mapping a MySQL Row to a Data Block

答案 13 :(得分:0)

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

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

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