WHERE_IN使用复合键查询?

Kev*_*uli 8 sql oracle

假设我有一个包含两部分组合键的表,以及4条记录,如下所示:

KEY_PART_1 KEY_PART_2
A          1
B          1
C          2
C          3
Run Code Online (Sandbox Code Playgroud)

我想编写一些动态SQL,只使用"WHERE IN"子句选择记录B,1和C,而不选择A,1或C,3.

有没有办法在没有临时表的情况下做到这一点?

这并不重要,但我们目前正在使用Oracle,并希望尽快转向PostgreSQL.

Jus*_*ave 17

此语法适用于Oracle和PostgreSQL:

SELECT *
  FROM table_name
 WHERE (key_part_1, key_part_2) IN ( ('B',1), ('C',2) );
Run Code Online (Sandbox Code Playgroud)

  • 不知道Oracle是否存在相同的问题,但是在MySQL中,如果使用此语法,则不会使用索引。 (2认同)
  • 它也适用于Postgres. (2认同)

Lal*_*r B 6

根据 @Justin Cave 的回答,这里有一个小测试用例,显示Oracle将为以下过滤谓词执行索引范围扫描,然后执行INLIST ITERATOR

WHERE (key_part_1, key_part_2) IN ( ('B',1), ('C',2) )
Run Code Online (Sandbox Code Playgroud)

设置

SQL> CREATE TABLE t(key1 VARCHAR2(1), key2 NUMBER);

Table created.

SQL>
SQL> INSERT INTO t VALUES('A', 1);

1 row created.

SQL> INSERT INTO t VALUES('B', 1);

1 row created.

SQL> INSERT INTO t VALUES('C', 2);

1 row created.

SQL> INSERT INTO t VALUES('C', 3);

1 row created.

SQL>
SQL> COMMIT;

Commit complete.

SQL>
Run Code Online (Sandbox Code Playgroud)

key1 和 key2复合索引:

SQL> CREATE INDEX t_idx ON t(key1, key2);

Index created.

SQL>
Run Code Online (Sandbox Code Playgroud)

收集统计数据:

SQL> EXEC DBMS_STATS.gather_table_stats('LALIT', 'T');

PL/SQL procedure successfully completed.

SQL>
Run Code Online (Sandbox Code Playgroud)

执行查询:

SQL> SELECT * FROM t
  2  WHERE (key1, key2) IN ( ('B',1), ('C',2) );

K       KEY2
- ----------
B          1
C          2

SQL>
Run Code Online (Sandbox Code Playgroud)

因此,它给出了正确的输出。

让我们看看解释计划

Case# 1键值对与索引的顺序相同。领先关键在领先。

SQL> SELECT * FROM TABLE(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2301620486

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |     2 |    10 |     1   (0)| 00:00:01 |
|   1 |  INLIST ITERATOR  |       |       |       |            |          |
|*  2 |   INDEX RANGE SCAN| T_IDX |     2 |    10 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
---------------------------------------------------

   2 - access(("KEY1"='B' AND "KEY2"=1 OR "KEY1"='C' AND "KEY2"=2))

14 rows selected.
Run Code Online (Sandbox Code Playgroud)

Case#2与索引顺序相反的键值对。前导键反向。

SQL> EXPLAIN PLAN FOR SELECT * FROM t
  2  WHERE (key2, key1) IN ( (1, 'B'), (2, 'C') );

Explained.

SQL>
SQL> SELECT * FROM TABLE(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2301620486

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |     2 |    10 |     1   (0)| 00:00:01 |
|   1 |  INLIST ITERATOR  |       |       |       |            |          |
|*  2 |   INDEX RANGE SCAN| T_IDX |     2 |    10 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
---------------------------------------------------

   2 - access(("KEY1"='B' AND "KEY2"=1 OR "KEY1"='C' AND "KEY2"=2))

14 rows selected.
Run Code Online (Sandbox Code Playgroud)

无论哪种情况,Oracle 都使用索引