問題的現象
在創建表A的時候,出現“信息 511,級別 16,狀態 1,第 5 行 無法創建大小為 的行,該值大於允許的最大值 8060。”的信息提示。很奇怪,網上查了一下,是因為要插入表的數據類型的定義類型小了些,把對應數據類型的大小調整一下就可好些了。
但新問題又出現了,后來由於將表A的數據Select Into的形式寫入表B,表A的數據結構不明確,因為來自表A的數據是動態的列,是經過列轉行的形式轉換的數據信息,不能確定對應列的個數和列的名稱,那怎么辦呢?新思路是能不能將整個表的所有列的數據類型轉換為同一個放大的數據類型,這個問題能解決嗎?只簡單測試一下如何將一個表的所有數據類型轉換為統一的類型的方法
。下面是解決分析:
問題解決
1.對問題現象理解
1).現象描述
一般出現這種現象都是適用sql文件在查詢分析器里建庫的時候,現象一般都是提示:
SQL Server創建表 'xxxx',但其最大行大小(10438)超過了每行的最大字節數(8060)。如果結果行長度超過 8060 字節,則此表中行的 INSERT 或 UPDATE 將失敗。
其中xxxx是你的建的表名,10438是你建表語句中可變長度列(如 nvarchar 或 varbinary)的總長度,8060是SQL Server對行長度的最大限制。
2).現象原因
因為SQL Server創建表語句中可變長度列的總長度超過了SQL Server對行最大長度的限制8060。如果每一行中數據的總長度不超過8060 字節,就仍可以向表中插入行。但是如果數據超過8060 字節,因此系統提示你就會出現插入或更新操作失敗。
錯誤提示:
服務器:信息 511,級別 16,狀態 1,第 5 行 無法創建大小為 的行,該值大於允許的最大值 8060。 語句已終止。
舉個例子:
比如我總共有10塊錢,買A東西可能花1-5塊,買B東西可能花2-3塊,買C東西可能花3-6塊,那我在做預算的時候就要提醒自己,如果ABC三個東西都要花上限的錢,那我的錢可就不夠了,因為5+3+6=14 >10,雖然可能我只花了1+2+3=6塊錢就把ABC全買了。
3).常用方法
修改SQL Server創建表語句中相應的列的數據類型或長度(如將nvarchar格式改成text),讓可變長度列的加和小於8060。這樣可以徹底避免出現上述錯誤發生,當然上述的錯誤並不是必然出現。即放大表的數據存儲空間。
2.對問題解決
針對上面的動態表的情況,如何避免這種問題呢?上面說的方式可以試一下,將表的所有數據列的數據類型統一定義為一個放大的數據類型,當然前提是表的原有數據類型和將要轉換后的數據類型,是可以進行數據類型互轉的,不然將會報錯無法執行。其實是修改表,修改列的類型進行數據類型轉換。
1).創建測試表
--創建測試表 IF OBJECT_ID('TestTB') IS NOT NULL DROP TABLE TestTB CREATE TABLE TestTB ( id int, NAME varchar(60), sex bit ) --插入測試表數據 INSERT TestTB( id, NAME, sex ) VALUES ( 0, -- id - int '99', -- NAME - varchar(60) 1 -- sex - bit ) INSERT TestTB( id, NAME, sex ) VALUES ( '44', -- id - int '22測試數據1', -- NAME - varchar(60) 0 -- sex - bit ) SELECT * FROM TestTB
2).修改測試表的所有數據列
--使用游標遍歷更新表的所有列 declare tb cursor for SELECT sql='alter table ['+d.name+'] alter column ['+a.name+'] varchar(200)' FROM syscolumns a left join systypes b on a.xtype=b.xusertype inner join sysobjects d on a.id=d.id and d.xtype='U' and d.name<>'dtproperties' where d.name='TestTB' and not exists( SELECT 1 FROM sysobjects where xtype='PK' and name in ( SELECT name FROM sysindexes WHERE indid in(SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid) )) --** 主鍵不能修改,所以有 not exists 條件 order by d.name,a.name declare @sql varchar(1000) open tb fetch next from tb into @sql while @@fetch_status = 0 begin exec(@sql) fetch next from tb into @sql end close tb deallocate tb go
其實執行的就是游標遍歷表的所有列更新表更新列的數據類型,游標遍歷執行SQL:
--其中拉取的遍歷執行的SQL SELECT sql='alter table ['+d.name+'] alter column ['+a.name+'] varchar(200)' FROM syscolumns a left join systypes b on a.xtype=b.xusertype inner join sysobjects d on a.id=d.id and d.xtype='U' and d.name<>'dtproperties' WHERE d.NAME='TestTB'
提示:(在使用游標進行表的列的數據類型轉換時,必須是可以轉換的數據列,如:上面的TestTB表,將所有列轉換的varchar(200)改為int類型就會報錯。)
3).查看表的信息
--查詢表的信息,查看是否修改數值類型 SELECT (case when a.colorder=1 then d.name else null end) 表名, a.colorder 字段序號,a.name 字段名, (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) 標識, (case when (SELECT count(*) FROM sysobjects WHERE (name in ( SELECT name FROM sysindexes WHERE (id = a.id) AND (indid in (SELECT indid FROM sysindexkeys WHERE (id = a.id) AND (colid in (SELECT colid FROM syscolumns WHERE (id = a.id) AND (name = a.name))))))) AND (xtype = 'PK'))>0 then '√' else '' end) 主鍵,b.name 類型,a.length 占用字節數, COLUMNPROPERTY(a.id,a.name,'PRECISION') as 長度, isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 小數位數,(case when a.isnullable=1 then '√'else '' end) 允許空, isnull(e.text,'') 默認值,isnull(g.[value], ' ') AS [說明] FROM syscolumns a left join systypes b on a.xtype=b.xusertype inner join sysobjects d on a.id=d.id and d.xtype='U' and d.name<>'dtproperties' left join syscomments e on a.cdefault=e.id left join sys.extended_properties g on a.id=g.major_id AND a.colid=g.minor_id left join sys.extended_properties f on d.id=f.class and f.minor_id=0 --where b.name is not null --WHERE d.name='TestTB' --如果只查詢指定表,加上此條件 WHERE d.NAME LIKE '%TestTB%' --如果只查詢指定表,加上此條件 order by a.id,a.colorder
其實可以在執行游標修改表結構前后調用,查看表的定義信息。
參考博客:
《SQL Server創建表最大行超過限制的解決方法》
《批量將一個庫里的所有表里的char改成nchar類型-數據庫專欄,SQL Server》
《SQLSERVER如何獲取一個數據庫中的所有表的名稱、一個表中所有字段的名稱》