当我测试我的 SQL 代码时,我发现了 Redshift 中的一个潜在错误。虽然计算列中的 CASE 条件计算结果为“yes”,但它并没有被 WHERE 语句过滤掉:
with tt as (
select
90 AS num,
91 AS next_num
)
select
num,
next_num,
(num-next_num) / num AS result
, case when (num-next_num) / num between -0.2 and 0.2 then 'yes' else 'no' end calculation
from tt
where calculation = 'no'
WHERE 语句中是否明确声明 CASE 条件并没有什么区别。然而,如果 num 和 next_num 声明为 NUMERIC(那么它可以工作),它确实会有所不同。显然,SELECT 语句与 WHERE 语句中的计算处理方式不同。
这是应该是这样的事情,我应该更了解,还是 Redshift 的错误?在 SQL Server 中,它运行良好,不会发生矛盾的行为。
这就是所谓的“观察”,归结为隐式转换。请参阅 Redshift,小数值的转换不会四舍五入
正如我在那个答案中观察到的那样“我会让数据库哲学家辩论这是否是一个错误。”
在这种情况下,让我们将您的案例简化为最基本的:
select (-0.1)::int AS result
where result < -0.2 or result > 0.2
这显示了您观察到的相同行为 - 结果为 0,但该行仍处于选中状态。 看起来很奇怪,对吧?
这可以通过仅查看负面比较来进一步澄清,因为这是最重要的。
select (-0.1)::int AS result
where result < -0.2
看起来是一个明显的错误,对吧?
但没那么快。 这一切都归结为隐式转换。 Redshift 如何决定将结果与 -0.2 进行比较? 比较 INT 和 DECIMAL。 请记住,首先计算 WHERE 子句,然后计算结果。 因此,有两条不同的路径正在计算,并且 WHERE 子句有一个隐式转换过程要执行。
让我们稍微改变一下:
select (-0.1) AS result
where result::int < -0.2
这也会生成一个值为 -0.1 的行,但显然不满足 WHERE 子句。这对 Redshift 来说看起来很糟糕。
但是让我们再改变一下:
select (-0.1) AS result
where result::decimal(3,0) < -0.2
再次产生一行。 当用于评估 WHERE 条件时,INT 与 DECIMAL(3,0) 相同吗? 看起来铸造的“WHERE 路径”正在进行一些简化。
正如上面提到的答案所示,从一个定义到 DECIMAL 的隐式转换可能会导致截断或舍入,具体取决于具体情况。这段代码中唯一的区别是负截断,在二进制世界中查看它会向下截断:-0.2 变成 -1,从而通过 WHERE 条件。
这里的底线没有改变 - 如果您让数据库对如何转换数据做出隐式决定,它可以为数据和 WHERE 决定 2 个不同的转换路径。 查询的结果可能看起来“混乱”。
最好的答案是避免隐式转换,这是很好的编码,并且还可以防止数据库在两条路径上进行不同的转换。