MSSQL聚合忽略where子句

时间:2014-02-21 08:26:36

标签: sql-server casting aggregate

我有一个奇怪的问题,当在类型转换varchar列上执行聚合函数时,我收到“消息8114,级别16,状态5,第1行。将数据类型nvarchar转换为bigint时出错。” query 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应该意味着'Value'仅返回整数的字符串表示。下面的查询失败,显示“消息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

但子查询不起作用。

任何帮助或想法都将不胜感激。

1 个答案:

答案 0 :(得分:0)

尝试使用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