【问题标题】:SQL Server subquery behaviourSQL Server 子查询行为
【发布时间】:2018-11-09 17:44:59
【问题描述】:

我有一个案例,我想检查是否在 varchar 表的列中找到整数值,但它是可以是整数的值的混合,有些只是字符串。我的第一个想法是使用子查询来选择具有数字式值的行。设置如下:

CREATE TABLE #tmp (
EmployeeID varchar(50) NOT NULL
)

INSERT INTO #tmp VALUES ('aa1234')
INSERT INTO #tmp VALUES ('1234')
INSERT INTO #tmp VALUES ('5678')

DECLARE @eid int
SET @eid = 5678

SELECT * 
FROM (
    SELECT EmployeeID
    FROM #tmp
    WHERE IsNumeric(EmployeeID) = 1) AS UED 
WHERE UED.EmployeeID = @eid

DROP TABLE #tmp

但是,这会失败,并显示:“将 varchar 值 'aa1234' 转换为数据类型 int 时转换失败。”。

当我在子查询中只选择了行“1234”和“5678”时,我不明白为什么它仍在尝试将@eid 与“aa1234”进行比较。

(我意识到我可以将 @eid 转换为 varchar,但我很好奇 SQL Server 在这种情况下的行为)

【问题讨论】:

  • 因为 SQL 将查询优化为 1 个单个查询。它不会先执行子查询。
  • 虽然这可能与 SQL Server 2008: Error converting data type nvarchar to float 重复,但我不会投票结束,因为我更喜欢这里给出的答案。
  • 这将工作SELECT EmployeeID FROM #tmp WHERE ISNUMERIC(EmployeeID) = 1 AND EmployeeID = @eid
  • @iSR5 不会。请阅读下面的答案,并可选择链接问题中的答案。
  • @iSR5 这可能有效,但也可能无效

标签: sql sql-server


【解决方案1】:

当 SQL Server 查看您编写的查询然后确定最佳执行计划时,您无法轻松控制事情发生的顺序。它不会总是按照您输入的相同逻辑、按相同顺序生成计划。

在这种情况下,为了找到您要查找的行,SQL Server 必须执行两个过滤器:

  1. 仅识别与您的变量匹配的行
  2. 只识别数字行

它可以按任意顺序执行此操作,因此这也是有效的:

  1. 只识别数字行
  2. 仅识别与您的变量匹配的行

如果您查看此执行计划的属性,您会看到与您的变量匹配的谓词首先列出(这仍然不能保证操作顺序),但无论如何,由于数据类型优先,它必须尝试将列数据转换为变量的类型:

子查询、CTE 或以不同方式编写查询(尤其是在这样的简单情况下)不太可能更改 SQL Server 用于执行这些操作的顺序。

在大多数情况下,您可以使用 CASE 表达式强制计算顺序(您也不需要子查询):

SELECT EmployeeID
FROM #tmp
WHERE EmployeeID = CASE IsNumeric(EmployeeID) WHEN 1 THEN @eid END;

在现代版本的 SQL Server 中(您忘了告诉我们您使用的是哪个版本),您也可以改用 TRY_CONVERT()

SELECT EmployeeID
FROM #tmp
WHERE TRY_CONVERT(int, EmployeeID) = @eid;

这本质上是CASE 表达式的简写,但还有一个额外的好处是它允许您指定显式类型which is one of the downsides of ISNUMERIC()。所有ISNUMERIC() 告诉您的是该值是否可以转换为any 数字类型。字符串'1e2' 通过ISNUMERIC() 检查,因为它可以转换为float,但请尝试将其转换为int...

为了完整起见,最好的解决方案 - 如果EmployeeID 上有索引 - 就是按照您的建议使用与列数据类型匹配的变量。

但更好的方法是使用一种数据类型来防止像'aa1234' 这样的垃圾数据首先进入表中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    相关资源
    最近更新 更多