我有一些数据表,它们是从平面文件源导入的,所以varchar(max)
除了FileRecordID
它是int
保存数据来自平面文件源的行号(并且是导入表的主键)之外的所有数据表。
以下查询完美运行
SELECT *
FROM [CLIENT]
left outer join
(
select cast(ENTRY_CODE as int) as ENTRY_CODE, ENTRY_DESCRIPTION
from [ENTRY]
inner join [TABLE] on [ENTRY].ENTRY_TABLE = [Table].FileRecordID
where ENTRY_RECD = 'A' and TABLE_RECD = 'A' and TABLE_CODE = 'CLTDISC' and ISNUMERIC(ENTRY_CODE) = 1
) as discounts on CLIENT_CLASS = ENTRY_CODE
where Client_recd = 'A'
--and client.[FileRecordID] = 10607
我得到了我期望的确切行数,一切都很好。但是,如果我取消注释最后一行,那么我只显示client
数据表中 的记录,该记录client.[FileRecordID] = 10607
向我报告
将 varchar 值“BK/TN”转换为数据类型 int 时,消息 245、级别 16、状态 1、第 1 行
转换失败。
问题出cast(ENTRY_CODE as int) as ENTRY_CODE
在第一个外部联接中。ENTRY
可以在 ENTRY_CODE 列中包含文本,但是所有ENTRY_TABLE
设置为行号的TABLE
记录TABLE_CODE = 'CLTDISC'
将始终仅为数字。我执行转换时ENTRY_CODE
可能有也可能没有前导 0 和空格,所以我试图让它们格式正确。
在我看来正在发生的事情是添加client.[FileRecordID] = 10607
到外部查询导致where
在内部查询中转换为 int 之前不评估该子句。
我已经尝试过添加and ISNUMERIC(ENTRY_CODE) = 1
到内部和外部查询之类的东西,但它没有任何影响。
- 我该如何解决?
- 任何人都可以解释发生了什么,以便我了解将来如何避免这种情况吗?
非过滤查询计划执行
过滤的(非工作)查询计划执行
注意和
的相反顺序Compute Scalar
Filter
我尝试过的其他不起作用的东西:
- 移入内部连接
TABLE_CODE = 'CLTDISC'
中的on
子句。 - 向查询添加
OPTION (FORCE ORDER)
了查询提示。
更新:
我找到了一个解决方案,但恕我直言,它非常“hackish”,我真的仍然想要解释如何避免将来发生此类事情以及如何正确执行此操作。
SELECT *
FROM [CLIENT]
left outer join
(
select ENTRY_CODE, ENTRY_DESCRIPTION
from [ENTRY]
inner join [TABLE] on [ENTRY].ENTRY_TABLE = [Table].FileRecordID
where ENTRY_RECD = 'A' and TABLE_RECD = 'A' and TABLE_CODE = 'CLTDISC' and ISNUMERIC(ENTRY_CODE) = 1
) as discounts on CLIENT_CLASS = REPLACE(LTRIM(REPLACE(ENTRY_CODE, '0', ' ')), ' ', '0')
where Client_recd = 'A'
and client.[FileRecordID] = 10607
我确定 dba.se 和 StackOverflow 有重复项,但我发布此链接要快得多:
https://feedback.azure.com/forums/908035-sql-server/suggestions/32912431-sql-server-should-not-raise-illogical-errors
简而言之,查询优化器真的可以自由地按照它认为最好的方式重写计划。有时,这意味着一个列被转换(CAST -> int)接近于检索,这样它就不必在执行 CAST 之前向前执行很多步骤。
防止错误的唯一可靠方法是查看 CAST 表达式本身,例如:
要测试可以成功转换为 INT 的值,您需要测试所有字符都是数字,或者换句话说,该字符串不包含任何非数字字符。
查询优化器应该在 ENTRY 时将谓词下推到索引扫描中,在尝试将它们转换为 INT 之前消除行。