【问题标题】:WHERE_IN query with a composite key?WHERE_IN 使用复合键查询?
【发布时间】:2015-04-07 13:18:21
【问题描述】:

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

KEY_PART_1 KEY_PART_2
A          1
B          1
C          2
C          3

我想编写一些动态 SQL 以使用“WHERE IN”子句仅选择记录 B,1 和 C,2,而不选择 A,1 或 C,3。

没有临时表有没有办法做到这一点?

没关系,但我们目前正在使用 Oracle,并希望尽快迁移到 PostgreSQL。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    此语法适用于 Oracle 和 PostgreSQL:

    SELECT *
      FROM table_name
     WHERE (key_part_1, key_part_2) IN ( ('B',1), ('C',2) );
    

    【讨论】:

    • 不知道Oracle是否有同样的问题,但是在MySQL中如果你使用这种语法它不会使用索引。
    • 你知道如何使用 ODP.NET 来做到这一点吗?
    • @Barmar 在 SQL 查询 the order in which the composing keys are used matters 中,它会影响是否依赖复合索引。
    • @Kamafeather 仅当WHERE 子句使用键中的列的子集,但此查询使用整个键时才相关。但是我测试了一下,mysql还是没有用到key。
    【解决方案2】:

    根据@Justin Cave 的回答,这里有一个小测试用例,表明 Oracle 会执行 INDEX RANGE SCAN,然后执行 INLIST ITERATOR 对于以下过滤谓词

    WHERE (key_part_1, key_part_2) IN ( ('B',1), ('C',2) )
    

    设置

    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>
    

    key1 和 key2 上的复合索引

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

    收集统计数据:

    SQL> EXEC DBMS_STATS.gather_table_stats('LALIT', 'T');
    
    PL/SQL procedure successfully completed.
    
    SQL>
    

    执行查询:

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

    所以,它给出了正确的输出。

    让我们看看解释计划

    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.
    

    案例# 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.
    

    在任何一种情况下,Oracle 都会使用索引

    【讨论】:

      【解决方案3】:

      我不确定,但我认为您想要这样的东西,它适用于几乎所有 RDBMS:

      select KEY_PART_1, KEY_PART_2 from your_table where KEY_PART_1='B' and KEY_PART_2 = '1'
      UNION
      select KEY_PART_1, KEY_PART_2 from your_table where KEY_PART_1='C' and KEY_PART_2 = '2'
      

      【讨论】:

      • 只是一个注释,虽然这个查询会给出想要的输出。但是,此查询将进行 2 次索引范围扫描。 Justin 的查询将执行单个索引范围扫描。因此,在性能方面,贾斯汀的查询更好。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-22
      • 2012-03-18
      • 2015-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多