【问题标题】:MSSQL aggregate ignoring where clauseMSSQL聚合忽略where子句
【发布时间】: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


【解决方案1】:

尝试使用 REGEX 表达式查找问题记录。这是一个 ISNUMERIC 未检测到问题但正则表达式检测到问题的示例

CREATE TABLE tst (value nvarchar(4000))
INSERT INTO tst select '£'
-- Record found ...
SELECT * FROM tst WHERE value NOT LIKE '%[0-9]%'
-- No record found ...
SELECT * from tst where isnumeric(value) <> 1

【讨论】:

  • 如果您打算建议一种测试字符串是否可转换为整数的方法,则第一个 SELECT 中的条件应为 LIKE '%[^0-9]%'(以查找 not i> 可转换)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
相关资源
最近更新 更多