表格定义中的列顺序重要吗?


35

定义表时,按目的对逻辑组中的列和组本身进行排序很有帮助。表中列的逻辑顺序将含义传达给开发人员,并且是一个良好样式的元素。

很清楚

但是,尚不清楚的是,表中列的逻辑顺序是否会对存储层的物理顺序有任何影响,或者是否有其他可能影响的影响。

除了对样式的影响之外,列顺序是否重要?

关于堆栈溢出有一个与此有关的问题,但是它缺乏权威性的答案。

Answers:


23

表中列的逻辑顺序是否会对存储层的物理顺序有影响?是。

不管是否重要,这是我无法回答的另一个问题。

以类似于Paul Randal经常链接的文章中对记录剖析的描述的方式,让我们看一个使用DBCC IND的简单两列表:

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

USE master;
GO

IF DATABASEPROPERTY (N'RowStructure', 'Version') > 0 DROP DATABASE RowStructure;
GO

CREATE DATABASE RowStructure;
GO

USE RowStructure;
GO

CREATE TABLE FixedLengthOrder
(
    c1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , c2 CHAR(10) DEFAULT REPLICATE('A', 10) NOT NULL
    , c3 CHAR(10) DEFAULT REPLICATE('B', 10) NOT NULL  
);
GO

INSERT FixedLengthOrder DEFAULT VALUES;
GO

DBCC IND ('RowStructure', 'FixedLengthOrder', 1);
GO

DBCC IND输出

上面的输出显示我们需要看一下第89页:

DBCC TRACEON (3604);
GO
DBCC PAGE ('RowStructure', 1, 89, 3);
GO

在DBCC PAGE的输出中,我们看到c1塞满了c2的“ B”之前的字符“ A”:

Memory Dump @0x000000000D25A060

0000000000000000:   10001c00 01000000 41414141 41414141 †........AAAAAAAA
0000000000000010:   41414242 42424242 42424242 030000††††AABBBBBBBBBB...

正因为如此,让我们RowStructure.mdf用十六进制编辑器打开胸围并确认'A'字符串位于'B'字符串之前:

AAAAAAAAAA

现在重复测试,但要颠倒字符串的顺序,将'B'字符放置在c1中,将'A'字符放置在c2中:

CREATE TABLE FixedLengthOrder
(
    c1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , c2 CHAR(10) DEFAULT REPLICATE('B', 10) NOT NULL
    , c3 CHAR(10) DEFAULT REPLICATE('A', 10) NOT NULL  
);
GO

这次,我们的DBCC PAGE输出是不同的,并且'B'字符串首先出现:

Memory Dump @0x000000000FC2A060

0000000000000000:   10001c00 01000000 42424242 42424242 †........BBBBBBBB 
0000000000000010:   42424141 41414141 41414141 030000††††BBAAAAAAAAAA... 

再次,只为咯咯地笑,让我们检查数据文件的十六进制转储:

BBBBBBBBBB

正如“记录剖析”所述,记录的固定长度和可变长度列存储在不同的块中。逻辑上交错的固定列和可变列类型与物理记录无关。但是,在每个块中,列的顺序确实映射到数据文件中字节的顺序。

CREATE TABLE FixedAndVariableColumns
(
    c1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , c2 CHAR(10) DEFAULT REPLICATE('A', 10) NOT NULL
    , c3 VARCHAR(10) DEFAULT REPLICATE('B', 10) NOT NULL  
    , c4 CHAR(10) DEFAULT REPLICATE('C', 10) NOT NULL
    , c5 VARCHAR(10) DEFAULT REPLICATE('D', 10) NOT NULL
    , c6 CHAR(10) DEFAULT REPLICATE('E', 10) NOT NULL  
);
GO

Memory Dump @0x000000000E07C060

0000000000000000:   30002600 01000000 41414141 41414141 0.&.....AAAAAAAA 
0000000000000010:   41414343 43434343 43434343 45454545 AACCCCCCCCCCEEEE 
0000000000000020:   45454545 45450600 00020039 00430042 EEEEEE.....9.C.B 
0000000000000030:   42424242 42424242 42444444 44444444 BBBBBBBBBDDDDDDD 
0000000000000040:   444444†††††††††††††††††††††††††††††††DDD

也可以看看:

列顺序无关紧要…通常,但是–取决于!


我同意+1。我总是发现,在每个部分中,列的顺序最初都是按照CREATE TABLE语句进行的(除了CI键列确实在该部分中排在第一位)。尽管如果ALTER COLUMN更改数据类型/列长度,则列的顺序可以更改。我能想到的唯一较小的情况是,变量长度部分结尾处带有空字符串或NULL的列在列偏移数组中根本不占空间(由Kalen Delaney在2008年内部手册中演示)
马丁·史密斯(Martin Smith)

1
在极少数情况下,列顺序可能很重要。例如,如果您有一个包含3列A,B和C的表,每个列长3kb。SQL Server页为8kb,因此C不合适,并进入其自己的扩展页。因此,select A, B从YourTable只需读取一半的页面select A, C from YourTable
安多玛(Andomar)2015年

"Whether it matters or not is a different issue that I can't answer (yet).":列的顺序会显着影响性能。此外,甚至会影响错误!检查一下 -演示2更好地显示了我的想法
Ronen Ariely

@RonenAriely有趣的例子,但在原始问题的背景下有些人为。您正在演示随后删除列时列顺序的影响。我认为我从来没有设计过具有预见性的表格。
Mark Storey-Smith

嗨@ MarkStorey-Smith。(1)作为一名建筑师,我总是向您解释,良好的设计与卓越的设计之间的区别在于,良好的设计满足了当前的需求,而卓越的设计满足了未来的需求(尚不为人所知)。(2)问题的答案为是。答案的实施取决于OP和我们每个人。这不在讨论范围之内,但是我们可以打开此主题进行讨论。但在stackoverflow论坛系列中却不是,因为该界面不允许进行真正的讨论,而只能在响应中添加一行简短的简短文字
Ronen Ariely

7

如果不定义聚簇索引,则会得到一个堆表。对于堆表,您在读取数据时将始终进行扫描,因此将读取整行,从而使列顺序成为问题。

定义聚簇索引后,将按照您指定的方式对数据进行物理重新排列,使其与列的物理顺序保持一致-此时,物理顺序变得很重要。实际顺序是根据您使用的谓语确定寻求运营商资格的因素。

虽然我不记得在任何地方阅读它,但我假设SQL Server不保证堆的列的物理顺序,而可以保证索引的物理顺序。要回答您的问题,不,定义中的列顺序无关紧要,因为它们在读取数据时无关紧要(请注意,这适用于堆-索引是另一回事)。

更新
实际上,您在问两个问题-“表中列的逻辑顺序是否会影响它们在存储层的物理顺序”是“否”。由元数据定义的逻辑顺序不必与物理顺序相同。我正在寻找的答案是,尽管上面有警告,但CREATE TABLE中的逻辑顺序是否会导致创建时的物理顺序相同(对于堆,我不知道)。


2

根据我所看到和阅读的内容,SQL Server中的列顺序没有区别。无论在CREATE TABLE语句中如何指定列,存储引擎都会在行上放置列。话虽这么说,我敢肯定有一些非常孤立的边缘案例确实很重要,但是我认为您很难在这些问题上找到一个明确的答案。Paul Randal的“ 内部存储引擎“博客类别的帖子是我所了解的有关存储引擎如何工作的所有详细信息的最佳来源。我认为您必须研究存储工作的所有方式以及针对所有用例的矩阵除非要指出适用于我的情况的特殊情况,否则我只是在逻辑上按顺序在CREATE TABLE上对列进行排序,希望对您有所帮助。


1

我明白你的意思。从设计的角度来看,一个表如下所示:

**EMPLOYEES**
EmployeeID
FirstName
LastName
Birthday
SSN 

比看起来像这样的表好很多:

**EMPLOYEES**
LastName
EmployeeID
SSN 
Birthday
FirstName

但是,如果您发出这样的tsql,则数据库引擎实际上并不关心您的逻辑列顺序:

SELECT FirstName, LastName, SSN FROM Employees

引擎只知道FirstName列表在磁盘中的存储位置。

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.