我正在创建一个包含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服务器对象来实现它?
答案 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操作中查询。
创建包含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://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
值的行,也会使用以下存储空间。
NULL_BITMAP
(每列1 bit
1,000列)这是一个保证已用完的129个字节(留下7,931个)。
如果任何列的值不是NULL
或空字符串,那么您还需要空间
如果该列和所有后面的列也是零长度,则列偏移数组每个可变长度列消耗2个字节,但除外。因此更新col_1000
会强制使用整个2000字节,而更新col_1
只会使用2个字节。
因此,您可以使用5个字节的数据填充每个列,并考虑到列偏移数组中的每个2个字节,这个字节最多可添加7,000个字节,在剩余的7,929个字节内。
但是,您存储的数据是102个字节(51 nvarchar
个字符),因此可以在行外存储24字节指针,指向行中剩余的实际数据。
FLOOR(7929/(24 + 2)) = 304
最好的情况是,您可以存储304列此长度数据,即如果您要从col_1
,col_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)
答案 6 :(得分:0)
请检查您是否真的需要对所有列使用 nvarchar,nvarchar 占用的字节数是 varchar 的两倍,因此尽可能将列转换为 varchar 可以减少行数据大小并可能帮助您克服此错误。
答案 7 :(得分:-3)
我们有申请,为贷款申请捕获5000个字段。所有字段都依赖于单个主键loanid。我们可以将表拆分为多个,但字段也是动态的。管理员还具有创建更多字段的功能。所以一切都是动态的。他们唯一的好处是贷款和领域之间的一对一关系。
所以,最后我们采用了XML解决方案。整个数据存储在xml文档中。最大的灵活性,但使其难以查询和报告。