Pet*_*ger 16 sql-server varchar normalization
我想在MS SQL Server 2005中创建一个表来记录某些系统操作的详细信息.从下面的表格设计中可以看出,除了之外的每一列Details都是不可为空的.
CREATE TABLE [Log]
(
[LogID] [int] IDENTITY(1,1) NOT NULL,
[ActionID] [int] NOT NULL,
[SystemID] [int] NOT NULL,
[UserID] [int] NOT NULL,
[LoggedOn] [datetime] NOT NULL,
[Details] [varchar](max) NULL
)
Run Code Online (Sandbox Code Playgroud)
因为Details列中不会始终包含数据.将此列存储在单独的表中并提供指向它的链接是否更有效?
CREATE TABLE [Log]
(
[LogID] [int] IDENTITY(1,1) NOT NULL,
[ActionID] [int] NOT NULL,
[SystemID] [int] NOT NULL,
[UserID] [int] NOT NULL,
[LoggedOn] [datetime] NOT NULL,
[DetailID] [int] NULL
)       
CREATE TABLE [Detail]
(
[DetailID] [int] IDENTITY(1,1) NOT NULL,
[Details] [varchar](max) NOT NULL
)
Run Code Online (Sandbox Code Playgroud)
对于较小的数据类型,我不会真正考虑它,但对于varchar(max)这样做有助于保持表格大小更小?或者我只想尝试智能数据库并实现一切?
Rem*_*anu 28
保持内联.自SQL 2005以来,SQL Server已将MAX列存储在单独的"分配单元"中.请参阅表和索引组织.这实际上与将MAX列保留在自己的表中完全相同,但没有明确这样做的任何缺点.
拥有一个显式表实际上会更慢(因为外键约束)并消耗更多空间(因为DetaiID重复).更不用说它需要更多的代码,并且...编写代码引入了错误.
更新
要检查数据的实际位置,可以使用简单的测试来显示:
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
Run Code Online (Sandbox Code Playgroud)
该%%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)   
Run Code Online (Sandbox Code Playgroud)
除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);
Run Code Online (Sandbox Code Playgroud)
注意m_a和nm_a列现在是LOB分配单元的Textpointer:
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)                    
Run Code Online (Sandbox Code Playgroud)
为了完成,我们还可以强制排除非最大字段之一:
update a set v_a = replicate('X', 8000);
dbcc page(2,1, 200, 3);
Run Code Online (Sandbox Code Playgroud)
请注意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) 
Run Code Online (Sandbox Code Playgroud)
因此,正如其他已经评论过的那样,MAX类型默认存储为内联,如果它们适合的话.对于许多DW项目而言,这是不可接受的,因为典型的DW负载必须扫描或至少扫描范围,因此sp_tableoption ..., 'large value types out of row', '1'应该使用.请注意,这不会影响现有行,在我的测试中甚至不会对索引重建产生影响,因此必须尽早启用该选项.
对于大多数OLTP类型的加载,尽管MAX类型在可能的情况下以内联方式存储这一事实实际上是一个优点,因为要搜索OLTP访问模式并且行宽对它几乎没有影响.
尽管如此,关于原始问题:没有必要使用单独的表格.打开large value types out of row选项可以免费获得开发/测试,从而获得相同的结果.
Cad*_*oux 10
矛盾的是,如果您的数据通常少于8000个字符,我会将其存储在一个单独的表中,而如果数据大于8000个字符,我会将它保存在同一个表中.
这是因为如果SQL允许行位于单页中,SQL Server会将数据保留在页面中,但是当数据变大时,它会像TEXT数据类型一样将其移出,并且只留下指针.行.因此,对于一堆3000个字符的行,每页的行数较少,这实际上效率很低,但对于一堆12000个字符的行,数据不在行中,因此它实际上更有效.
话虽如此,通常你会有各种各样的长度混合,因此我会把它移到自己的桌子上.这使您可以灵活地将此表移动到其他文件组等.
请注意,您还可以使用sp_tableoption 指定它以强制数据不在行中.varchar(max)基本上类似于TEXT数据类型,它默认为行中的数据(对于varchar(max)),而不是默认为行外的数据(对于TEXT).
|   归档时间:  |  
           
  |  
        
|   查看次数:  |  
           6822 次  |  
        
|   最近记录:  |