dew*_*rde 3 sql t-sql sql-server truncation sql-server-2012
我有这个问题:
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
Run Code Online (Sandbox Code Playgroud)
(为保密而编辑的实际列名称)
我得到了传统的说法:"消息8152,级别16,状态10,行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
)
Run Code Online (Sandbox Code Playgroud)
好的,一旦你知道这个秘密就会变得有趣但很明显.
如注释(和架构)中所述,value.Value是nvarchar(max).它是传统的Key-Value反模式,用于存储您不希望存储在特定位置的数据.现在,我们在这个列上运行try_convert(nvarchar(max)),但这很好,因为
ON Key = N'SomethingSpecific'
Run Code Online (Sandbox Code Playgroud)
子句意味着它只在Key = N'SomethingSpecific'行上运行try_convert,对吗?
对?
不.
根据表中的数据,执行计划可以选择try_convert列中的每个值.并且nvarchar(max)中的一行超出了try_convert参数的容量.因此崩溃.这也解释了为什么我得到了我期望得到的所有结果,它正在评估这些结果,然后在我预期它忽略的数据行上崩溃.
更好的是,不相关的数据或结构更改可能会改变执行计划的行为,所以这个错误可能会变得"Heisenbug"非常容易和消失,因为更多/更少的数据,对结构的微小更改,以及在一个调试过程中,我是非常确定空白.
那么最简单的解决方案是什么?将配置值分别输入到try_convert的变量中,然后使用它.
对于任何设计数据库或编写数据库代码的人: