我知道您可以使用FIRST和AFTER更改MySQL中的列顺序,但是为什么要麻烦呢?由于良好的查询会在插入数据时显式命名列,因此确实有任何理由要关心列在表中的顺序吗?
Answers:
列顺序对我已调优的某些数据库(包括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
列不占用空间,这就是为什么您应始终将它们放在表的末尾。
在行中Oracle
和行中SQL Server
,如果行较大,ROW CHAINING
也可能会出现a。
ROW CHANING
正在将不适合一个块的行拆分,并将其跨越多个块,并与一个链表相连。
读取不适合第一个块的尾随列将需要遍历链接列表,这将导致额外的I/O
操作。
请参阅此页面以获取ROW CHAINING
in中的插图Oracle
:
因此,您应该将经常使用的列放到表的开头,将不经常使用的列放到表NULL
的末尾。
重要的提示:
如果你喜欢这个答案,要选它,也请投给@Andomar
的回答。
他回答了同样的事情,但似乎无缘无故被否决了。
在上一份工作的Oracle培训期间,我们的数据库管理员建议将所有不可为空的列放在可为空的列之前是有好处的……尽管TBH我不记得为什么要这样做了。还是只是那些可能会更新的内容应该在最后进行?(如果扩展,也许推迟了不必移动行)
通常,它不会有任何区别。如您所说,查询应始终自己指定列,而不要依赖“ select *”中的顺序。我不知道有任何数据库可以更改它们……好吧,直到您提到它,我才知道MySQL不允许它。
一些写得不好的应用程序可能依赖于列顺序/索引而不是列名。他们不应该这样,但确实如此。更改列的顺序会破坏此类应用程序。
不,SQL数据库表中的列顺序完全无关紧要-除了显示/打印目的。对列进行重新排序没有任何意义-大多数系统甚至没有提供执行此操作的方法(除去旧表并以新的列顺序重新创建它)。
马克
编辑:从关系数据库上的Wikipedia条目中,以下是相关部分,对我而言,这清楚地表明了列顺序永远不会受到关注:
关系定义为一组n元组。在数学和关系数据库模型中,集合都是无序的项目集合,尽管某些DBMS对其数据强加了顺序。在数学中,元组具有顺序,并允许重复。EF Codd最初使用此数学定义定义了元组。后来,这是EF Codd的重要见解之一,在基于关系的计算机语言中,使用属性名而不是顺序会更方便(通常)。今天,这种见解仍在使用。
如果您将大量使用UNION,则如果您对它们的排序有约定,那么它会使匹配列更容易。
如上所述,存在许多潜在的性能问题。我曾经在一个数据库上工作过,如果您没有在查询中引用这些列,则在最后放置很大的列可以提高性能。显然,如果一条记录跨越多个磁盘块,那么数据库引擎一旦获得了所需的所有列,就可以停止读取块。
当然,任何性能影响都不仅高度依赖于您所使用的制造商,而且还高度依赖于版本。几个月前,我注意到我们的Postgres无法使用索引进行“喜欢”比较。也就是说,如果您编写了“类似'M%'之类的列”,那么当它找到第一个N时,跳到M并退出就不够聪明。我打算更改一堆查询以使用“ between”。然后我们得到了新版本的Postgres,它可以智能地处理类似的东西。很高兴我从来没有绕过更改查询。显然这里没有直接关系,但我的意思是,您出于效率考虑而做的任何事情都可能在下一版本中过时。
列顺序几乎总是与我非常相关,因为我通常编写通用代码来读取数据库模式以创建屏幕。就像我的“编辑记录”屏幕一样,几乎总是通过读取模式以获取字段列表,然后按顺序显示它们来构建的。如果我更改了列的顺序,我的程序仍然可以运行,但是显示可能对用户来说很奇怪。就像,您希望看到名称/地址/城市/州/邮政编码,而不是城市/地址/邮政编码/名称/州。当然,我可以将列的显示顺序放在代码或控制文件之类的东西中,但是每次我们添加或删除列时,我们都必须记住要更新控制文件。我喜欢说一次。另外,当纯粹根据架构构建编辑屏幕时,添加新表可能意味着编写零行代码以为其创建编辑屏幕,这很酷。(嗯,好吧,实际上,通常我必须在菜单上添加一个条目才能调用通用编辑程序,并且由于存在太多例外,因此我通常放弃了通用“选择要更新的记录” )
除了明显的性能调整之外,我还遇到了一个极端的情况,即重新排序列会导致(以前起作用的)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命令以包括一个默认值,但是由于列重新排序而导致工作停止的查询使我很头疼。
2002年,Bill Thorsteinson在Hewlett Packard论坛上发布了他的建议,即通过对列进行重新排序来优化MySQL查询。此后,他的帖子被字面上照抄了至少在互联网上至少粘贴了一百遍,而且经常没有被引用。准确地引用他的话...
一般经验法则:
- 首先是主键列。
- 接下来是外键列。
- 接下来是经常搜索的列。
- 以后经常更新的列。
- 空列最后。
- 在更常用的可空列之后使用最少的可空列。
- 自己表中的Blob和其他一些列。
资料来源:惠普论坛。
但是那个帖子是在2002年发布的! 该建议适用于MySQL 3.23版,比MySQL 5.1发布早了六年多。 并且没有参考或引用。所以,比尔对吗?在这个级别上,存储引擎如何工作?
引用Oracle认证的专家Martin Zahn在有关Oracle行链接和迁移的秘密的文章中...
链接的行对我们的影响不同。在这里,这取决于我们需要的数据。如果我们有两列的行分布在两个块中,则查询:
SELECT column1 FROM table
如果column1在块1中,则不会导致任何“表提取连续行”。实际上,它不必获取column2,也不会一直跟踪链接的行。另一方面,如果我们要求:
SELECT column2 FROM table
并且column2由于行链接而位于第2块中,那么实际上您会看到“表提取连续行”
本文的其余部分相当不错!但是,我在这里仅引用与我们即将提出的问题直接相关的部分。
18年多以后,我得说:谢谢,比尔!