2023年8月3日发(作者:)

sqlserver中⼆进制截断快速判断字段第⼀篇⽂章,把我在做项⽬中遇到的⼆进制截断问题的解决⽅式贡献出来,希望对⼤家有所帮助,废话不多说,下⾯直接贴效果,最后把代码奉上,已经封装为存储过程–创建测试表,简单起见,表结构⾮常简单,但是⼯作中的表字段⾮常多,如果写表的时候报⼆进制截断的错误,是⼀件让⼈很恼⽕的事情,特别是系统上线初期,寻找起来很痛苦,楼主是个懒⼈,出此主意if object_id(‘tempdb…#temp’) is not nulldrop table #tempcreate table #temp(id int identity(1,1) primary key,name char(10))if object_id(‘tempdb…#t’) is not nulldrop table #tselect ‘尼古拉斯凯奇赵四同学’ as name into #tinsert into #temp select * from #t–报错如下–消息 8152,级别 16,状态 14,第 14 ⾏–将截断字符串或⼆进制数据。–语句已终⽌。使⽤存储过程exec GET_tb_col_max ‘#temp’,’#t’,’’,’’输⼊为:此过程⼀共有四个参数,被写⼊表和选择表⽀持同是时实体表和同时是临时表的场景;使⽤过程中的建议和场景1. 写⼊表和选择表同时是实体表,可以⼀次性判断出所有⼆进制截断的字段信息,并且给出修改建议的sql2. 写⼊表和选择表同时是临时表的场景,对⼆进制截断的判断能⼒同第1场景3. 如果是临时表和实体表同时存在,建议通过select * into 实体表名称 from 临时表,这种⽅式将临时表转换到实体表,然后采⽤第1种场景判断4. 如何该过程有不够强⼤的地⽅还请⼤家通过qq联系我,我尽⼒完善5. 临时表名称不能使⽤#tmp 因为判断过程中使⽤到了这个临时表名称6. 另外⼤家经常遇到varchar或者其它字符型数据转换到某个值类型出错的情况,下篇⽂章我来说下如何快速定位这样的字段,解决⼤家实施过程中的找这样问题的烦恼ALTER PROCEDURE [dbo].[GET_tb_col_max]@tbname varchar(200), --被写⼊表要求⽀持临时表检索@sel_tbname varchar(200)=’’,–选择列表可以为空,为空则查询表定义@in_tb_col_type varchar(30)=’’,–1、插⼊表特定类型字段检索异常,D代表数值类型,没有明确int,decimal,float,numeric,等数值类型–2、C:代表字符类型–3、⾮C且⾮D,则检索指定特定数据类型–4、如果为空字符 ‘’ 则代表不查找特定异常@sel_tb_col_type varchar(30)=’’–此字段含义同上USE [CR_JMPZZX_V4_BEW]GO/****** Object: StoredProcedure [dbo].[GET_tb_col_max] Script Date: 2019-04-22 13:57:28 ******//****** Object: StoredProcedure [dbo].[GET_tb_col_max] Script Date: 2019-04-22 13:57:28 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGO--EXEC [GET_tb_col_max] 'spzl_pivas','cwrm','',''ALTER PROCEDURE [dbo].[GET_tb_col_max]

@tbname varchar(200), --被写⼊表要求⽀持临时表检索

@sel_tbname varchar(200)='',--选择列表可以为空,为空则查询表定义@in_tb_col_type varchar(30)='',--1、插⼊表特定类型字段检索异常,D代表数值类型,没有明确int,decimal,float,numeric,等数值类型

--2、C:代表字符类型 --3、⾮C且⾮D,则检索指定特定数据类型 --4、如果为空字符 '' 则代表不查找特定异常@sel_tb_col_type varchar(30)=''--此字段含义同上AS BEGIN

--set @tbname='orders_cq'

--获取被写⼊表的列定义

--SELECT dj_sn FROM tt

DECLARE @col_length_max INT ,@col_max NVARCHAR(max),@col_min NVARCHAR(MAX),@sql_statement nvarchar(max)

IF OBJECT_ID('tempdb..#tmp') IS NOT NULL

DROP TABLE #tmp

CREATE TABLE #tmp(tbname VARCHAR(200), colname VARCHAR(200), column_id INT , col_type_name VARCHAR(200), max_length INT , col_max NVARCHAR(MAX), col_min NVARCHAR(max), col_length_max INT, precision INT, scale INT, message_error VARCHAR(200), sql_statement VARCHAR(500))--如果@tbname和@sel_tbname 都是临时表

IF CHARINDEX('#',@tbname)=1 AND CHARINDEX('#',@sel_tbname)=1 BEGIN

PRINT '全部都是临时表' select @tbname='[tempdb]..'+@tbname,@sel_tbname='[tempdb]..'+@sel_tbname print @tbname print @sel_tbname INSERT INTO #tmp(tbname,colname,column_id,col_type_name,max_length,precision,scale) SELECT AS tbname, AS colname,_id, AS col_type_name,

_length,ion,

FROM s a JOIN s b ON _id=_id AND _id=object_id(@tbname)

JOIN c ON _type_id=_type_id

WHERE IN(SELECT name FROM s WHERE object_id=OBJECT_ID(@sel_tbname)) print 'iiii'

END

ELSE BEGIN PRINT '不全部都是临时表' INSERT INTO #tmp(tbname,colname,column_id,col_type_name,max_length,precision,scale) SELECT AS tbname, AS colname,_id, AS col_type_name,

_length,ion,

FROM s a JOIN s b ON _id=_id AND _id=object_id(@tbname)

JOIN c ON _type_id=_type_id

WHERE IN(SELECT name FROM s WHERE object_id=OBJECT_ID(@sel_tbname))

END

--SELECT *FROM s WHERE object_id=object_id('cr_dj_jmjj35') --SELECT *FROM s WHERE object_id=object_id('cr_dj_jmjj35')

--希望追加四个字段,最长度时的值,最⼤长度时的值,最⼤值长度,错误提醒,建议的sql语句 ,遍历插⼊表@sel_tbname

-- SELECT * FROM #tmp RETURN

IF EXISTS(SELECT *FROM sors WHERE cursor_name='cur_cq')

BEGIN

CLOSE cur_cq

DEALLOCATE cur_cq

END

DECLARE @colname VARCHAR(200),@sql NVARCHAR(2000),@col_type_name VARCHAR(200),@precision INT,@scale INT

DECLARE cur_cq CURSOR FOR SELECT colname,col_type_name,precision,scale FROM #tmp

OPEN cur_cq

FETCH next FROM cur_cq INTO @colname,@col_type_name,@precision,@scale

WHILE @@FETCH_STATUS=0

BEGIN

if @in_tb_col_type='' and @sel_tb_col_type='' begin

--1、获取表@sel_tbname 中的列@colname 最⼤值,最⼤值长度

SET @sql=N'select @col_min=min('+RTRIM(QUOTENAME(@colname))+') ,@col_max=max('+RTRIM(QUOTENAME(@colname))+'),@col_length_max=max(datalength(rtrim('+QUOTENAME(@colname)+'))) from '+CASE WHEN CHARINDEX('#',@sel_tbname)=1 THEN 'tempdb..' ELSE '' END + @sel_tbname

PRINT @sql

EXEC sp_executesql @sql,N'@col_min nvarchar(max) output,@col_max nvarchar(max) output,@col_length_max int output',

@col_max =@col_max OUTPUT,@col_length_max=@col_length_max OUTPUT,@col_min=@col_min output

--output 关键字前⾯的变量是输出变量,⼀定要在代码中声明 --如果是字符类型数据,则最⼤值重新定义为,最⼤字节长度时的值 IF @col_type_name IN ('varchar','char','nchar')

BEGIN --sp_exectutesql 同时存在输⼊参数和输出参数

SET @sql='SELECT @col_max='+RTRIM(QUOTENAME(@colname))+ ' FROM '+CASE WHEN CHARINDEX('#',@sel_tbname)=1 THEN 'tempdb..' ELSE '' END + @sel_tbname +' WHERE DATALENGTH('+RTRIM(QUOTENAME(@colname))+')=@col_length_max' PRINT @sql EXEC sp_executesql @sql,N'@col_max nvarchar(max) output,@col_length_max int',@col_max =@col_max OUTPUT,@col_length_max =@col_length_max END

--更新#tmp 表 字段col_max,col_length_max

print '@col_type_name ='+cast(@col_type_name as varchar(max))

print '@precision ='+cast(@precision as varchar(max))

print '@scale ='+cast(@scale as varchar(max))

PRINT '@col_min ='+cast(@col_min as varchar(max))

PRINT '@col_max ='+cast(@col_max as varchar(max))

PRINT '@col_length_max ='+cast(@col_length_max as varchar(max))

--if @col_type_name='decimal' and @col_max<=0.0

--continue

SET @sql=N'update a set _min=@col_min, _max=@col_max , col_length_max=@col_length_max,

message_error=case

when col_type_name=''int'' and @col_max>power(cast(2 as bigint),31) then ''整数值超过最⼤值2147483648''

when col_type_name=''int'' and @col_min<-power(cast(2 as bigint),31)+1 then ''整数值最⼩值为-2147483647,请检查''

when col_type_name=''decimal'' and CAST(SUBSTRING(@col_max,1,CHARINDEX(''.'',@col_max)-1) AS bigint)>CAST(REPLICATE(9,precision-scale) AS bigint)

then ''decimal数据类型,整数部分溢出''

when col_type_name=''decimal'' and CAST(SUBSTRING(@col_max,1,CHARINDEX(''.'',@col_max)-1) AS bigint)>CAST(REPLICATE(9,precision-scale) AS bigint)

then ''decimal数据类型,整数部分溢出''

when col_type_name=''numeric'' and CAST(SUBSTRING(@col_max,1,CHARINDEX(''.'',@col_max)-1) AS bigint)>CAST(REPLICATE(9,precision-scale) AS bigint)

then ''decimal数据类型,整数部分溢出''

when col_type_name=''numeric'' and CAST(SUBSTRING(@col_max,1,CHARINDEX(''.'',@col_max)-1) AS bigint)>CAST(REPLICATE(9,precision-scale) AS bigint)

then ''numeric数据类型,整数部分溢出''

when col_type_name not in(''int'',''decimal'',''numeric'',''datetime'') and @col_length_max>max_length then ''字符超出''

else '''' end

FROM #tmp a where 1= case when _type_name in(''decimal'',''numeric'') and CHARINDEX(''.'',@col_max)=0 then 0 else 1 end

and colname=' +''''+@colname+''''

PRINT @sql

EXEC sp_executesql @sql,N'@col_max nvarchar(max),@col_length_max int,@col_min nvarchar(max)',

@col_max=@col_max,@col_length_max=@col_length_max,@col_min=@col_min @col_max=@col_max,@col_length_max=@col_length_max,@col_min=@col_min

end

else if @in_tb_col_type<>'' begin --如果插⼊表,字段类型不为空 --场景1:插⼊表字段是数值型,但是不知道准确的数据类型,选择列表的字段类型确定,⽐如说,选择列表字段数据类型varchar if @in_tb_col_type='D' and @sel_tb_col_type='varchar' begin update a set e_error='数据类型不⼀致,请检查' from #tmp a where colname=@colname and col_type_name in('numeric','decimal','float','int','bigint','bit','smallint','byte') and exists(SELECT b.* FROM s b join c on _type_id=_type_id WHERE _id=OBJECT_ID(@sel_tbname) and =@colname and ='varchar' )

end

end

else begin print '先保留'

end

-- -- 找出orders_hld表numeric 类型字段--select , as col_type_name,_col_type from-- s a join b on _type_id=_type_id

--and _id=object_id('orders_hld') and in('numeric','decimal')

--join (select as colname, as in_col_type from s c join d on _type_id=_type_id

--and _id=object_id('jmpz_orders_v_hld') and ='varchar') e on =e

FETCH next FROM cur_cq INTO @colname,@col_type_name,@precision,@scale

END

CLOSE cur_cq

DEALLOCATE cur_cq

update a set _statement='alter table '+tbname+' alter column '+colname+' '+col_type_name+'('+cast(col_length_max*2 as varchar(200))+')' from #tmp a where message_error='字符超出'

SELECT *FROM #tmp WHERE message_error<>''

END

本⽂纯原创,如果疑问可以联系我qq :2625526306,另外如果代码有问题,⼤家补充,可以⽀持临时表和实体表的判断 ,转载需要备注来源,谢谢请尊重原创

2023年8月3日发(作者:)

sqlserver中⼆进制截断快速判断字段第⼀篇⽂章,把我在做项⽬中遇到的⼆进制截断问题的解决⽅式贡献出来,希望对⼤家有所帮助,废话不多说,下⾯直接贴效果,最后把代码奉上,已经封装为存储过程–创建测试表,简单起见,表结构⾮常简单,但是⼯作中的表字段⾮常多,如果写表的时候报⼆进制截断的错误,是⼀件让⼈很恼⽕的事情,特别是系统上线初期,寻找起来很痛苦,楼主是个懒⼈,出此主意if object_id(‘tempdb…#temp’) is not nulldrop table #tempcreate table #temp(id int identity(1,1) primary key,name char(10))if object_id(‘tempdb…#t’) is not nulldrop table #tselect ‘尼古拉斯凯奇赵四同学’ as name into #tinsert into #temp select * from #t–报错如下–消息 8152,级别 16,状态 14,第 14 ⾏–将截断字符串或⼆进制数据。–语句已终⽌。使⽤存储过程exec GET_tb_col_max ‘#temp’,’#t’,’’,’’输⼊为:此过程⼀共有四个参数,被写⼊表和选择表⽀持同是时实体表和同时是临时表的场景;使⽤过程中的建议和场景1. 写⼊表和选择表同时是实体表,可以⼀次性判断出所有⼆进制截断的字段信息,并且给出修改建议的sql2. 写⼊表和选择表同时是临时表的场景,对⼆进制截断的判断能⼒同第1场景3. 如果是临时表和实体表同时存在,建议通过select * into 实体表名称 from 临时表,这种⽅式将临时表转换到实体表,然后采⽤第1种场景判断4. 如何该过程有不够强⼤的地⽅还请⼤家通过qq联系我,我尽⼒完善5. 临时表名称不能使⽤#tmp 因为判断过程中使⽤到了这个临时表名称6. 另外⼤家经常遇到varchar或者其它字符型数据转换到某个值类型出错的情况,下篇⽂章我来说下如何快速定位这样的字段,解决⼤家实施过程中的找这样问题的烦恼ALTER PROCEDURE [dbo].[GET_tb_col_max]@tbname varchar(200), --被写⼊表要求⽀持临时表检索@sel_tbname varchar(200)=’’,–选择列表可以为空,为空则查询表定义@in_tb_col_type varchar(30)=’’,–1、插⼊表特定类型字段检索异常,D代表数值类型,没有明确int,decimal,float,numeric,等数值类型–2、C:代表字符类型–3、⾮C且⾮D,则检索指定特定数据类型–4、如果为空字符 ‘’ 则代表不查找特定异常@sel_tb_col_type varchar(30)=’’–此字段含义同上USE [CR_JMPZZX_V4_BEW]GO/****** Object: StoredProcedure [dbo].[GET_tb_col_max] Script Date: 2019-04-22 13:57:28 ******//****** Object: StoredProcedure [dbo].[GET_tb_col_max] Script Date: 2019-04-22 13:57:28 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGO--EXEC [GET_tb_col_max] 'spzl_pivas','cwrm','',''ALTER PROCEDURE [dbo].[GET_tb_col_max]

@tbname varchar(200), --被写⼊表要求⽀持临时表检索

@sel_tbname varchar(200)='',--选择列表可以为空,为空则查询表定义@in_tb_col_type varchar(30)='',--1、插⼊表特定类型字段检索异常,D代表数值类型,没有明确int,decimal,float,numeric,等数值类型

--2、C:代表字符类型 --3、⾮C且⾮D,则检索指定特定数据类型 --4、如果为空字符 '' 则代表不查找特定异常@sel_tb_col_type varchar(30)=''--此字段含义同上AS BEGIN

--set @tbname='orders_cq'

--获取被写⼊表的列定义

--SELECT dj_sn FROM tt

DECLARE @col_length_max INT ,@col_max NVARCHAR(max),@col_min NVARCHAR(MAX),@sql_statement nvarchar(max)

IF OBJECT_ID('tempdb..#tmp') IS NOT NULL

DROP TABLE #tmp

CREATE TABLE #tmp(tbname VARCHAR(200), colname VARCHAR(200), column_id INT , col_type_name VARCHAR(200), max_length INT , col_max NVARCHAR(MAX), col_min NVARCHAR(max), col_length_max INT, precision INT, scale INT, message_error VARCHAR(200), sql_statement VARCHAR(500))--如果@tbname和@sel_tbname 都是临时表

IF CHARINDEX('#',@tbname)=1 AND CHARINDEX('#',@sel_tbname)=1 BEGIN

PRINT '全部都是临时表' select @tbname='[tempdb]..'+@tbname,@sel_tbname='[tempdb]..'+@sel_tbname print @tbname print @sel_tbname INSERT INTO #tmp(tbname,colname,column_id,col_type_name,max_length,precision,scale) SELECT AS tbname, AS colname,_id, AS col_type_name,

_length,ion,

FROM s a JOIN s b ON _id=_id AND _id=object_id(@tbname)

JOIN c ON _type_id=_type_id

WHERE IN(SELECT name FROM s WHERE object_id=OBJECT_ID(@sel_tbname)) print 'iiii'

END

ELSE BEGIN PRINT '不全部都是临时表' INSERT INTO #tmp(tbname,colname,column_id,col_type_name,max_length,precision,scale) SELECT AS tbname, AS colname,_id, AS col_type_name,

_length,ion,

FROM s a JOIN s b ON _id=_id AND _id=object_id(@tbname)

JOIN c ON _type_id=_type_id

WHERE IN(SELECT name FROM s WHERE object_id=OBJECT_ID(@sel_tbname))

END

--SELECT *FROM s WHERE object_id=object_id('cr_dj_jmjj35') --SELECT *FROM s WHERE object_id=object_id('cr_dj_jmjj35')

--希望追加四个字段,最长度时的值,最⼤长度时的值,最⼤值长度,错误提醒,建议的sql语句 ,遍历插⼊表@sel_tbname

-- SELECT * FROM #tmp RETURN

IF EXISTS(SELECT *FROM sors WHERE cursor_name='cur_cq')

BEGIN

CLOSE cur_cq

DEALLOCATE cur_cq

END

DECLARE @colname VARCHAR(200),@sql NVARCHAR(2000),@col_type_name VARCHAR(200),@precision INT,@scale INT

DECLARE cur_cq CURSOR FOR SELECT colname,col_type_name,precision,scale FROM #tmp

OPEN cur_cq

FETCH next FROM cur_cq INTO @colname,@col_type_name,@precision,@scale

WHILE @@FETCH_STATUS=0

BEGIN

if @in_tb_col_type='' and @sel_tb_col_type='' begin

--1、获取表@sel_tbname 中的列@colname 最⼤值,最⼤值长度

SET @sql=N'select @col_min=min('+RTRIM(QUOTENAME(@colname))+') ,@col_max=max('+RTRIM(QUOTENAME(@colname))+'),@col_length_max=max(datalength(rtrim('+QUOTENAME(@colname)+'))) from '+CASE WHEN CHARINDEX('#',@sel_tbname)=1 THEN 'tempdb..' ELSE '' END + @sel_tbname

PRINT @sql

EXEC sp_executesql @sql,N'@col_min nvarchar(max) output,@col_max nvarchar(max) output,@col_length_max int output',

@col_max =@col_max OUTPUT,@col_length_max=@col_length_max OUTPUT,@col_min=@col_min output

--output 关键字前⾯的变量是输出变量,⼀定要在代码中声明 --如果是字符类型数据,则最⼤值重新定义为,最⼤字节长度时的值 IF @col_type_name IN ('varchar','char','nchar')

BEGIN --sp_exectutesql 同时存在输⼊参数和输出参数

SET @sql='SELECT @col_max='+RTRIM(QUOTENAME(@colname))+ ' FROM '+CASE WHEN CHARINDEX('#',@sel_tbname)=1 THEN 'tempdb..' ELSE '' END + @sel_tbname +' WHERE DATALENGTH('+RTRIM(QUOTENAME(@colname))+')=@col_length_max' PRINT @sql EXEC sp_executesql @sql,N'@col_max nvarchar(max) output,@col_length_max int',@col_max =@col_max OUTPUT,@col_length_max =@col_length_max END

--更新#tmp 表 字段col_max,col_length_max

print '@col_type_name ='+cast(@col_type_name as varchar(max))

print '@precision ='+cast(@precision as varchar(max))

print '@scale ='+cast(@scale as varchar(max))

PRINT '@col_min ='+cast(@col_min as varchar(max))

PRINT '@col_max ='+cast(@col_max as varchar(max))

PRINT '@col_length_max ='+cast(@col_length_max as varchar(max))

--if @col_type_name='decimal' and @col_max<=0.0

--continue

SET @sql=N'update a set _min=@col_min, _max=@col_max , col_length_max=@col_length_max,

message_error=case

when col_type_name=''int'' and @col_max>power(cast(2 as bigint),31) then ''整数值超过最⼤值2147483648''

when col_type_name=''int'' and @col_min<-power(cast(2 as bigint),31)+1 then ''整数值最⼩值为-2147483647,请检查''

when col_type_name=''decimal'' and CAST(SUBSTRING(@col_max,1,CHARINDEX(''.'',@col_max)-1) AS bigint)>CAST(REPLICATE(9,precision-scale) AS bigint)

then ''decimal数据类型,整数部分溢出''

when col_type_name=''decimal'' and CAST(SUBSTRING(@col_max,1,CHARINDEX(''.'',@col_max)-1) AS bigint)>CAST(REPLICATE(9,precision-scale) AS bigint)

then ''decimal数据类型,整数部分溢出''

when col_type_name=''numeric'' and CAST(SUBSTRING(@col_max,1,CHARINDEX(''.'',@col_max)-1) AS bigint)>CAST(REPLICATE(9,precision-scale) AS bigint)

then ''decimal数据类型,整数部分溢出''

when col_type_name=''numeric'' and CAST(SUBSTRING(@col_max,1,CHARINDEX(''.'',@col_max)-1) AS bigint)>CAST(REPLICATE(9,precision-scale) AS bigint)

then ''numeric数据类型,整数部分溢出''

when col_type_name not in(''int'',''decimal'',''numeric'',''datetime'') and @col_length_max>max_length then ''字符超出''

else '''' end

FROM #tmp a where 1= case when _type_name in(''decimal'',''numeric'') and CHARINDEX(''.'',@col_max)=0 then 0 else 1 end

and colname=' +''''+@colname+''''

PRINT @sql

EXEC sp_executesql @sql,N'@col_max nvarchar(max),@col_length_max int,@col_min nvarchar(max)',

@col_max=@col_max,@col_length_max=@col_length_max,@col_min=@col_min @col_max=@col_max,@col_length_max=@col_length_max,@col_min=@col_min

end

else if @in_tb_col_type<>'' begin --如果插⼊表,字段类型不为空 --场景1:插⼊表字段是数值型,但是不知道准确的数据类型,选择列表的字段类型确定,⽐如说,选择列表字段数据类型varchar if @in_tb_col_type='D' and @sel_tb_col_type='varchar' begin update a set e_error='数据类型不⼀致,请检查' from #tmp a where colname=@colname and col_type_name in('numeric','decimal','float','int','bigint','bit','smallint','byte') and exists(SELECT b.* FROM s b join c on _type_id=_type_id WHERE _id=OBJECT_ID(@sel_tbname) and =@colname and ='varchar' )

end

end

else begin print '先保留'

end

-- -- 找出orders_hld表numeric 类型字段--select , as col_type_name,_col_type from-- s a join b on _type_id=_type_id

--and _id=object_id('orders_hld') and in('numeric','decimal')

--join (select as colname, as in_col_type from s c join d on _type_id=_type_id

--and _id=object_id('jmpz_orders_v_hld') and ='varchar') e on =e

FETCH next FROM cur_cq INTO @colname,@col_type_name,@precision,@scale

END

CLOSE cur_cq

DEALLOCATE cur_cq

update a set _statement='alter table '+tbname+' alter column '+colname+' '+col_type_name+'('+cast(col_length_max*2 as varchar(200))+')' from #tmp a where message_error='字符超出'

SELECT *FROM #tmp WHERE message_error<>''

END

本⽂纯原创,如果疑问可以联系我qq :2625526306,另外如果代码有问题,⼤家补充,可以⽀持临时表和实体表的判断 ,转载需要备注来源,谢谢请尊重原创