我知道您可以使用 FIRST 和 AFTER 更改 MySQL 中的列顺序,但是您为什么要打扰呢?既然好的查询在插入数据时会显式命名列,那么真的有任何理由关心您的列在表中的顺序吗?
14 回答
列顺序对我调整过的一些数据库有很大的性能影响,包括 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 开头的引用。这完全否定了我上面列出的效果。所以对于最近的数据库,列顺序不再有任何影响。
更新:
在MySQL
中,这样做可能是有原因的。
由于可变数据类型(如VARCHAR
)以可变长度存储在 中InnoDB
,因此数据库引擎应遍历每行中的所有先前列以找出给定列的偏移量。
对列的影响可能高达17%20
。
有关更多详细信息,请参阅我的博客中的此条目:
在Oracle
中,尾随NULL
列不占用空间,这就是为什么您应该始终将它们放在表的末尾。
同样 inOracle
和 in SQL Server
,在大行的情况下,ROW CHAINING
可能会出现 a。
ROW CHANING
正在拆分不适合一个块的行并将其跨越多个块,并通过链表连接。
读取不适合第一个块的尾随列将需要遍历链表,这将导致额外的I/O
操作。
有关in的说明,请参阅此页面:ROW CHAINING
Oracle
这就是为什么你应该把你经常使用的列放在表的开头,把你不经常使用的列或者往往是的列NULL
放在表的末尾。
重要的提示:
如果您喜欢这个答案并想为它投票,也请为@Andomar
's answer投票。
他回答了同样的事情,但似乎无缘无故地被否决了。
在上一份工作的 Oracle 培训期间,我们的 DBA 建议将所有不可为空的列放在可空的列之前是有利的……虽然 TBH 我不记得为什么的细节。或者也许只是那些可能会更新的应该在最后?(如果行扩展,可能会推迟移动行)
一般来说,它不应该有任何区别。正如您所说,查询应始终指定列本身,而不是依赖于“select *”的排序。我不知道任何允许更改它们的数据库......好吧,直到你提到它,我才知道 MySQL 允许它。
必须键入时输出的可读性:
select * from <table>
在您的数据库管理软件中?
这是一个非常虚假的原因,但目前我想不出别的。
一些写得不好的应用程序可能依赖于列顺序/索引而不是列名。他们不应该,但它确实发生了。更改列的顺序会破坏此类应用程序。
不,SQL 数据库表中列的顺序是完全不相关的——除了显示/打印目的。重新排序列没有意义——大多数系统甚至没有提供这样做的方法(除了删除旧表并使用新的列顺序重新创建它)。
马克
编辑:从关系数据库的维基百科条目中,这是相关部分,我清楚地表明,列顺序永远不应该受到关注:
关系被定义为一组 n 元组。在数学和关系数据库模型中,集合是项目的无序集合,尽管一些 DBMS 对其数据施加了顺序。在数学中,元组具有顺序,并且允许重复。EF Codd 最初使用这个数学定义来定义元组。后来,EF Codd 的一大见解是,在基于关系的计算机语言中,使用属性名称而不是排序会更方便(通常)。这种见解今天仍在使用。
除了明显的性能调整之外,我还遇到了一个极端情况,即重新排序列导致(以前的功能)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 命令以包含默认值,但是由于列重新排序而导致有效的查询停止工作的事实让我很头疼。
通常情况下,最大的因素是下一个必须在系统上工作的人。我尝试首先拥有主键列,其次是外键列,然后按照对系统的重要性/重要性的降序排列其余列。
我能想到的唯一原因是调试和灭火。我们有一个表,其“名称”列在列表中的第 10 位左右出现。当您从表中的 id in (1,2,3) 中快速选择 * 然后您必须滚动查看名称时,这很痛苦。
但仅此而已。
2002 年,Bill Thorsteinson 在 Hewlett Packard 论坛上发布了他通过重新排序列来优化 MySQL 查询的建议。此后,他的帖子在互联网上至少被复制和粘贴了一百次,而且通常没有引用。准确地引用他的话...
一般经验法则:
- 首先是主键列。
- 接下来是外键列。
- 接下来是经常搜索的列。
- 以后经常更新的专栏。
- 可空列最后。
- 在更频繁使用的可空列之后使用最少的可空列。
- 自己的表中的 Blob,其他列很少。
资料来源:惠普论坛。
但那篇文章是在 2002 年发表的! 这个建议是针对 MySQL 3.23 版本的,比 MySQL 5.1 发布早了六年多。 并且没有参考文献或引文。那么,比尔是对的吗?存储引擎在这个级别上究竟是如何工作的?
- 是的,比尔是对的。
- 这一切都归结为链式行和内存块的问题。
引用Oracle 认证专家Martin Zahn在一篇关于Oracle 行链接和迁移的秘密的文章...
链式行对我们的影响不同。在这里,这取决于我们需要的数据。如果我们有一行有两列,分布在两个块上,查询:
SELECT column1 FROM table
其中 column1 在块 1 中,不会导致任何«table fetch continue row»。它实际上不必获取 column2,它不会一直跟随链接的行。另一方面,如果我们要求:
SELECT column2 FROM table
由于行链接,column2 在第 2 块中,那么您实际上会看到一个«table fetch continue row»
这篇文章的其余部分是一个相当不错的阅读!但我在这里只引用与我们手头的问题直接相关的部分。
18 年后,我必须说:谢谢,比尔!
如果您要经常使用 UNION,如果您对它们的排序有一个约定,它会使匹配列更容易。
如前所述,存在许多潜在的性能问题。我曾经在一个数据库上工作过,如果您没有在查询中引用这些列,那么将非常大的列放在最后会提高性能。显然,如果一条记录跨越多个磁盘块,数据库引擎在获得所需的所有列后可能会停止读取块。
当然,任何性能影响不仅高度依赖于您使用的制造商,还可能依赖于版本。几个月前,我注意到我们的 Postgres 无法使用索引进行“点赞”比较。也就是说,如果你写了“somecolumn like 'M%'”,那么在找到第一个 N 时跳到 M 并退出是不够聪明的。我打算将一堆查询更改为使用“between”。然后我们得到了一个新版本的 Postgres,它智能地处理了类似的事情。很高兴我从来没有改变查询。显然在这里没有直接关系,但我的观点是,你为效率考虑所做的任何事情都可能在下一个版本中过时。
列顺序几乎总是与我非常相关,因为我经常编写通用代码来读取数据库模式以创建屏幕。就像,我的“编辑记录”屏幕几乎总是通过读取架构以获取字段列表,然后按顺序显示它们来构建。如果我改变了列的顺序,我的程序仍然可以工作,但显示对用户来说可能很奇怪。就像,您希望看到姓名/地址/城市/州/邮编,而不是城市/地址/邮编/姓名/州。当然,我可以将列的显示顺序放在代码或控制文件或其他东西中,但是每次添加或删除列时,我们都必须记住去更新控制文件。我喜欢说一次。此外,当编辑屏幕完全由模式构建时,添加一个新表可能意味着编写零行代码来为其创建一个编辑屏幕,这非常酷。(好吧,好吧,实际上通常我必须在菜单中添加一个条目来调用通用编辑程序,而我通常已经放弃了通用的“选择要更新的记录”,因为有太多的例外使其实用.)
您唯一需要担心列顺序的情况是您的软件是否特别依赖该顺序。通常这是由于开发人员变得懒惰并做了 aselect *
然后在结果中通过索引而不是名称来引用列。
一般来说,当您通过 Management Studio 更改列顺序时,SQL Server 中会发生什么,它会创建一个具有新结构的临时表,将数据从旧表移动到该结构,删除旧表并重命名新表。正如您可能想象的那样,如果您有一张大桌子,这对于性能来说是一个非常糟糕的选择。我不知道 My SQL 是否也这样做,但这是我们许多人避免重新排序列的原因之一。因为 select * 不应该在生产系统中使用,所以在最后添加列对于设计良好的系统来说不是问题。表中的列顺序一般不应该被弄乱。