此时 SQL Server 会验证数据以进行插入

use*_*949 4 sql-server insert

我有一个查询将数据从一个表插入到另一个表中。源列之一大于目标表的列,但查询中选择的实际数据并非如此。因此,例如,源表是varchar(25),目标表是varchar(10),并且查询为该特定列选择不超过 10 个字符的记录。

我的初始查询因截断错误而失败,但是当我重新编写它时,它选择了相同的记录但更改了 where 子句,即WHERE IN在 cte 上使用和过滤它成功运行。

即而不是做

select * from tbl where col1 = 1
Run Code Online (Sandbox Code Playgroud)

我做了:

with cte as (select 1) select * from tbl where col1 in (select 1 from cte)
Run Code Online (Sandbox Code Playgroud)

我知道这是 SQL Server 中的一个错误,但我仍然想知道 SQL Server 在哪一点验证插入的数据。在哪一点抛出错误?

Aar*_*and 9

老实说,这完全取决于查询的处理方式,并且更改查询(即使它产生相同的最终结果)当然可能会产生表达式(例如尝试将大字符串放入小列所需的转换) 发生在过滤器消除这些行之前(并且可能会出现不同的计划形状,因为语法、统计信息已更改、索引已添加等)。

Erland Sommarskog 一直称这些为“不合逻辑的错误”——这个 UserVoice 问题是从至少十年前最初发布的 Connect 项目迁移而来的。

在您的特定情况下,您可以在此处使用多个故障保护,假设small_col是 avarchar(10)并且large_col在以下情况下不能超过 10 个字符col1 = 1

INSERT dbo.target(small_col)
  SELECT CASE col1 
    WHEN 1 THEN CONVERT(varchar(10), large_col) END
  FROM dbo.source
  WHERE col1 = 1;
Run Code Online (Sandbox Code Playgroud)

事实上,在 SQL Server 2012 及更高版本中,根据所涉及的数据类型,额外的故障保护将使用 ,TRY_CONVERT()而不是CONVERT()。我认为这不适用于这种特定情况,因为几乎任何字符串类型都可以转换为varchar(10),但在其他情况下可能有用。

过去有一个类似但不完全相同的问题,我在其中添加了其他示例和外部参考链接:

我也在 #1 中写过这个:

无论如何,为了回答您的明确问题,只有在以这样一种方式处理查询时才会抛出错误,即在违规行被过滤掉之前尝试表达式。这就是为什么你往往会听到我们很多老派的人在讲授总是使用正确的数据类型,而不是重载列等。