1.建表,报警告,行最大大小超过8060,INSERT和UPDATE可能失败2.插入数据,报超过8060,插入失败3.用ALTER TABLE ALTER COLUMN COL_NAME VARCHAR(MAX)把所有字符串列全改成VARCHAR(MAX),其它列长度加起来共3204.插入数据,报超过8060,插入失败
就这样,被华丽丽的刷了,虽然现在已经解决了,但还是郁闷。
第一个详细解释原因的给250技术分,谨以此分献给MS
就这样,被华丽丽的刷了,虽然现在已经解决了,但还是郁闷。
第一个详细解释原因的给250技术分,谨以此分献给MS
看一下是这个原因不,鸭子
text是作为blob数据进行存放
将列移动到 ROW_OVERFLOW_DATA 分配单元中的页后,将在 IN_ROW_DATA 分配单元中的原始页上维护 24 字节的指针。如果后续操作减小了行的大小,SQL Server 会动态将列移回到原始数据页。此限制不适用于 varchar(max)、nvarchar(max)、varbinary(max)、text、image 或 xml 列。行溢出的事项:
a.超过 8,060 字节的行大小限制可能会影响性能,因为 SQL Server 仍保持每页 8 KB 的限制。
上面提到了 当超出8060的时候 动态移动行的做法。所以我们要在设计表的时候考虑溢出的行的比列以及查询这些溢出行的频率.
如果可能需要经常查询行溢出数据中的许多行,请考虑对表格进行规范化处理,以使某些列移动到另一个表中。然后可以在异步 JOIN 操作中执行查询。
b.其他数据类型列(一般数据列,Text/Image 页类型存储的数据不包含)必须在 8,060 字节的行限制之内.
c.聚集索引的索引键不能包含在 ROW_OVERFLOW_DATA 分配单元中具有现有数据的 varchar 列。
这里举个例子:CREATE table #ksss1
(
id1 varchar(820),
id2 varchar(800),
id3 varchar(800),
id4 varchar(800),
id5 varchar(800),
id6 varchar(800),
id7 varchar(800),
id8 varchar(800),
id9 varchar(800),
id10 varchar(800),
id11 varchar(800),
id12 int
)
--插入测试记录
DECLARE @S VARCHAR(820),@s1 varchar(800)
SELECT @S=ISNULL(@S,'')+LTRIM(A.NUMBER+B.NUMBER),@s1=ISNULL(@S,'')+LTRIM(A.NUMBER+B.NUMBER)
FROM
MASTER..SPT_VALUES A,MASTER..SPT_VALUES B
WHERE A.TYPE='P' AND B.TYPE='P'
exec ('insert #ksss1 select '''+@s+''','''+@s1+''','''+@s1+''','''+@s1+''','''+@s1+
''','''+@s1+''','''+@s1+''','''+@s1+''','''+@s1+''','''+@s1
+''','''+@s1+''',1')
--建立索引
create clustered index OS_ID1_DI2 ON #Ksss1(ID1,id12)
--这里的表的最大行总和长度超过了8060字节, 我插入的这条记录超过了8060字节,当我插入的时候发生页面溢出,id1列溢出到ROW_OVERFLOW_DATA分配单元内.在IN_ROW_DATA 分配单元中留下了个指针
--接着我为表建立聚集索引
create clustered index OS_ID1_DI2 ON #Ksss1(ID1)
--这个时候再插入上面那条记录,也可以插入成功---
--这里发生的过程其实是这样的:再创建聚集索引后,数据发生“页面搬家”-此时最为键列的ID1列已经不在ROW_OVERFLOW_DATA分配单元内,它重新回到了IN_ROW_DATA 分配单元中,换成了其他非键列溢出.
--这样以后每次插入记录ID1不会再溢出.d.对于使用稀疏列的表,记录大小限制为 8,018 字节。
e.若要获得有关可能包含行溢出数据的表或索引的信息,请使用 sys.dm_db_index_physical_stats 动态管理函数。以上信息,大部分源自网上
你自己检查一下总长度吧。另外是不是建了约束、触发器等对象,这些也会占些空间。
比如
create table t_Users(a varchar(8000),b varchar(8000)) 因为a+b=16000的长度已经超过了一条记录的最大存储长度8060
这种情况下,表可以正常地被创建,只要你存储/修改数据时
datalength(a)+datalength(b)<8060
那你的操作就不会有任何问题.
否则,操作就会失败
1.建表的时候发出警告,是因为你的表记录的定长长度并没有超过8060的限制,但是由于有变长数据类型的列,而sql server 2005可以允许行溢出数据的存在和大对象数据的存在下超过8060的限制,所以只是警告(而不是错误)可能超过8060而且在某些情况下会影响插入和更新,具体哪些情况,我在后面会说。2.插入数据失败。原因是数据确实超过8060字节(其实除了真实的数据以后,还有几个字节的额外数据,不过我个人觉得不是你插入失败的真正原因)。3.修改表结构,一般来说,如果修改成比原来长度小的数据类型是,实际存储的长度会保持原来的长度,比如int是4字节,修改为smallint后依然占用4字节。但这不是这里的关键,我不知道你的表的具体结构,无法做这方面的判断,也不清楚你所说320是否只是定长数据的长度,我暂且认为320是定长数据长度。4.再插入数据,还是超过8060失败。这里我就仔细说一下,sql可以通过行溢出数据和大对象数据超过8060的限制,但是这些数据并不是没有代价,如果是行溢出数据,那么超过的部分会被存储到行溢出数据区里,但是原始数据还必须维护24字节的指针,而大对象数据也要维护16字节的指针。这样一来,虽然可以超过限制,但是也带来了一些你容易计算失误的地方,我举个例子。if object_id('test1') is not null
drop table test1;
gocreate table test1
(
a char(3000),
b char(3000),
c char(2040),
d varchar(max)
);
go
/*
执行结果:
警告: 已经创建表 'test1',但是它的最大行大小超过了允许的最大字节数 8060。如果得到的行超过此大小限制,则对此表的 INSERT 或 UPDATE 操作将失败。
*/
insert into test1(a, b, c, d)
values(replicate('a', 3000), replicate('b', 3000), replicate('c', 2040), replicate('d', 9))
/*
执行结果
(1 行受影响)
数据长度= 3000 + 3000 + 2040 + 9 + 2(状态位) + 2(列位置) +
2(列数目) + 1(空位图) + 2(变长列数目) + 2(第一个变长列位置)
= 8060字节
*/
insert into test1(a, b, c, d)
values(replicate('a', 3000), replicate('b', 3000), replicate('c', 2040), replicate('d', 10))
/*
消息 511,级别 16,状态 1,第 1 行
不能创建大小为 8061 的行,该大小大于所允许的最大值 8060。因为最后一个变长的插入使得行超出8060,原本应该溢出,但是溢出后需要增加24字节还是超出8060所以报错
*/
if object_id('test1') is not null
drop table test1;
gocreate table test1
(
a char(3000),
b char(3000),
c char(2025),
d varchar(max)
);
go
insert into test1(a, b, c, d)
values(replicate('a', 3000), replicate('b', 3000), replicate('c', 2025), replicate('d', 24));
go
/*
执行结果
(1 行受影响)
数据长度= 3000 + 3000 + 2025 + 24 + 2(状态位) + 2(列位置) +
2(列数目) + 1(空位图) + 2(变长列数目) + 2(第一个变长列位置)
= 8060字节
*/
insert into test1(a, b, c, d)
values(replicate('a', 3000), replicate('b', 3000), replicate('c', 2025), replicate('d', 25));
go
/*
执行结果
(1 行受影响)
虽然加上额外的开销已经到达8061字节,但是数据溢出只用维护24字节指针,因此刚好8060字节
*/综上所述,我觉得,你的问题是因为维护溢出行的指针使得长度超过8060,你可以计算一下,如果一个溢出行的指针24字节,8060能存多少个这样的指针,如果有定长数据的时候,还得出去定长的长度。我相信你大概找出你的问题所在,可惜你没有测试数据,我只能按照我的理解来
下面就是大家讨论一下,这种情况算不算BUG。
在 SQL Server 2005 中,每个数据库最多可包含 20 亿个表,每个表可包含 1,024 列。表的行数及总大小仅受可用存储空间的限制。每行最多包括 8,060 个字节。对于带 varchar、nvarchar、varbinary 或 sql_variant 列(导致已定义表的总宽超过 8,060 字节)的表,此限制将放宽。其中每列的长度仍必须在 8,000 字节的限制内,但是它们的总宽可能超过表的 8,060 字节的限制。
在SQL Server 2008 也有类似的说明在 SQL Server 2008 中,每个数据库最多可包含 20 亿个表。一个表中最多可以包含 30,000 个列,其中最多有 1024 个非稀疏列和计算列。表的行数及总大小仅受可用存储空间的限制。每行最多包括 8,060 个字节。对于带 varchar、nvarchar、varbinary 或 sql_variant 列(导致已定义表的总宽超过 8,060 字节)的表,此限制将放宽。其中每列的长度仍必须在 8,000 字节的限制内,但是它们的总宽可能超过表的 8,060 字节的限制。
如果应用于SQL Server 2012需要参照这份说明:
<Maximum Capacity Specifications for SQL Server>
http://msdn.microsoft.com/en-us/library/ms143432.aspx
把char 改 varchar
http://topic.csdn.net/u/20111107/09/4d3a2d90-33eb-49f2-9710-f119bd04ee62.html
create table test1
(
a varchar(1000),
b char(8000),
c char(53),
d varchar(1000)
);
go
--这个只会警告
--但是下面的就会报错
create table test2
(
a int,
b char(8000),
c char(53),
d varchar(1000)
);
go
2、如果是test1 的情况,那么这里超过了肯定就报错(另外2008 r2 创建的时候无视警告,插入前直接会报错,就算插入的值的长度合计远远不够8060)3、你的修改肯定是 alter table alter column 语句修改的如果点表--右键设计--修改数据类型,这样就不存在这个问题了因为这样修改数据类型的时候,点生成更改脚本 看里面的脚本是新建个表,然后移植数据,在删除原来的表,在重命名刚才的表。所以不要觉得写脚本搞就牛逼哄哄的,点图形界面就很挫了,对于上面来说只要完成他们交给的任务就行,所以怎么快怎么有效的来更方便。
总结: 可能alter table alter column 存在bug 或者什么空间设置等问题吧,我相信微软的人应该肯定早发现这个了,或者说这个不算是什么大问题另外个人觉得一直研究这些存储问题也没什么意思。
低于50的就算不定长,用char 也比较好,这样让页的长度更稳定。
2、SSMS的界面操作实际上它也是生成了sql 语句去执行的
3、这个问题恐怕设计之初就要避免或者解决,一开始插入数据就会报错和警告,难道不修改等海量数据了再说?
应该不算BUG吧,虽然不好理解,但还是可以用道理解释得通,这只能怪文档中“每行最多包括 8,060 个字节”的描述容易让人误解。
1、当然坑定有char 或者nchar 了 如果是varchar nvarchar 是不会有这种警告的,不信你试试
2、SSMS的界面操作实际上它也是生成了sql 语句去执行的
3、这个问题恐怕设计之初就要避免或者解决,一开始插入数据就会报错和警告,难道不修改等海量数据了再说?1、T-SQL的数据类型只有char nchar nvarchar nvarchar?int datetime这些不是么,如果用这些楼主会算错长度?你这个逻辑上就不混淆,说肯定是char,原因是如果是varchar nvarchar就不会有这种警告...2、没说SSMS操作不是生成sql去执行,而是说SSMS的操作是组合的语句,那不是原本意义上的修改表的操作,你也说了它是先删除在创建的方法,这种方法本身就是不高效的,它只能面对那些不懂的修改细节,只求结果的用户,所以这种方法是用户层面的,作为操作数据库来说,这种方式远远不够,也不合适,只是让你操作简单一点而已。3、有多少问题是设计之初就能考虑得到的?也许你接手的时候就是一个这样的表或者数据库,很多时候你只能调整优化,而不是推翻重来