为什么在子查询中忽略 MIN()/MAX() 函数

Vic*_*VKh 0 sql oracle

我需要解释为什么在子查询中忽略 MIN/MAX 函数。我在文档中没有找到任何有关见面行为的内容。

例如

有2张桌子

CREATE TABLE A(COL1 NUMBER);
CREATE TABLE B(COL1 NUMBER);
Run Code Online (Sandbox Code Playgroud)

通过以下 PL/SQL 块填充数据

SET TIMING ON
DECLARE
INSERT_STATEMENT VARCHAR2(200);
BEGIN 
FOR I IN 1..100
LOOP
INSERT_STATEMENT := 'INSERT INTO A(COL1) VALUES(round(DBMS_RANDOM.VALUE(1,3)))';
EXECUTE IMMEDIATE INSERT_STATEMENT;
INSERT_STATEMENT := 'INSERT INTO B(COL1) VALUES(round(DBMS_RANDOM.VALUE(1,2)))';
EXECUTE IMMEDIATE INSERT_STATEMENT;
END LOOP;
commit;
END;
/
Run Code Online (Sandbox Code Playgroud)

表值的分布

SELECT COL1,COUNT(COL1) COUNT# FROM A GROUP BY COL1;
SELECT COL1,COUNT(COL1) COUNT# FROM B GROUP BY COL1;


      COL1     COUNT#
---------- ----------
         1         26
         2         53
         3         21

SQL>
      COL1     COUNT#
---------- ----------
         1         50
         2         50
Run Code Online (Sandbox Code Playgroud)

“问题”查询是

SQL> SELECT COUNT(*) FROM A WHERE A.COL1=(SELECT MIN(B.COL1) FROM B WHERE A.COL1=B.COL1);

  COUNT(*)
----------
        79

SQL> SELECT COUNT(*) FROM A WHERE A.COL1=(SELECT MAX(B.COL1) FROM B WHERE A.COL1=B.COL1);

  COUNT(*)
----------
        79

-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |    67 |  2546 |     5  (20)| 00:00:01 |
|*  1 |  FILTER              |      |       |       |            |          |
|   2 |   HASH GROUP BY      |      |    67 |  2546 |     5  (20)| 00:00:01 |
|*  3 |    HASH JOIN SEMI    |      |    67 |  2546 |     4   (0)| 00:00:01 |
|   4 |     TABLE ACCESS FULL| A    |   100 |  2500 |     2   (0)| 00:00:01 |
|   5 |     TABLE ACCESS FULL| B    |   100 |  1300 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("A"."COL1"=MIN("A"."COL1"))
   3 - access("A"."COL1"="B"."COL1")
Run Code Online (Sandbox Code Playgroud)

两个查询都从表 A 中返回 79 行,其中表 A 的值存在于表 B 中。

我有点困惑,因为我预计上面的 MIN()/MAX() 函数查询应该分别返回 26 和 53 行。但看起来 MIN/MAX 函数在此类查询中被忽略。

预期结果的工作示例

SQL> SELECT COUNT(*) FROM A A2 WHERE A2.COL1=(SELECT MIN(B.COL1) FROM A,B WHERE A.COL1=B.COL1);

  COUNT(*)
----------
        26

SQL> SELECT COUNT(*) FROM A A2 WHERE A2.COL1=(SELECT MAX(B.COL1) FROM A,B WHERE A.COL1=B.COL1);

  COUNT(*)
----------
        53
Run Code Online (Sandbox Code Playgroud)

我想知道为什么在子查询中忽略 MIN/MAX 函数

SQL> SELECT COUNT(*) FROM A WHERE A.COL1=(SELECT MIN(B.COL1) FROM B WHERE A.COL1=B.COL1);
    
      COUNT(*)
    ----------
            79
    
SQL> SELECT COUNT(*) FROM A WHERE A.COL1=(SELECT MAX(B.COL1) FROM B WHERE A.COL1=B.COL1);
    
      COUNT(*)
    ----------
            79
Run Code Online (Sandbox Code Playgroud)

Ale*_*ole 5

你的子查询

(SELECT MAX(B.COL1) FROM B WHERE A.COL1=B.COL1)
Run Code Online (Sandbox Code Playgroud)

与 A 相关,因此对于 的每个值A.COL1,如果 B 中存在具有相同COL1值的任何行(对于 1 和 2 是这样,但对于 3 则不然),那么由于A.COL1=B.COL1MAX(B.COL1)也必须等于MAX(A.COL1)。您可以在谓词信息中看到它已转换为等效内容:

   1 - filter("A"."COL1"=MIN("A"."COL1"))
   3 - access("A"."COL1"="B"."COL1")
Run Code Online (Sandbox Code Playgroud)

又因为相关性,A.COL1 = MIN(A.COL1)真的只是A.COL1 = A.COL1

另一种看待它的方式是,当 B 中有一行时,您实际上将子查询结果替换为:

WHERE A.COL1 = A.COL1
Run Code Online (Sandbox Code Playgroud)

对于值 1 和 2 始终为 true COL1,对于 3 始终为 false;并且 B 中没有行,那么对于那些它被有效替换为的行:

WHERE A.COl1 = null
Run Code Online (Sandbox Code Playgroud)

这从来都不是真的。

删除这种相关性,它将执行您所期望的操作 - 如db<fiddle所示,具有不同的随机数据:

SELECT COUNT(*) FROM A WHERE A.COL1=(SELECT MIN(B.COL1) FROM B WHERE A.COL1=B.COL1);
Run Code Online (Sandbox Code Playgroud)
数数(*)
76
SELECT COUNT(*) FROM A WHERE A.COL1=(SELECT MAX(B.COL1) FROM B WHERE A.COL1=B.COL1);
Run Code Online (Sandbox Code Playgroud)
数数(*)
76
SELECT COUNT(*) FROM A WHERE A.COL1=(SELECT MIN(B.COL1) FROM B);
Run Code Online (Sandbox Code Playgroud)
数数(*)
24
SELECT COUNT(*) FROM A WHERE A.COL1=(SELECT MAX(B.COL1) FROM B);
Run Code Online (Sandbox Code Playgroud)
数数(*)
52