为什么在 where 子句中对 char 字段使用 coalesce 时查询结果会发生变化?

Why do query results change when using coalesce on a char field in a where clause?

我在 Oracle 数据库的合并字符字段上使用 where 子句查询时发现一些意外行为。

好像

的结果
CASE WHEN COALESCE(char_field, 'some_val') = 'someOtherVal'

的结果不同
CASE WHEN char_field = 'someOtherVal'

我注意到这个奇怪输出的具体比较是 'between'、'in' 和 'equals'。这些是我看到的奇怪输出:

  1. Between在上端似乎是非包容性的
  2. 每次比较都等于return false

这里有一些 sql 来复制古怪的东西:

CREATE TABLE delete_me( some_char CHAR(8) );

INSERT ALL   
  INTO delete_me (some_char) VALUES ('1')
  INTO delete_me (some_char) VALUES ('2')
  INTO delete_me (some_char) VALUES ('4')
  INTO delete_me (some_char) VALUES ('5')
  INTO delete_me (some_char) VALUES ('abc1')
  INTO delete_me (some_char) VALUES (null)
SELECT 1 FROM DUAL;

SELECT some_char,
  COALESCE(some_char, 'wasNull') AS coalesce_some_char,
  CASE
    WHEN (some_char BETWEEN '1' AND '5')
    THEN 'true'
    ELSE 'false'
  END AS between_1_5,
  CASE
    WHEN (COALESCE(some_char, 'wasNull') BETWEEN '1' AND '5')
    THEN 'true'
    ELSE 'false'
  END AS coalesce_between_1_5,
  CASE
    WHEN (some_char IN ('1', '5'))
    THEN 'true'
    ELSE 'false'
  END AS in_1_5,
  CASE
    WHEN (COALESCE(some_char, 'wasNull') IN ('1', '5'))
    THEN 'true'
    ELSE 'false'
  END AS coalesce_in_1_5,
  CASE
    WHEN (some_char = 'abc1')
    THEN 'true'
    ELSE 'false'
  END AS equals_abc1,
  CASE
    WHEN (COALESCE(some_char, 'wasNull') = 'abc1')
    THEN 'true'
    ELSE 'false'
  END AS coalesce_equals_abc1
FROM delete_me;

我原以为对于除 IS NULL 之外的所有运算符,合并字段的比较输出与非合并字段的比较输出相匹配。

有谁知道为什么这些结果不匹配?

您的问题出在 some_char 的数据类型上。当 CHAR 类型的列与字符串进行比较时,Oracle 将字符串空白填充到列的长度(参见 docs). In the tests you are doing, the values match in length ('1' vs '1') or are completely different ('1' vs 'abc1') so everything works fine. However when you use COALESCE on a CHAR field, the output of COALESCE is the fully blank padded column value returned as a VARCHAR (see the docs for NVL),因此比较字符串是 not 空白填充,然后您比较 '1 ''1',但失败了。有几种方法可以解决这个问题。您可以 TRIM COALESCE 的输出,即

TRIM(COALESCE(some_char, 'wasNull'))

或将 some_char 的数据类型更改为 VARCHAR(8)

我已经在 dbfiddle

上制作了所有这些的演示