我使用从远程Pg数据库写入SELECT的代码自动生成一些查询,并将其插入本地SQL Server数据库中。但是,其中之一正在生成此错误:

[Microsoft] [ODBC SQL Server驱动程序] [SQL Server]字符串或二进制数据将被截断。 (SQL-22001)[状态现在是22001,现在为01000]
[Microsoft] [ODBC SQL Server驱动程序] [SQL Server]该语句已终止。 (SQL-01000)在。\ insert.pl第106行。

如何找出哪个列正在生成该错误并且缺少输入的长度?有没有办法不用蛮力猜测所有的varchar

#1 楼

不,它不会记录在任何地方。投票并陈述您的业务案例;这是SQL Server应该修复的一长串内容。

这是几年前在Connect上提出的(可能是在SQL Server 2000或2005年的时间范围内),然后在新的反馈系统:


二进制或字符串数​​据将被截断=>错误消息增强功能

现在已提供以下版本:


SQL Server 2019
SQL Server 2017 CU12

SQL Server 2016 SP2 CU6

SQL Server 2019的第一个公共CTP ,它仅在跟踪标志460下浮出水面。这听起来有些秘密,但已在此Microsoft白皮书中发布。这将是默认行为(无需跟踪标志),尽管您可以通过新的数据库范围配置VERBOSE_TRUNCATION_WARNINGS进行控制。以下是示例:

USE tempdb;
GO
CREATE TABLE dbo.x(a char(1));

INSERT dbo.x(a) VALUES('foo');
GO


SQL Server 2019之前所有受支持的版本中的结果:


Msg 8152,级别16,状态30,第5行
字符串或二进制数据将被截断。
语句已终止。


现在,在SQL Server 2019 CTP上,启用了跟踪标志:

DBCC TRACEON(460);
GO

INSERT dbo.x(a) VALUES('foo');
GO
DROP TABLE dbo.x;
DBCC TRACEOFF(460);


结果显示表,列和(截断的,不完整的)值:


Msg 2628,级别16,状态1,行11
表'tempdb.dbo.x'的列'a'中的字符串或二进制数据将被截断。截断的值:'f'。
该语句已终止。


在可以移至受支持的版本/ CU或移至Azure SQL数据库之前,您可以更改“自动”代码以实际从sys.columns中提取max_length以及无论如何必须到达的名称,然后应用LEFT(column, max_length)或任何PG的等效物。或者,由于这仅意味着您将无声地丢失数据,因此请找出哪些列不匹配并修复目标列,以使它们适合源中的所有数据。给定元数据访问这两个系统的权限,并且您已经在编写一个必须与源->目标列自动匹配的查询(否则,该错误几乎不是您的最大问题),您不必进行任何暴力完全是猜测。

#2 楼

如果您有权从SQL Server Management Studio运行SQL Server导入和导出向导(右键单击数据库>任务>导入数据...),请创建一个任务,该任务使用查询作为数据源从SQL Client导入到目标表。

在运行导入之前,您可以查看数据映射,它会告诉您哪些列的字段类型不一致。而且,如果您运行导入任务,它将告诉您哪些列导入失败。

示例验证警告:


警告0x802092a7:数据流任务1:由于
将数据从长度为316的数据流列“ NARRATIVE”插入到长度为60的数据库列“ NARRATIVE”,所以可能会发生截断。(SQL Server导出向导)


#3 楼

最终,Microsoft决定从SQL Server 2016 SP2 CU,SQL Server 2017 CU12和SQL Server 2019开始为String or binary would be truncated提供有意义的信息。有问题的值(被截断为120个字符):


消息2628,级别16,状态1,行x字符串或二进制数据将在表'TheDb.TheSchema中被截断.TheTable”,“ TheColumn”列。截断的值:“ ...”。
该语句已终止。


#4 楼

最终,我无法找到一种无需自己编写即可获取列信息的方法。

我在列列表中使用了类似的代码,以获取包含两个元素DBD::ODBCsys.columns (max_length)(在DBI COLUMN_NAME中记录)的数组列表。 />
然后我在MAX_LENGTH上捕获了异常,并打印了一些有用的东西。在此示例中,column_info()是发送到INSERT的数据

my @max_lengths = map [ @{$_->fetchall_arrayref->[0]}[3,6] ]
    , map $dbh_mssql->column_info('database', 'dbo', $dest_table, $_)
    , @col_mssql
;


另外,请投票并投票赞成其他答案

评论


我没有放置任何引用sys.columns的代码,因为我完全不知道您当前使用什么代码来“自动”生成查询。与SELECT名称,object_id,max_length FROM sys.columns;相比,我想将代码合并到您的代码中实际上没有什么复杂的。由于您已经具有必须执行此操作的自动代码-或类似的代码-我认为没有必要提供示例。

–亚伦·伯特兰(Aaron Bertrand)
2013年12月15日20:26



我不确定sys.columns如何与两个具有相同名称的列一起使用。另外,我使用库而不是sys来工作,为什么要选择它作为答案? Microsoft SQL没有x,而是y是有效的贡献,但是如果您的y不如我的y,则我将做一些不同的事情并将其标记为已选择。

–埃文·卡洛尔(Evan Carroll)
2013年12月16日8:00



从本质上讲,您的问题是,我如何找出导致错误的列(大概是这样,您可以修复一个问题,而不是重新设计解决方案)。我告诉你去哪里看:sys.columns。您应该在哪里比较源列长度和目标列长度。您的操作方式取决于您。我没有告诉您如何修复代码,因为我完全不知道首先如何生成自动查询,因此,就像我说的那样,不知道如何将长度确定添加到您已经拥有的任何查询中。

–亚伦·伯特兰(Aaron Bertrand)
2013年12月16日12:42