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

Ban*_*yan 22 sql sql-server

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

警告:已创建表"Test",但其最大行大小超过允许的最大值8060字节.如果生成的行超出大小限制,则对此表的INSERT或UPDATE将失败.

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

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

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

"Lorem ipsum dolor坐下来,精神恍惚."

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

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

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

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

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

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

我也试过使用Sparse Column,如:

Create table ABCD(Id int, Name varchar(100) Sparse, Age int);
Run Code Online (Sandbox Code Playgroud)

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

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

有没有办法使用一些临时表或使用任何其他类型的SQL服务器对象来实现它?

小智 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

组合每行超过8,060字节的varchar,nvarchar,varbinary,sql_variant或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字节行限制的限制.
  • 聚簇索引的索引键不能包含在ROW_OVERFLOW_DATA分配单元中具有现有数据的varchar列.如果在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
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请访问以下链接:

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

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

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


Mar*_*ith 12

这根本不可能.请参阅存储引擎内部:记录剖析

假设你的桌子是这样的.

CREATE TABLE T1(
    col_1 varchar(8000) NULL,
    col_2 varchar(8000) NULL,
    /*....*/
    col_999 varchar(8000) NULL,
    col_1000 varchar(8000) NULL
) 
Run Code Online (Sandbox Code Playgroud)

然后,即使是包含所有NULL值的行也将使用以下存储.

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

所以这已经被保证用了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
Run Code Online (Sandbox Code Playgroud)

所以,最好的情况是,你可以存储这个长度数据的304列,那就是如果你是从更新col_1,col_2,....如果col_1000包含数据则计算结果为

FLOOR(5929/24) = 247
Run Code Online (Sandbox Code Playgroud)

因为NTEXT计算是类似的,除了它可以使用16字节指针,这将允许您将数据压缩到几个额外的列

FLOOR(7929/(16 + 2)) = 440
Run Code Online (Sandbox Code Playgroud)

对于任何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)
Run Code Online (Sandbox Code Playgroud)


小智 7

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

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

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

  • 就个人而言,我讨厌这样的答案.我几次遇到表中列数限制的问题.这有很好的理由,特别是在对数据进行非规范化分析时.遗憾的是,数据库具有如此神秘的限制. (18认同)
  • 正如我所说,当您对数据进行非规范化以进行分析时,列数很重要.几年前,用于KDD竞赛的数据("知识发现和数据挖掘")是具有数十万列的生物遗传数据.另一个例子是用于文本分析的文档术语矩阵.而且,将历史数据放在一起通常会产生数百或数千列. (10认同)
  • @GordonLinoff老实说,我非常感兴趣的是为什么一个表永远不需要1000列.你能否解释为什么需要它?我真的可以想到一个好的,很想听到一个反例. (3认同)