是否有任何理由担心表中的列顺序?
我知道你可以用FIRST和AFTER来改变MySQL的列顺序,但你为什么要打扰呢? 由于好的查询在插入数据时显式地命名列,是否真的有理由关心你的列在表中的顺序?
列顺序对我调整的一些数据库有很大的性能影响,包括Sql Server,Oracle和MySQL。 这个职位有很好的经验 :
- 主键列首先
- 接下来是外键列。
- 下一个频繁search的列
- 以后经常更新列
- 可空列最后。
- 在使用更多的可空列之后,最less使用可为空的列
性能差异的一个例子是索引查找。 数据库引擎根据索引中的一些条件find一行,并取回一个行地址。 现在说你正在寻找SomeValue,它在这张表中:
SomeId int, SomeString varchar(100), SomeValue int
引擎必须猜测SomeValue的起始位置,因为SomeString的长度是未知的。 但是,如果您将订单更改为:
SomeId int, SomeValue int, SomeString varchar(100)
现在引擎知道SomeValue可以在行开始之后find4个字节。 所以列顺序可能会有相当大的性能影响。
编辑:SQL Server 2005存储固定长度的字段在行的开始。 每行都有一个对varchar开始的引用。 这完全否定了我上面列出的效果。 所以对于最近的数据库,列顺序不再有任何影响。
更新:
在MySQL
,这可能是有原因的。
由于variables数据types(如VARCHAR
)在InnoDB
以可变长度存储,因此数据库引擎应该遍历每行中的所有先前列以找出给定偏移量。
20
列的影响可能高达17% 。
在我的博客中查看这个条目了解更多详情:
- select列顺序
在Oracle
,后面的NULL
列不会占用空间,这就是为什么你总是把它们放在表的最后。
同样在Oracle
和SQL Server
,如果行较大, ROW CHAINING
可能发生ROW CHAINING
。
ROW CHANING
正在拆分一个不适合一个块的行,并将其跨越多个块,并与一个链表相关联。
读取不适合第一个块的尾部列将需要遍历链接列表,这将导致额外的I/O
操作。
请参阅此页面以查看Oracle
的ROW CHAINING
:
这就是为什么你应该把你经常使用的列放到表的开头,你不经常使用的列或者往往是NULL
列到表的末尾。
重要的提示:
如果你喜欢这个答案,并希望投票支持,也请投票支持@Andomar
的回答 。
他回答同样的问题,但似乎没有理由低估了。
在之前的Oracle培训工作中,我们的DBAbuild议把所有不可为空的列放在可为空的列之前是有利的…虽然TBH我不记得详细的原因。 或者,也许这只是那些可能会得到更新应该在最后? (如果扩展的话也许会推迟行)
一般来说,它不应该有任何区别。 正如你所说,查询应该总是自己指定列,而不是依靠“select *”的顺序。 我不知道任何数据库允许他们被改变…嗯,我不知道MySQL允许它,直到你提到它。
一些写得不好的应用程序可能依赖于列顺序/索引而不是列名。 他们不应该,但它确实发生。 改变列的顺序会破坏这样的应用程序。
当你input以下内容时输出的可读性:
select * from <table>
在你的数据库pipe理软件?
这是一个非常虚假的原因,但目前我不能想到别的。
不,SQL数据库表中列的顺序是完全不相关的 – 除了显示/打印的目的。 对列进行重新sorting没有任何意义 – 大多数系统甚至不提供这种方式(除了删除旧表并使用新的列顺序重新创build表)。
渣子
编辑:从关系数据库维基百科条目,这是相关的部分,我清楚地表明,列顺序应该永远不会担心:
关系被定义为一组n元组。 在math和关系数据库模型中,一个集合是一个无序的项目集合,尽pipe一些DBMS对他们的数据施加了一个顺序。 在math中,一个元组有秩序,并且允许重复。 EF Codd最初使用这个math定义来定义元组。 后来,这是EF Codd的一个很好的见解,即在基于关系的计算机语言中,使用属性名称而不是sorting会更加方便(通常)。 这种见解今天仍在使用。
我能想到的唯一原因就是debugging和灭火。 我们有一张桌子,名字列出现在名单上的第十位。 当你从(1,2,3)中的id表中快速select*,然后你必须滚动查看名字,这是一个痛苦。
但是这是关于它的。
通常情况下,最大的因素是下一个必须在系统上工作的人。 我尝试首先使用主键列,然后使用外键列,然后按照对系统重要性/重要性的降序排列其余列。
除了显而易见的性能调优外,我只是遇到了一个重新sorting列导致(以前的function)的SQL脚本失败的情况。
从文档“TIMESTAMP和DATETIME列没有自动属性,除非它们被明确指定,但有以下例外:默认情况下,第一个TIMESTAMP列同时具有DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP,如果没有明确指定” https://dev.mysql .COM / DOC / refman / 5.6 / EN /时间戳initialization.html
所以,一个命令ALTER TABLE table_name MODIFY field_name timestamp(6) NOT NULL;
如果这个字段是表中的第一个时间戳(或date时间),将会工作,但不是。
显然,你可以修改alter命令来包含一个默认值,但是由于列重新sorting而停止工作的查询使得我的头受到伤害。
唯一需要担心列顺序的是如果您的软件特别依赖于该顺序。 通常,这是由于开发人员懒惰并做了select *
,然后通过索引而不是按名称在结果中引用列。
如果你打算使用UNION,那么如果你有一个关于sorting的约定,它会使匹配的列更容易。
一般来说,在通过Management Studio更改列顺序时,SQL Server中会发生什么情况,它是使用新结构创build临时表,将数据从旧表移动到该结构,然后删除旧表并重命名新表。 就像你想象的那样,如果你有一张大桌子,这对性能来说是一个很差的select。 我不知道我的SQL是否也一样,但这是我们许多人避免重新sorting列的一个原因。 由于select *决不能在生产系统中使用,因此最后添加列对于devise良好的系统来说不是一个好现象。 表格中列的顺序不应该混淆。
如上所述,有许多潜在的性能问题。 我曾经在一个数据库上工作,如果你没有在查询中引用这些列,那么在最后加上非常大的列可以提高性能。 显然,如果一条logging跨越了多个磁盘块,数据库引擎一旦获得所有需要的列,就可以停止读取块。
当然,任何性能影响都不仅取决于您使用的制造商,还取决于版本。 几个月前,我注意到我们的Postgres不能使用索引来进行“类似”的比较。 也就是说,如果你写了“像'M%'这样的列',那么跳到M就不够聪明了,find第一个N就退出了。我打算改变一堆查询来使用”between“。 然后我们得到了一个Postgres的新版本,它处理类似的智能。 很高兴我永远不会改变查询。 显然这里并不直接相关,但我的观点是,任何你为了效率考虑而做的事都可能在下一个版本中被淘汰。
列顺序几乎总是与我非常相关,因为我经常编写读取数据库模式以创build屏幕的通用代码。 就像,我的“编辑logging”屏幕几乎总是通过阅读架构来获得字段列表,然后按顺序显示它们。 如果我改变了列的顺序,我的程序仍然可以正常工作,但显示对用户来说可能很奇怪。 喜欢,你希望看到名称/地址/城市/州/邮编,而不是城市/地址/邮编/名称/状态。 当然,我可以把列的显示顺序放在代码或控制文件中,但是每次我们添加或移除列时,都必须记得更新控制文件。 我喜欢说一次。 而且,当编辑屏幕纯粹是从模式构build的时候,添加一个新表格可能意味着编写零行代码来为其创build编辑屏幕,这很酷。 (好吧,在实践中,通常我必须在菜单中添加一个条目来调用通用编辑程序,而且我通常会放弃generics“select要更新的logging”,因为有太多例外可以实现。)