1.建表,报警告,行最大大小超过8060,INSERT和UPDATE可能失败2.插入数据,报超过8060,插入失败3.用ALTER TABLE ALTER COLUMN COL_NAME VARCHAR(MAX)把所有字符串列全改成VARCHAR(MAX),其它列长度加起来共3204.插入数据,报超过8060,插入失败
就这样,被华丽丽的刷了,虽然现在已经解决了,但还是郁闷。
第一个详细解释原因的给250技术分,谨以此分献给MS

解决方案 »

  1.   

    VAHCHAR(MAX)不是到溢出块了么?还能限制?
      

  2.   

    额..http://blog.csdn.net/happyflystone/article/details/4923803
    看一下是这个原因不,鸭子
      

  3.   

    当表中具有可变长度列(如 nvarchar 或 varbinary)时,所有列的总最大长度之和不能大于 8,060 字节。如果每一行中数据的总长度不超过 8,060 字节,就仍可以向表中插入行。解决办法,把较大长度的列改类型为text或者ntext. 
      

  4.   

    参看第三步,已经用第三步的方法把所有字符串都改成VARCHAR(MAX)了,也改成过TEXT,效果一样的。
      

  5.   

    不是你被mssql耍了,是你把mssql耍了。修改列的话,实际上原来的列还是存在的 ,会在列的最后加一个列,那么一行的数据就会更大。sql 2005中,varchar,nvarchar等 单列的数据必须小于8000,但是合并宽度可以大于8060字节。
    text是作为blob数据进行存放
      

  6.   

    一个表中的每一行最多可以包含 8,060 字节。在 SQL Server 2008 中,对于包含 varchar、nvarchar、varbinary、sql_variant 或 CLR 用户定义类型列的表,可以放宽此限制。其中每列的长度仍必须在 8,000 字节的限制内,但是它们的总宽可以超过 8,060 字节的限制。创建和修改 varchar、nvarchar、varbinary、sql_variant 或 CLR 用户定义类型的列以及更新或插入数据时,此限制适用于上述列。当表中的所有固定列和可变列的行的总大小超过限制的 8,060 字节时,SQL Server 将从最大长度的列开始动态将一个或多个可变长度列移动到 ROW_OVERFLOW_DATA 分配单元中的页。
    将列移动到 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 动态管理函数。以上信息,大部分源自网上
      

  7.   

    ms sql当中,数据记录不能跨页存储,每条记录所占的空间不能超过8060字节,也就是表中1条记录的所有字段的长度和不能超过8060。一般解决办法是把一些长度比较大的字段设为text或者image类型。
    你自己检查一下总长度吧。另外是不是建了约束、触发器等对象,这些也会占些空间。
    比如
    create table t_Users(a varchar(8000),b varchar(8000)) 因为a+b=16000的长度已经超过了一条记录的最大存储长度8060 
    这种情况下,表可以正常地被创建,只要你存储/修改数据时 
    datalength(a)+datalength(b)<8060 
    那你的操作就不会有任何问题. 
    否则,操作就会失败
      

  8.   

    一个问题一个问题的来解释,我个人观点是:你并没有仔细计算,不会是sql server出错。
    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能存多少个这样的指针,如果有定长数据的时候,还得出去定长的长度。我相信你大概找出你的问题所在,可惜你没有测试数据,我只能按照我的理解来
      

  9.   

    公布我的答案之前已经有不少大大分析过ALTER TABLE ALTER COLUMN的实现方法是在页中加列,问题就是出在这我前三步都是正常的,问题是在第四步。当已经把列改成VARCHAR(MAX)后,看起来表中所有列的长度(加上指针)不超过400,但同样报长度超长的错误,也就是说,看起来我的修改根本没达到我要的效果,即用溢出页存储大数据。解决方法很简单,ALTER COLUMN后,直接DROP TABLE再CREATE TABLE就可以了。原因没仔细分析,初步推断原因可能有两个,一是由于是在页中新加列,长数据进来没有进到溢出页,而是存储在了原列中,导致存储失败。另一个是更改列长度后,某些统计信息没更新,导致分析插入语句时认为无法插入这么长的数据。综上,25楼最接近答案,我也是改完后又查了一列全部列的长度合,确定很短后才考虑到删表重建的。
    下面就是大家讨论一下,这种情况算不算BUG。
      

  10.   

    楼主,我提个建议,你要想把问题讨论深刻,明白真相,你就把你的测试数据贴上来,证明你说的情况怎么发生的,csdn上有很多高手,要解释你的问题根本不是问题,就算你要精确到每个字节
      

  11.   

    微软在联机帮助文档中已经对这方面作明确的说明:
    在 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
      

  12.   

    看来我只好接250了
    把char 改 varchar
      

  13.   

    这个问题跟此帖有无关系呢?
    http://topic.csdn.net/u/20111107/09/4d3a2d90-33eb-49f2-9710-f119bd04ee62.html
      

  14.   

    只对于2005 或者以上版本1、警告是因为有char列存在,因为char 是定长的,所以他会去计算,varchar 是非定长的只要实际存储的数据没有超过这个上限, 就不会有问题,别且这个8060 不会去考虑varchar,搞成0来统计
    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 或者什么空间设置等问题吧,我相信微软的人应该肯定早发现这个了,或者说这个不算是什么大问题另外个人觉得一直研究这些存储问题也没什么意思。
      

  15.   

    实际中,当字符换比较长的时候 超过50 基本上就没有定长了,所以就不要char 了
    低于50的就算不定长,用char 也比较好,这样让页的长度更稳定。
      

  16.   

    这位同学说的有道理,你怎么肯定楼主计算长度的时候没有按照定长去计算的,你怎么肯定一定是char引起的?一方面楼主没有给出他的测试数据,另外修改数据的方法alter table alter column是标准,是SQL标准就指明了alter table就是这样完成修改的,至于你使用SSMS里的修改列,要知道SSMS那种功能是属于应用层的功能啊,它肯定更贴近用户,而SQL语言更贴近数据,当然不同啦,如果就使用这种修改表的方法,对于一些大型表你试试?删除表重建?给你等到海枯石烂
      

  17.   

    是啊,这么点破事儿弄得神神秘秘的,有装逼嫌疑。再说即便是bug,SQL Server每个补丁都会解决十几个甚至几十个bug,有啥稀奇的。
      

  18.   

    电脑速度不快用MSSQL的路过,暂时没做过什么大的数据库,不知道这个情况,学习了
      

  19.   

    1、当然坑定有char  或者nchar 了 如果是varchar nvarchar 是不会有这种警告的,不信你试试
    2、SSMS的界面操作实际上它也是生成了sql 语句去执行的
    3、这个问题恐怕设计之初就要避免或者解决,一开始插入数据就会报错和警告,难道不修改等海量数据了再说?
      

  20.   

    以前遇到过,改text类型改不了,最后新建一张表,导入数据了事。
    应该不算BUG吧,虽然不好理解,但还是可以用道理解释得通,这只能怪文档中“每行最多包括 8,060 个字节”的描述容易让人误解。
      

  21.   

    MS没有帅你,你应该按下F1,新版本的SQL Server有解决这些问题的,但永远跟不上问题的出现
      

  22.   

    [Quote=引用 66 楼  的回复:]
    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、有多少问题是设计之初就能考虑得到的?也许你接手的时候就是一个这样的表或者数据库,很多时候你只能调整优化,而不是推翻重来
      

  23.   

    原理其实很简单,这与sqlserver数据库底层引擎相关,一般数据库都有一个自己的基本数据块大小,这个块大小一般都是理想记录大小,比如oracle的就是8k.这样,每次找查找一行的时候就只要一次IO就可以了,而且存储的时候,如果一行大小不够一个记录块,就会空着,会保持一个数据块只存放一个行的数据,如果一行数据大于一个记录块,当然会占有多个数据块。一般来讲,对于大数据类型是会单独存放这个列的,不会放在同一个记录中,这也就是为什么有大数据列的时候,如果select *会比较的原因,因为每行都需要2次以上的IO.一行(一个记录)尽量保持在一个数据库记录块中,不仅对于查询,更新很方便,对于建立索引也非常方便,这个道理大家可以思考一下,如果一个记录跨块得情况下,或者更极端点,行与行窜记录块的情况下,效率都是非常低的。
      

  24.   

    這是資料庫每筆記錄的容量上限,(如每筆記錄真的有這樣大)只可將資料庫來升級解決,否則應使用varchar or nvarchar type,來減小每筆記錄所需容量。
      

  25.   

    楼主,是一行里字段(除了页类型的字段,但楼主的字段都是varchar)宽度加起来超过8060了哦,楼主还是仔细查一下看,是不是有某行的所有字段宽度加起来超过了8060,注意是一行的所有字段(除了页类型的字段)宽度加起来哦。