保持内联。自 SQL 2005 以来,SQL Server 已经将 MAX 列存储在单独的“分配单元”中。请参阅表和索引组织。这实际上与将 MAX 列保留在其自己的表中完全相同,但没有显式这样做的任何缺点。
拥有一个显式表实际上会更慢(因为外键约束)和消耗更多空间(因为 DetaiID 重复)。更不用说它需要更多的代码,并且通过......编写代码引入了错误。
替代文字 http://i.msdn.microsoft.com/ms189051.3be61595-d405-4b30-9794-755842d7db7e(en-us,SQL.100).gif
更新
要检查数据的实际位置,一个简单的测试可以显示它:
use tempdb;
go
create table a (
id int identity(1,1) not null primary key,
v_a varchar(8000),
nv_a nvarchar(4000),
m_a varchar(max),
nm_a nvarchar(max),
t text,
nt ntext);
go
insert into a (v_a, nv_a, m_a, nm_a, t, nt)
values ('v_a', N'nv_a', 'm_a', N'nm_a', 't', N'nt');
go
select %%physloc%%,* from a
go
%%physloc%%
伪列将显示行的实际物理位置,在我的例子中是第 200 页:
dbcc traceon(3604)
dbcc page(2,1, 200, 3)
Slot 0 Column 2 Offset 0x19 Length 3 Length (physical) 3
v_a = v_a
Slot 0 Column 3 Offset 0x1c Length 8 Length (physical) 8
nv_a = nv_a
m_a = [BLOB Inline Data] Slot 0 Column 4 Offset 0x24 Length 3 Length (physical) 3
m_a = 0x6d5f61
nm_a = [BLOB Inline Data] Slot 0 Column 5 Offset 0x27 Length 8 Length (physical) 8
nm_a = 0x6e006d005f006100
t = [Textpointer] Slot 0 Column 6 Offset 0x2f Length 16 Length (physical) 16
TextTimeStamp = 131137536 RowId = (1:182:0)
nt = [Textpointer] Slot 0 Column 7 Offset 0x3f Length 16 Length (physical) 16
TextTimeStamp = 131203072 RowId = (1:182:1)
除 TEXT 和 NTEXT 之外的所有列值都内联存储,包括 MAX 类型。
更改表选项并插入新行(sp_tableoption 不影响现有行)后,MAX 类型被逐出到自己的存储中:
sp_tableoption 'a' , 'large value types out of row', '1';
insert into a (v_a, nv_a, m_a, nm_a, t, nt)
values ('2v_a', N'2nv_a', '2m_a', N'2nm_a', '2t', N'2nt');
dbcc page(2,1, 200, 3);
请注意 m_a 和 nm_a 列现在如何成为 LOB 分配单元的文本指针:
Slot 1 Column 2 Offset 0x19 Length 4 Length (physical) 4
v_a = 2v_a
Slot 1 Column 3 Offset 0x1d Length 10 Length (physical) 10
nv_a = 2nv_a
m_a = [Textpointer] Slot 1 Column 4 Offset 0x27 Length 16 Length (physical) 16
TextTimeStamp = 131268608 RowId = (1:182:2)
nm_a = [Textpointer] Slot 1 Column 5 Offset 0x37 Length 16 Length (physical) 16
TextTimeStamp = 131334144 RowId = (1:182:3)
t = [Textpointer] Slot 1 Column 6 Offset 0x47 Length 16 Length (physical) 16
TextTimeStamp = 131399680 RowId = (1:182:4)
nt = [Textpointer] Slot 1 Column 7 Offset 0x57 Length 16 Length (physical) 16
TextTimeStamp = 131465216 RowId = (1:182:5)
为了完整起见,我们还可以强制非最大字段之一:
update a set v_a = replicate('X', 8000);
dbcc page(2,1, 200, 3);
注意 v_a 列是如何存储在 Row-Overflow 存储中的:
Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4
v_a = [BLOB Inline Root] Slot 0 Column 2 Offset 0x19 Length 24 Length (physical) 24
Level = 0 Unused = 99 UpdateSeq = 1
TimeStamp = 1098383360
Link 0
Size = 8000 RowId = (1:176:0)
因此,正如其他人已经评论过的那样,MAX 类型默认情况下是内联存储的,如果它们合适的话。对于许多 DW 项目,这是不可接受的,因为典型的 DW 加载必须扫描或至少范围扫描,因此sp_tableoption ..., 'large value types out of row', '1'
应该使用。请注意,这不会影响现有行,在我的测试中甚至不会影响 index rebuild,因此必须尽早打开该选项。
对于大多数 OLTP 类型加载,尽管 MAX 类型尽可能内联存储这一事实实际上是一个优势,因为 OLTP 访问模式是寻找并且行宽对其几乎没有影响。
尽管如此,关于原始问题:不需要单独的表格。打开该large value types out of row
选项可免费获得相同的开发/测试结果。