解决SQL Server最大列限制1024和8kb记录大小

时间:2012-12-28 14:24:09

标签: sql sql-server

我正在创建一个包含1000列的表。大多数列都是nvarchar类型。表已创建,但带有警告

  

警告:已创建表“Test”,但其最大行大小   超过允许的最大值8060字节。 INSERT或UPDATE到此   如果结果行超出大小限制,表将失败。

表中的大多数列都已包含数据(即99%的列都有数据)。 当我尝试更新310之后的任何列时(所有起始309列都有一些值),它会给出错误:

  

无法创建大于8061的行,该行大于允许值   最大行大小为8060.

我将此数据插入所有起始308列

“Lorem ipsum dolor sit amet,consectetur adipisicing elit。”

当我使用ntext数据类型时,它允许我更新大约450列但超出ntext的范围也不允许我。我必须更新至少700列。哪个SQL Server不允许这样做。我有一个场景,我不能将一些表的列移动到另一个表。

实际上我正在为现有的窗口应用程序工作。这是一个非常大的Windows应用程序。

实际上,我试图在其中插入最多700个nvarchar列数据的表是在运行时动态创建的。 仅在某些情况下,它需要插入400-600列。但一般来说它需要100-200列,我能够轻松处理。

问题是我无法在多个表中拆分此表。因为使用这种结构和表名创建的许多表都保存在另一个表中,即有超过100个具有这种结构的表,并且它们是动态创建的。为了创建表并操纵其数据,正在使用4-5种语言(C#,Java ..),WCF,Windows服务和Web服务也参与其中。

所以我不认为在拆分表之后操作表及其数据会很容易。如果我拆分表,则需要进行大量的结构更改。

所以请建议我解决这个问题的最佳方法。

我也尝试使用稀疏列,如:

Create table ABCD(Id int, Name varchar(100) Sparse, Age int);

我也想过 ColumnStoreIndex ,但我的目的没有解决。

稀疏列允许我为表创建3000列,但它也限制了我的页面大小。

是否可以使用某些临时表或使用任何其他类型的SQL服务器对象来实现它?

8 个答案:

答案 0 :(得分:19)

SQL Server最大列限制

每个短字符串列的字节 8,000

每个GROUP BY的字节数,ORDER BY 8,060

每行字节 8,060

每个索引键的列 16

每个外键的列 16

每个主键的列 16

每个非续表的列 1,024

每张广告表的列数 30,000

每个SELECT语句的列 4,096

每个INSERT语句的列 4096

每UPDATE语句的列数(宽表) 4096

组合 varchar,nvarchar,varbinary,sql_variant,或每行超过 8,060 字节的CLR用户定义类型列时,请考虑以下事项:

超过8,060字节的行大小限制可能会影响性能,因为SQL Server仍然保持每页8 KB的限制。当varchar,nvarchar,varbinary,sql_variant或CLR用户定义类型列的组合超出此限制时,SQL Server数据库引擎将具有最大宽度的记录列移动到ROW_OVERFLOW_DATA分配单元中的另一个页面,同时保持24-原始页面上的字节指针。将大型记录移动到另一个页面会动态发生,因为记录会根据更新操作延长。缩短记录的更新操作可能会导致记录移回IN_ROW_DATA分配单元中的原始页面。此外,查询和执行其他选择操作(例如对包含行溢出数据的大型记录进行排序或连接)会减慢处理时间,因为这些记录是同步处理而不是异步处理。

因此,在设计具有多个varchar,nvarchar,varbinary,sql_variant或CLR用户定义类型列的表时,请考虑可能流过的行的百分比以及此溢出数据可能出现的频率。被查询。如果可能在许多行溢出数据上频繁查询,请考虑对表进行规范化,以便将某些列移动到另一个表。然后可以在异步JOIN操作中查询。

  • 各列的长度仍必须在限制范围内 varchar,nvarchar,varbinary,sql_variant和CLR为8,000个字节 用户定义的类型列。只有他们的组合长度可以超过 表的行数限制为8,060字节。
  • 其他数据类型列的总和,包括char和nchar数据, 必须落在8,060字节的行限制内。大对象数据也是 免除8,060字节的行限制。
  • 聚簇索引的索引键不能包含varchar列 在ROW_OVERFLOW_DATA分配单元中具有现有数据。如果 在varchar列和现有列上创建聚簇索引 数据在IN_ROW_DATA分配单元中,后续插入或 更新列上的操作会将数据推送到行外 失败。有关分配单元的更多信息,请参阅表和 指数组织。
  • 您可以包含包含行溢出数据的列作为键或 非聚簇索引的非键列。
  • 使用稀疏列的表的记录大小限制为8,018 字节。当转换的数据加上现有的记录数据超过时 返回8,018字节,MSSQLSERVER ERROR 576。当列是 在稀疏和非稀疏类型之间转换,数据库引擎保持一个 当前记录数据的副本。这暂时使存储空间翻倍 这是记录所必需的。
  • 获取有关可能包含的表或索引的信息 行溢出数据,使用sys.dm_db_index_physical_stats动态 管理职能。

创建包含n列和数据类型Nvarchar

的表
CREATE Proc [dbo].[CreateMaxColTable_Nvarchar500]
(@TableName nvarchar(100),@NumofCols int)
AS
BEGIN

DECLARE @i INT
DECLARE @MAX INT
DECLARE @SQL VARCHAR(MAX)
DECLARE @j VARCHAR(10)
DECLARE @len int
SELECT @i=1
SELECT @MAX=@NumofCols
SET @SQL='CREATE TABLE ' + @TableName + '('

WHILE @i<=@MAX

BEGIN
select @j= cast(@i as varchar)
SELECT @SQL= @SQL+'X'+@j  +' NVARCHAR(500) , '
SET @i = @i + 1
END
select @len=len(@SQL)

select  @SQL = substring(@SQL,0,@len-1)


SELECT @SQL= @SQL+ ' )'

exec (@SQL)

END

有关详细信息,请访问以下链接:

http://msdn.microsoft.com/en-us/library/ms186981%28SQL.105%29.aspx?PHPSESSID=tn8k5p1s508cop8gr43e1f34d2

http://technet.microsoft.com/en-us/library/ms143432.aspx

但是请你告诉这个场景为什么你需要一个包含这么多列的表? 我认为你应该考虑重新设计数据库。

答案 1 :(得分:12)

这根本不可能。见Inside the Storage Engine: Anatomy of a record

假设你的桌子是这样的。

CREATE TABLE T1(
    col_1 varchar(8000) NULL,
    col_2 varchar(8000) NULL,
    /*....*/
    col_999 varchar(8000) NULL,
    col_1000 varchar(8000) NULL
) 

然后,即使是包含所有NULL值的行,也会使用以下存储空间。

  • 1字节状态位A
  • 1字节状态位B
  • 2字节列数偏移
  • 125字节NULL_BITMAP(每列1 bit 1,000列)

这是一个保证已用完的129个字节(留下7,931个)。

如果任何列的值不是NULL或空字符串,那么您还需要空间

  • 2个字节的可变长度列数(剩余7,929个)。
  • 列偏移数组的2 - 2000字节之间的任何地方。
  • 数据本身。

如果该列和所有后面的列也是零长度,则列偏移数组每个可变长度列消耗2个字节,但除外。因此更新col_1000会强制使用整个2000字节,而更新col_1只会使用2个字节。

因此,您可以使用5个字节的数据填充每个列,并考虑到列偏移数组中的每个2个字节,这个字节最多可添加7,000个字节,在剩余的7,929个字节内。

但是,您存储的数据是102个字节(51 nvarchar个字符),因此可以在行外存储24字节指针,指向行中剩余的实际数据。

FLOOR(7929/(24 + 2)) = 304

最好的情况是,您可以存储304列此长度数据,即如果您要从col_1col_2...进行更新。如果col_1000包含数据,则计算为

FLOOR(5929/24) = 247

对于NTEXT,计算类似,但it can use a 16 byte pointer除外,这样可以将数据压缩到一些额外的列中

FLOOR(7929/(16 + 2)) = 440

需要针对表格跟踪任何SELECT的所有这些关闭行指针,这可能会对性能产生极大的不利影响。

测试此

的脚本
DROP TABLE T1

/* Create table with 1000 columns*/
DECLARE @CreateTableScript nvarchar(max) = 'CREATE TABLE T1('

SELECT @CreateTableScript += 'col_' + LTRIM(number) + ' VARCHAR(8000),'
FROM master..spt_values
WHERE type='P' AND number BETWEEN 1 AND 1000
ORDER BY number

SELECT @CreateTableScript += ')'

EXEC(@CreateTableScript)

/* Insert single row with all NULL*/
INSERT INTO T1 DEFAULT VALUES


/*Updating first 304 cols succeed. Change to 305 and it fails*/
DECLARE @UpdateTableScript nvarchar(max) = 'UPDATE T1 SET  '

SELECT @UpdateTableScript += 'col_' + LTRIM(number) + ' = REPLICATE(1,1000),'
FROM master..spt_values
WHERE type='P' AND number BETWEEN 1 AND 304
ORDER BY number

SET @UpdateTableScript = LEFT(@UpdateTableScript,LEN(@UpdateTableScript)-1)
EXEC(@UpdateTableScript)

答案 2 :(得分:7)

拥有1.000列的表会告诉您数据库设计中存在一些非常错误。

我继承了一个项目,其中一个表有超过500列,经过一年多的时间我仍然无法显着减少它,因为我将不得不重做90%的应用程序。

所以在为时已晚之前重新设计你的数据库。

答案 3 :(得分:2)

每个'非全'表中的最大列数:1,024 每个“宽”表的最大列数:30,000

虽然情况确实如此,但每张桌子需要这个数字? 强烈建议将表垂直分区几次,以获得更好的性能和更容易的开发。

答案 4 :(得分:2)

创建具有n个列和数据类型Nvarchar

的表
CREATE Proc [dbo].[CreateMaxColTable_Nvarchar500]
(@TableName nvarchar(100),@NumofCols int)
AS
BEGIN

DECLARE @i INT
DECLARE @MAX INT
DECLARE @SQL VARCHAR(MAX)
DECLARE @j VARCHAR(10)
DECLARE @len int
SELECT @i=1
SELECT @MAX=@NumofCols
SET @SQL='CREATE TABLE ' + @TableName + '('

WHILE @i<=@MAX

BEGIN
select @j= cast(@i as varchar)
SELECT @SQL= @SQL+'A'+@j  +' NVARCHAR(500) , '
SET @i = @i + 1
END
select @len=len(@SQL)

select  @SQL = substring(@SQL,0,@len-1)


SELECT @SQL= @SQL+ ' )'

exec (@SQL)

END

答案 5 :(得分:1)

SQL Server中的每一行都有限制。

http://msdn.microsoft.com/en-us/library/ms143432.aspx

提供详细信息

答案 6 :(得分:0)

请检查您是否真的需要对所有列使用 nvarchar,nvarchar 占用的字节数是 varchar 的两倍,因此尽可能将列转换为 varchar 可以减少行数据大小并可能帮助您克服此错误。

答案 7 :(得分:-3)

我们有申请,为贷款申请捕获5000个字段。所有字段都依赖于单个主键loanid。我们可以将表拆分为多个,但字段也是动态的。管理员还具有创建更多字段的功能。所以一切都是动态的。他们唯一的好处是贷款和领域之间的一对一关系。

所以,最后我们采用了XML解决方案。整个数据存储在xml文档中。最大的灵活性,但使其难以查询和报告。