当没有INSERT语句时,为什么我会收到“字符串或二进制数据会被截断错误”?

时间:2014-09-12 17:10:17

标签: sql sql-server tsql sql-server-2012 truncation

我有这个问题:

USE [SomeDatabase];
GO

DECLARE @percentageValue decimal(15,4) = 1.50; 

SELECT a.ID, a.Amount, a.Status 
FROM [dbo].ATable as a          
INNER JOIN [dbo].BTable as b 
  ON a.LinkToB = b.ID
INNER JOIN [OtherDatabase].[dbo].CTable as value 
  ON value.[Key] = CONCAT(N'APrefixAboutThisLongThatsNecessaryBecauseDontAsk',b.AltID)
WHERE a.Status = N'SomeStatus'
AND a.Amount > (COALESCE(TRY_CONVERT(DECIMAL(15,2), value.Value), 0)*@percentageValue);
GO

(为保密而编辑的实际列名称)

我得到了传统的: “Msg 8152,Level 16,State 10,Line 3 字符串或二进制数据将被截断。“ 错误。谷歌告诉我,我正试图在一个太小的列中插入一些东西,这是有道理的。

然而,这不是一个INSERT操作(这实际上是我的查询的所有SQL),所以我不能在我的生活中检测截断发生的位置或者为什么。我认为这是Transact-SQL的内容,但最奇怪的问题是我从查询中得到结果,尽管有错误。

根据要求,这是表格架构的相关部分。

USE [SomeDatabase]

CREATE TABLE [dbo].[ATable](
    [ID] [uniqueidentifier] NOT NULL,
    [Amount] [decimal](15, 2) NOT NULL,
    [Status] [nvarchar](32) NOT NULL,
    [LinkToB] [uniqueidentifier] NOT NULL,
 CONSTRAINT [PK_TableA] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
))
GO

CREATE TABLE [dbo].[BTable](
    [ID] [uniqueidentifier] NOT NULL,
    [AltID]  [uniqueidentifier] NOT NULL
 CONSTRAINT [PK_TableA] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
))
GO

USE [OtherDatabase]
GO

CREATE TABLE [dbo].[CTable](
    [ID] [uniqueidentifier] NOT NULL,
    [Key] [nvarchar](100) NOT NULL,
    [Value] [nvarchar](max) NOT NULL)
CONSTRAINT [PK_TableC] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)

2 个答案:

答案 0 :(得分:7)

好的,一旦你知道这个秘密,这个结果很有趣但很明显。

如评论中所述(以及架构),value.Value是nvarchar(max)。它是传统的键值反模式,用于存储您不希望存储在特定位置的数据。现在,我们在此列上运行try_convert(nvarchar(max)),但这很好,因为

 ON Key = N'SomethingSpecific'

子句意味着它只在Key = N' SomethingSpecific'上运行try_convert。一排,对吗?

右?

不。

根据表中的数据,执行计划可以选择try_convert列中的每个值。 nvarchar(max)中的一行是超出try_convert参数的容量。因此崩溃。这也解释了为什么我得到了我期望得到的所有结果,它正在评估这些结果,然后在我预期它忽略的数据行上崩溃。

更好的是,不相关的数据或结构更改可能会改变执行计划的行为,因此这个错误可能会导致" Heisenbug"在给出更多/更少的数据,对结构进行微小更改以及在一次调试过程中,我非常容易和消失,我非常确定空白

那么最简单的解决方案是什么?将配置值分别输入到try_convert的变量中,然后使用它。

任何设计数据库或编写数据库代码的人:

  • 不要在SQL where子句
  • 中操作数据类型
  • 不要使用一列来存储截然不同的数据类型并编写复杂的转换例程来处理问题
  • 请为您的数据使用正确的数据类型,并使用正确的数据类型处理其他表格
  • 请记住,SQL函数通常在引擎盖下执行类似INSERT的操作

答案 1 :(得分:0)

for grins试一试

USE [SomeDatabase];
GO

DECLARE @percentageValue decimal(15,4) = 1.50; 
DECLARE @prefix nvarchar(max) =  N'APrefixAboutThisLongThatsNecessaryBecauseDontAsk'; 

SELECT a.ID, a.Amount, a.Status 
FROM [dbo].ATable as a          
INNER JOIN [dbo].BTable as b 
  ON a.LinkToB = b.ID
INNER JOIN [OtherDatabase].[dbo].CTable as value 
  ON value.[Key] = CONCAT(@prefix,b.AltID)
WHERE a.Status = @status
AND a.Amount > (COALESCE(TRY_CONVERT(DECIMAL(15,2), value.Value), 0)*@percentageValue);
GO