T-SQL 谜题:将其用作内联视图时查询失败
Posted
技术标签:
【中文标题】T-SQL 谜题:将其用作内联视图时查询失败【英文标题】:T-SQL puzzle: query fails when using it as an inline view 【发布时间】:2021-04-15 15:34:43 【问题描述】:准备
create table t (x varchar(18))
insert into t (x) values ('8003372602728'), ('a')
查询 #1:
select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100
查询 #1 的结果:
y
--------------------
8003372602728
(1 row affected)
查询 #2:
select *
from
(select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100) a
where y is not null
查询 #2 的结果:
y
--------------------
8003372602728
消息 8114,第 16 级,状态 5,第 1 行 将数据类型 varchar 转换为 bigint 时出错。
查询#2的结果错误的原因是什么?
【问题讨论】:
SQL 不会按照您编写它们的顺序执行WHERE
,它可以很容易地尝试在isnumeric(x)=1
之前传递cast(x as bigint)>100
。无论如何,只需使用TRY_CONVERT
/TRY_CAST
,它的功能比ISNUMERIC
好得多。
尝试CAST
一个故意包含VARCHAR
值的列是一个坏主意。另外,我真的不明白where y is not null
子句的含义。但是,如果您坚持原样,您可以随时选择您想要CAST
的行到另一个表中,然后在那里进行您需要的任何操作。
@WAMLeslie 'where y is not null' 仅用于演示错误。如果我没有给出任何“where”条件,那么查询 #2 也可以正常工作!这就是谜题。
【参考方案1】:
首先,请注意这些查询之间的区别:
select cast(x as bigint) y
from t
where isnumeric(x) = 1
and cast(x as bigint) > 100;
select cast(x as bigint) y
from t
where cast(x as bigint) > 100
and isnumeric(x) = 1;
第一个有效,第二个因转换错误而失败。第一个有效,因为首先评估 isumeric 并且仅评估数值。第二个查询正在评估:CAST(x as bigint)
first。理论上,这两个查询都不应该起作用,因为不能保证按任何特定顺序评估过滤器。
接下来,这可行:
select *
from
(select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100) a
----WHERE a.y > 100
... 但是,一旦取消注释 WHERE 子句,它就会失败。这是因为外部查询的 WHERE 子句在确定 ISNUMERIC 是否为真之前评估 x。
这行得通:
select *
from
(select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100) a
WHERE isnumeric(a.y) = 1
但是,再次,当我更改外部查询以包含 y > 100 的过滤器时...
select *
from
( select cast(x as bigint) y
from t
where isnumeric(x) = 1 and cast(x as bigint) > 100) a
WHERE isnumeric(a.y) = 1 and CAST(a.y AS bigint) > 100
失败了。为什么?因为无法保证评估顺序。
TRY_CAST 解决了两个问题:
首先,isnumeric 是一个专有的遗留函数,它不符合大多数人的想法。 isumeric 认为这一切都是数字:
SELECT ISNUMERIC('.'), ISNUMERIC('$'), ISNUMERIC('-'), ISNUMERIC('+'), ISNUMERIC('$+.');
第二,它解决了评估的顺序问题。此查询有效:
select *
from
(select TRY_CAST(x as bigint) y
from t
where TRY_CAST(x AS bigint) IS NOT NULL ) a
WHERE y > 100; -- Implies that it's NOT NULL
【讨论】:
+1 这是关键:“无法保证评估顺序”。由于看似任意的原因,优化器可以选择在isnumeric(x) = 1
之前评估cast(x as bigint)
,因此查询逻辑不应依赖于该顺序(而可以根据您的建议依赖TRY_CAST
)。干得好。以上是关于T-SQL 谜题:将其用作内联视图时查询失败的主要内容,如果未能解决你的问题,请参考以下文章
T-SQL谜题:将表的每一行作为内联函数的输入传递,并使用UNION ALL开发新的堆栈数据集
T-SQL Server 代理作业失败“用户无权执行此操作”
托斯卡谜题 73589 用 RBFW 解决。 (失败:SyntaxError:扫描字符串文字时 EOL (<string>)