Access链接SQL Server表时,截断除法与实际除法比较失效问题排查
这个问题我之前帮朋友排查过,核心是Access和SQL Server的运算逻辑差异,加上链接表查询时的“远程执行”特性导致的,咱们一步步拆解:
为什么原语句在链接表上失效?
主要有两个关键原因:
整数除法的行为差异
SQL Server中如果两个整数类型的字段相除(比如你的Qty和PackSize都是整数),会直接执行整数除法——截断小数部分返回整数。比如425/2在SQL Server里结果是212,而不是Access里的212.5。
你写的Where Qty/Packsize <> Fix(Qty/Packsize),在链接表查询时会被Access推送到SQL Server执行,此时Qty/Packsize已经是整数,自然和Fix()的结果相等,所以返回空结果。函数转换的不确定性
Access的Fix()/Int()函数在查询链接SQL Server表时,会被自动转换为SQL Server的等价函数(比如FLOOR()或者CAST(... AS INT)),但因为第一步的除法已经是整数,这个转换后的判断完全失去意义。
至于你遇到的Where Cdbl(Qty/Packsize) = int(Qty/Packsize)返回所有记录的问题,也是因为SQL Server里Qty/Packsize本身就是整数,转成Cdbl后还是整数,和int()的结果完全一致,所以所有记录都满足条件——你在拆分查询时看到的425.5,是因为数据被拉到Access本地后,用Access的除法逻辑计算出来的,和远程执行的结果不一样。
单步查询的解决方案
推荐两种更可靠的写法,都能在单步查询中生效:
方案1:使用模运算(最优解)
SQL Server支持模运算符%,直接判断Qty除以PackSize的余数是否不为0,这是判断倍数关系最直接、高效的方式,而且没有浮点精度问题:
WHERE Qty % PackSize <> 0
这个语句会被正确推送到SQL Server执行,直接筛选出不是倍数的记录。
方案2:强制浮点除法
如果你还是想用除法的方式,可以强制SQL Server执行浮点除法,只要把其中一个字段转为浮点类型即可,这样除法结果会保留小数:
WHERE (CAST(Qty AS FLOAT) / PackSize) <> FLOOR(CAST(Qty AS FLOAT) / PackSize)
或者更简洁的写法(用1.0相乘触发浮点转换):
WHERE (Qty * 1.0 / PackSize) <> FLOOR(Qty * 1.0 / PackSize)
为什么拆分两步能生效?
因为第一步创建新表时,Access会把SQL Server中的数据全部拉取到本地,然后用Access自己的运算逻辑处理除法(返回浮点数),所以第二步的判断能正确识别出小数和整数的差异。但这种方式效率很低,尤其是数据量大的时候,远不如直接在SQL Server端完成筛选。
内容的提问来源于stack exchange,提问作者GroovySu

