我有这个问题:
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
)
答案 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的变量中,然后使用它。
任何设计数据库或编写数据库代码的人:
答案 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