【问题标题】:when we need trim when we don't need in oracle当我们在 oracle 中不需要修剪时
【发布时间】:2017-11-16 18:50:11
【问题描述】:

我在 Oracle 中看到,有时是 'A ' = 'A',但有时是 'A ' != 'A',请参阅下面的示例,您会看到详细信息。我一直在尝试缩短代码。

CREATE TABLE AAA (
    T1 CHAR(3),
    T2 VARCHAR(3),
    T3 VARCHAR(5)
);
insert into AAA
SELECT 'A','A  ', 'A    ' FROM DUAL;
COMMIT;
SELECT * FROM AAA WHERE T1 = 'A'
UNION ALL
SELECT  * FROM AAA WHERE T2 = 'A  '
UNION ALL
SELECT  * FROM AAA WHERE T2 = 'A    '
UNION ALL
SELECT  * FROM AAA WHERE TRIM(T2) = TRIM('A   ')
UNION ALL
SELECT CASE WHEN 'A' = 'A ' THEN '1' ELSE '0' END T1, 
       CASE WHEN 'A       ' = 'A' THEN '1' ELSE '0' END T2,
       CASE WHEN 'A ' = 'A ' THEN '1' ELSE '0' END T2   
FROM DUAL;

我问这个问题是因为在我的数据库中,有时当我将表 A 连接到表 B 时,有时我们不需要执行此操作。当我们需要TRIM(列)?当'A ' = 'A' ?什么时候'A ' != 'A'

【问题讨论】:

  • 当你想要或需要更松散的匹配时,你会规范化数据。 ` 'Password' = TRIM(' Password ') ` 可能很糟糕。
  • 理想情况下,如果您在将数据放入表之前清理数据并删除前导/尾随空格,则不需要使用 TRIM
  • @Tim Biegeleisen 我知道,但是这个数据一直存在,有很多这样的专栏。
  • 如果您需要进行精确比较,我认为您可以尝试使用T1 || '^' = 'A^'(最后添加附加字符)。唉,我现在没有Oracle,也查不到。
  • 我知道,精确比较是 trim(),没关系,但有时如果我们不比较 2 table care 很容易出错。

标签: sql oracle


【解决方案1】:

查看literals 上的 Oracle 文档:

文本文字同时具有CHARVARCHAR2 数据类型的属性:

  • 在表达式和条件中,Oracle 将文本文字视为具有数据类型 CHAR,通过使用空白填充的比较语义进行比较。

以及blank-padded comparison semantics 的文档说明:

使用空格填充语义,如果两个值的长度不同,那么 Oracle 首先在较短的值的末尾添加空格,使它们的长度相等。然后,Oracle 逐个字符地比较这些值,直到第一个不同的字符为止。在第一个不同位置具有较大字符的值被认为较大。如果两个值没有不同的字符,则认为它们相等。此规则意味着如果两个值仅在尾随空格的数量上有所不同,则它们是相等的。仅当比较中的两个值都是数据类型 CHARNCHAR、文本文字或由 USER 函数。

所以,对于你的例子:

SELECT * FROM AAA WHERE T1 = 'A'

由于比较的左侧,T1 等于 'A ',是 CHAR(3),右侧是文本文字 'A',然后使用空白填充比较语义和 @ 987654336@ 是真的。

然后:

SELECT  * FROM AAA WHERE T2 = 'A  '

比较的左侧T2 等于'A ',是VARCHAR2(3),右侧是文本文字'A ',然后不使用空白填充的比较语义(两者边不是CHAR 或文本文字),但值相等('A ' = 'A '),因此条件为真。

还有:

SELECT  * FROM AAA WHERE T2 = 'A    '

LHS 是一个等于'A 'VARCHAR2,而RHS 是一个等于'A ' 的文本字面量;将不使用空白填充的比较语义,并且值不相等,因此条件不成立并且不会返回该行。

然后:

SELECT  * FROM AAA WHERE TRIM(T2) = TRIM('A   ')

TRIM() function 声明:

如果 trim_source 是字符数据类型,则返回的字符串是 VARCHAR2 数据类型;如果 trim_source 是 LOB 数据类型,则返回的字符串是 LOB

两个值都被修剪,所以你有条件'A' = 'A',其中两边都是VARCAHR2数据类型,所以不会使用空白填充的比较语义,但值是相等的,所以条件为真,行将是返回。

最后:

SELECT CASE WHEN 'A' = 'A ' THEN '1' ELSE '0' END T1, 
       CASE WHEN 'A       ' = 'A' THEN '1' ELSE '0' END T2,
       CASE WHEN 'A ' = 'A ' THEN '1' ELSE '0' END T2   
FROM DUAL;

在所有情况下,等式的两边都是文本文字,因此将使用空白填充的比较语义,以便

  • 'A' = 'A ' LHS 将被填充,因此字符串的长度相同;
  • 'A ' = 'A' RHS 将被填充;
  • 'A ' = 'A ' 两边长度相等,因此不需要填充。

在每种情况下,等式都是相等的。

如果您使用CAST 将文本文字更改为VARCHAR2 数据类型:

SELECT CASE WHEN 'A' = CAST( 'A ' AS VARCHAR2(5) ) THEN 1 ELSE 0 END AS T1
FROM   DUAL

LHS 是文本文字,但 RHS 是 VARCHAR2,并且未使用空白填充的比较语义,并且相等性不正确。

【讨论】:

  • 请不要在我的回答下宣传你的回答。
  • @TimBiegeleisen 我链接到一个答案,其中引用了适当的文档,以便未来的用户对行为有一个简明和正式的解释(而不是必须破译你的问题中没有的 ASKTOM 链接格式化所以不容易阅读,也没有链接到文档,所以没有探索使用空白填充比较语义时的完整条件)。我本可以同样链接到也有文档的another answer,但链接到这个更相关。
【解决方案2】:

我认为您在查询实际表时看到的行为是预期的,唯一可能让您感到惊讶的行为是在以下查询中:

SELECT CASE WHEN 'A' = 'A ' THEN '1' ELSE '0' END T1, 
       CASE WHEN 'A       ' = 'A' THEN '1' ELSE '0' END T2,
       CASE WHEN 'A ' = 'A ' THEN '1' ELSE '0' END T2   
FROM DUAL;

this Oracle discussion 看来,正在发生的事情似乎与'A''A ' 等是char 类型和不是 varchar2 类型有关。因此,为了比较两个char 值,它们的长度必须相同。考虑以下CASE 表达式:

CASE WHEN 'A       ' = 'A' THEN '1' ELSE '0' END T2

这里的'A' 文字,即char(1),将通过在右侧填充空格来提升为char(8)。因此,当实际进行比较时,LHS 和 RHS 实际上是相同的。请注意,以下 CASE 表达式不会评估为真条件:

CASE WHEN '  A' = 'A' THEN '1' ELSE '0' END T2

这是因为填充会发生在右侧的'A' 上,所以' A' 将与'A ' 进行比较,两者并不相同。

按照下面的链接查看演示,该演示表明,如果您使用所有 varchar 列,输入的原始空格/宽度将被保留,并且字符串的逻辑比较是您所期望的。

Demo

【讨论】:

  • 文本字面量的行为并不意外,并在 oracle 文档中定义(请参阅my answer
  • @MT0 从 OP 的角度来看,我说的是无意的。当然,这种行为是众所周知的,Oracle 是一个有几十年历史的数据库。
猜你喜欢
  • 2023-03-03
  • 1970-01-01
  • 2023-04-02
  • 2020-08-22
  • 2012-06-22
  • 2019-06-14
  • 2023-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多