如何确定负责“字符串或二进制数据将被截断”的列。


31

我使用从远程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

Answers:


35

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

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

现在已在SQL Server 2019,SQL Server 2017 CU12中交付,并将在以后的SQL Server 2016 SP2 CU中出现。

在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之前的所有受支持版本:

消息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);

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

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

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


2

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

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

样品验证警告:

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


1

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

此错误消息是由生成的DBD::ODBC,但是您也可以使用sys.columns (max_length)(我只是不知道如何)。

我在列列表中使用了这样的代码,以获取包含两个元素的数组的列表COLUMN_NAME,和,MAX_LENGTH(记录在DBI中column_info())。

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

然后我发现了异常,INSERT并打印了一些有用的东西。在此示例@$row中,数据已发送至sth->execute()

if ($@) {
        warn "$@\n";
        for ( my $idx=0; $idx <= $#{ $row }; $idx++ ) {
                Dumper {
                        maxlength => $max_lengths[$idx]->[1]
                        , name    => $max_lengths[$idx]->[0]
                        , length  => length( $row->[$idx] )
                        , content => $row->[$idx]
                };
        }
        die;
}

另外,请投票并支持其他答案


2
我没有放置任何代码引用,sys.columns因为我完全不知道您当前使用什么代码来“自动”生成查询。关于合并到您的代码中,我想不到的真的没有什么要复杂的SELECT name, object_id, max_length FROM sys.columns;。由于您已经具有必须执行此操作的自动代码-或类似的代码-我认为没有必要提供示例。
亚伦·伯特兰

我不确定sys.columns两个相同的列如何工作name。另外,我使用库而不是库来工作sys,为什么要选择它作为答案?Microsoft SQL doesn't have x, do y instead是有效的贡献,但如果您y不如我y,我将做些不同的事情并将其标记为选中。
埃文·卡罗尔

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

1

最后,微软已经决定提供有意义的信息用于String or binary would be truncated从SQL Server 2016 SP2 CU,SQL服务器2017年CU12和SQL Server 2019开始。

现在,该信息既包含违规表列(全限定名称),又包含违规值(截断为120个字符):

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.