在SQL查询中添加括号为什么会导致结果发生变化?

17

当我执行以下查询时,尽管有11条记录匹配,但按照写法没有返回任何记录。然而,如果我删除第6行和第9行上的括号,则会像预期的那样返回所有11条记录。

1  select obj_id, obj_title, UI_DISPLAYNAME
2  from PITS_OBJECT 
3  LEFT OUTER JOIN ui_displayname_view ON obj_create_ui_id = UI_ID  
4  where
5  /* w/ parens, no results, w/o parens, expected results */
6  (
7     Upper( UI_DISPLAYNAME ) LIKE Upper( '%smith%' )  
8     OR Upper( OBJ_TITLE ) LIKE Upper( '%smith%' ) 
9  )
10 /* end w/ parents, no results.... */
11 AND OBJ_ID IN (select obj_id from PITS_OBJECT where 
12     (UPPER( OBJ_TITLE ) LIKE UPPER( '%smith%' )) 
13     AND obj_id in( select sa_obj_id as obj_id from security_access 
14         where sa_type_id = 494 
15         and sa_usrgrp_id = 35
16         and sa_usrgrp_type_id = 230 
17         union 
18         select sa_obj_id from security_access 
19         where sa_type_id = 494 
20         and sa_usrgrp_type_id = 231 
21         and sa_usrgrp_id in ( select ug_gi_id from user_group where ug_ui_id = 35)) )

这会有什么影响吗?OR语句不是意味着其中一个必须为真吗?我错过了什么吗?


4
对于一篇好文章点赞。添加行号以便描述问题出现的位置是个不错的点子。 - Ken White
5个回答

19

三个词:运算顺序。就像你在数学中学到的,某些运算符优先于其他运算符(例如乘法先于加法),除非你使用括号来强制执行。在这种情况下,AND 的优先级高于OR

如果不添加自己的括号,您的 WHERE 子句将像这样进行评估:

Upper( UI_DISPLAYNAME ) LIKE Upper( '%smith%' )
OR 
(Upper( OBJ_TITLE ) LIKE Upper( '%smith%' ) 
    AND OBJ_ID IN (select obj_id from PITS_OBJECT where 
    ...)

但是,当您手动添加这些括号时,您强制执行了先评估OR操作。

(Upper( UI_DISPLAYNAME ) LIKE Upper( '%smith%' )
    OR Upper( OBJ_TITLE ) LIKE Upper( '%smith%' ))
AND OBJ_ID IN (select obj_id from PITS_OBJECT where 
...

编辑: 我应该直接回答你为什么会返回更多数据的问题。原因是没有括号时,如果在第七行发现条件成立,则引擎将对检查进行短路。换句话说,它将包括所有记录,其中 Upper(UI_DISPLAYNAME) LIKE Upper('%smith%'),不考虑其他条件。

当添加这些括号时,逻辑就会改变。它将包括记录,其中Upper(UI_DISPLAYNAME) LIKE Upper('%smith%') OR Upper(OBJ_TITLE) LIKE Upper('%smith%'),然后检查该记录是否还满足从第12行开始的内部选择的条件。这些额外的记录不会显示,因为它们不符合该内部选择的条件。


谢谢,这也解释了为什么当我反转列的顺序(第7和8行)时,无论是否有括号,都得不到任何结果。唯一的TRUE值是UI_DISPLAYNAME包含“%smith%”; OBJ_TITLE或子选择中都没有匹配记录。 - Sam F.
1
实验绝对是弄清 SQL 查询状况的最佳方式。只需开始添加、减去、翻转从句,直到你搞明白为止 :) - ean5533

16

你的where子句目前类似于以下内容:

(A || B) && C && D

没有括号,它是这样的

A || B && C && D

这等同于

A || (B && (C && D))

2
实际上只有ABC。你看到的D实际上是从第12行开始的内部选择的一部分。由于SQL格式的方式,很难判断。不过你的基本想法还是正确的。 - ean5533

2

0

由于您已将 where 语句更改为始终返回符合 Upper( OBJ_TITLE ) LIKE Upper( '%smith%' ) 条件的记录,无论下一个 AND 如何。


1
为了帮助Sam找到它,它在第12行。顺便说一句,很棒的发现。+1 - Ken White
第12行是内部选择的一部分,我不明白它与OP的问题有什么关系。 - ean5533
它不是内部查询的一部分。删除第6行和第9行上的括号将导致在Upper(OBJ_TITLE)LIKE Upper('% smith%')为true时返回记录,如果您撤消您的反对票,我将不胜感激...... - Maess
@Maess,很抱歉,但你错了。看看第11行:AND OBJ_ID IN (select ... -- 那个内部选择继续到第12行,实际上一直延伸到最后(因为还有另一个嵌套在其中的内部选择,然后是_另一个_嵌套在其中的内部选择!这是一个四级深度的内部选择)。 - ean5533
如果前面的OR为真,则该AND的评估没有影响,因为()被消除了。内部选择直到第11行才开始。无论内部选择如何评估,如果OR语句评估为真,则优先级更高。我认为你误读了问题,他问为什么在删除()时它会返回结果。 - Maess
你还是误解了。导致他在没有括号的情况下获得更多数据的原因是第一个子句 Upper( UI_DISPLAYNAME ) LIKE Upper( '%smith%' ) 评估为真并短路了。你所指的子句只有在 找不到 数据时才能短路 where。再次强调,and 必须先被评估,然后其结果才成为 or 的一部分。换句话说,为了确定记录是否包含在没有括号的情况下,引擎会说“如果第7行为真或者(第8行和第9行及以上为真)”。因此,他会获取返回第7行为真的记录。 - ean5533

0

这是基本的操作顺序。括号意味着在测试任何其他内容之前,所有内容都会被测试。在评估括号中的项目后返回的值将用于评估其余部分。由于AND优先于OR,所以括号使SQL首先评估OR。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接