【发布时间】:2014-03-22 15:38:05
【问题描述】:
我有一个奇怪的问题,在类型转换 varchar 列上执行聚合函数时,我收到“消息 8114,级别 16,状态 5,第 1 行。将数据类型 nvarchar 转换为 bigint 时出错。”查询 where 子句应该过滤掉非数字值。
表结构类似这样:
IF EXISTS (SELECT * FROM sys.all_objects ao WHERE ao.name = 'Identifier' AND ao.type = 'U') BEGIN DROP TABLE Identifier END
IF EXISTS (SELECT * FROM sys.all_objects ao WHERE ao.name = 'IdentifierType' AND ao.type = 'U') BEGIN DROP TABLE IdentifierType END
CREATE TABLE IdentifierType
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Style] [int] NULL,
CONSTRAINT [PK_IdentifierType_ID] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
CREATE TABLE Identifier
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[IdentifierTypeID] [int] NOT NULL,
[Value] [nvarchar](4000) NOT NULL,
CONSTRAINT [PK_Identifier_ID] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
ALTER TABLE Identifier WITH CHECK ADD CONSTRAINT [FK_Identifier_IdentifierTypeID] FOREIGN KEY([IdentifierTypeID]) REFERENCES IdentifierType ([ID])
GO
Identifier.Value 是一个 VARCHAR 列,它可以并且确实包含非数字数据。将查询过滤为 IdentifierType.Style = 0 应该意味着“值”仅返回整数的字符串表示形式。以下查询失败并显示“消息 8114,级别 16,状态 5,第 1 行。将数据类型 nvarchar 转换为 bigint 时出错。”
SELECT
MAX(CAST(Value AS BIGINT))
FROM
Identifier i,
IdentifierType it
WHERE
i.IdentifierTypeID = it.ID AND
it.Style = 0
如果我扩展 WHERE 子句以包含“AND ISNUMERIC(i.Value) = 1”,它将返回最大整数值。这对我来说意味着我的结果集中有一个非数字字符串。但是我没有从这里返回任何行:
SELECT
*
FROM
Identifier i,
IdentifierType it
WHERE
i.IdentifierTypeID = it.ID AND
it.Style = 0 AND
ISNUMERIC(i.Value) <> 1
我无法识别导致类型转换失败的行。上面的查询应该已经暴露了异常行。此外,也没有空字符串或超长字符串(最大字符串为6个字符)
MSSQL 是否有可能尝试对所有行进行 CAST,而不是先通过 WHERE 子句进行过滤?
或者有没有其他人见过类似的东西?
第二个解决方法是将查询的组件实例化到一个临时表中,然后从中选择 MAX 值。
SELECT
Value
INTO
IdentifierClone
FROM
Identifier i,
IdentifierType it
WHERE
i.IdentifierTypeID = it.ID AND
it.Style = 0
SELECT MAX(CAST(Value as BIGINT)) FROM IdentifierClone
但是子查询不起作用。
任何帮助或想法将不胜感激。
【问题讨论】:
-
请参阅 Connect 反馈网站上的 SQL Server should not raise illogical errors。 SQL Server 有时会向前跳并执行较早的转换,这些转换会引发错误,如果它实际上以正确的逻辑顺序处理查询,则不会生成。
-
我在 isumeric 方面遇到了一些类似的问题,试图找到不是数值并且由于 isumeric 本身而没有得到我预期的结果。如果这是顺序问题,请尝试使用子表进行组织以查看是否更好,请在子表中执行 where 子句,然后执行强制转换。你确定它以前是这样过滤的。
-
@MatheseF - 如果您指的是子查询,那么“确保之前已过滤”是错误的 - 优化器可以并且将重新排列转换和过滤,即使使用子查询,并且仍然会产生这些不合逻辑的错误。我知道防止它的唯一可靠方法是将查询拆分为两个完全独立的查询,并让第一个查询填充一个临时表/表变量,然后第二个查询对其进行操作。
-
Damien,谢谢提供信息,我从来没有遇到过这种情况,但可能是因为优化器没有重新组织我的子查询,只是运气问题,所以不能自信
-
查看执行计划,它会告诉你是否发生了任何隐式转换。
标签: sql-server casting aggregate