假设我有一个包含两部分组合键的表,以及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)
根据 @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 都使用索引。