SQL查询模式和相对性能

for*_*rtm 1 java sql oracle spring-jdbc spring-batch

考虑具有主键的表作为columnd"id"

必须查询一系列id为[1,2,3,4 ......]

有2个选项 -

1)

Select * from Table where id = 1;
Select * from Table where id = 2;
Select * from Table where id = 3;
Select * from Table where id = 4;
Run Code Online (Sandbox Code Playgroud)

2)

Select * from Table where id in ( 1, 2, 3, 4 );
Run Code Online (Sandbox Code Playgroud)

对于Oracle而言,这两者中的哪一个在性能方面更好,并且使用Spring JDBC模板来实现持久性.假设数据集1,2,3,4将在java数据结构的限制范围内,请从应用程序的角度考虑内存约束.

它使用数据库连接池.

Joh*_*Woo 5

我宁愿使用第二个查询,因为它只查询数据库一次.除此之外,您只需连接数据库一次.

请记住,数据库连接成本很高(消耗大量资源).

  • 我同意.单个语句总是比4个单独的语句快(如果它们检索相同的总数据量). (4认同)
  • 我认为这没有考虑到与缓存,连接池等有关的事情,或者IN子句比常规equals子句贵得多的事实.你可能仍然是对的,但我认为它不会被削减和干燥"更少的查询比更多的查询更好". (2认同)

Rob*_*ijk 5

要备份JW和a_horse_with_no_name的语句,这里是一个测试,都使用相同的连接,因此这里忽略连接(池)问题.

首先使用主键创建表并收集优化程序的统计信息:

SQL> create table mytable
  2  as
  3   select level id
  4        , lpad('*',1000,'*') filler
  5     from dual
  6  connect by level <= 10000
  7  /

Table created.

SQL> alter table mytable add constraint my_pk primary key (id)
  2  /

Table altered.

SQL> exec dbms_stats.gather_table_stats(user,'mytable')

PL/SQL procedure successfully completed.
Run Code Online (Sandbox Code Playgroud)

接下来,将所有语句放在共享池中,作为预热,切断查询的大部分输出:

SQL> select * from mytable where id = 1
  2  /

        ID
----------
FILLER
---------------------------------------------------------------------------------------------------------------------------------------
         1
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
***************************************************************************************************************************************
*******************************************************


1 row selected.

SQL> select * from mytable where id = 2
  2  /

1 row selected.

SQL> select * from mytable where id = 3
  2  /

1 row selected.

SQL> select * from mytable where id = 4
  2  /

1 row selected.

SQL> select * from mytable where id in (1,2,3,4)
  2  /    

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

接下来,检查autotrace输出,以查看数据库必须执行的工作:

SQL> set autotrace traceonly
SQL> select * from mytable where id = 1
  2  /

1 row selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3280897506

---------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     1 |  1005 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| MYTABLE |     1 |  1005 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | MY_PK   |     1 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------

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

   2 - access("ID"=1)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
       1387  bytes sent via SQL*Net to client
        448  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select * from mytable where id = 2
  2  /

1 row selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3280897506

---------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     1 |  1005 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| MYTABLE |     1 |  1005 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | MY_PK   |     1 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------

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

   2 - access("ID"=2)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
       1387  bytes sent via SQL*Net to client
        448  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select * from mytable where id = 3
  2  /

1 row selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3280897506

---------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     1 |  1005 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| MYTABLE |     1 |  1005 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | MY_PK   |     1 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------

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

   2 - access("ID"=3)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
       1387  bytes sent via SQL*Net to client
        448  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select * from mytable where id = 4
  2  /

1 row selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3280897506

---------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     1 |  1005 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| MYTABLE |     1 |  1005 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | MY_PK   |     1 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------

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

   2 - access("ID"=4)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
       1387  bytes sent via SQL*Net to client
        448  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select * from mytable where id in (1,2,3,4)
  2  /

4 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 1637292604

----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |     4 |  4020 |     4   (0)| 00:00:01 |
|   1 |  INLIST ITERATOR             |         |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| MYTABLE |     4 |  4020 |     4   (0)| 00:00:01 |
|*  3 |    INDEX UNIQUE SCAN         | MY_PK   |     4 |       |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------------

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

   3 - access("ID"=1 OR "ID"=2 OR "ID"=3 OR "ID"=4)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          8  consistent gets
          0  physical reads
          0  redo size
       2435  bytes sent via SQL*Net to client
        448  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          4  rows processed

SQL> set autotrace off
Run Code Online (Sandbox Code Playgroud)

四次3次连续获得与8次一致获得.最后,让我们通过10,000次执行所有语句来计算两种变体:

SQL> set timing on
SQL> declare
  2    v_id     number;
  3    v_filler varchar2(1000);
  4  begin
  5    for i in 1 .. 10000
  6    loop
  7      select *
  8        into v_id, v_filler
  9        from mytable
 10       where id = 1
 11      ;
 12      select *
 13        into v_id, v_filler
 14        from mytable
 15       where id = 2
 16      ;
 17      select *
 18        into v_id, v_filler
 19        from mytable
 20       where id = 3
 21      ;
 22      select *
 23        into v_id, v_filler
 24        from mytable
 25       where id = 4
 26      ;
 27    end loop;
 28  end;
 29  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:01.03
SQL> /

PL/SQL procedure successfully completed.

Elapsed: 00:00:01.00
SQL> /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.99
SQL> declare
  2    v_id     number;
  3    v_filler varchar2(1000);
  4  begin
  5    for i in 1 .. 10000
  6    loop
  7      for r in (select * from mytable where id in (1,2,3,4))
  8      loop
  9        v_id := r.id;
 10        v_filler := r.filler;
 11      end loop;
 12    end loop;
 13  end;
 14  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.41
SQL> /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.39
SQL> /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.39
SQL> set timing off
Run Code Online (Sandbox Code Playgroud)

在这里,您可以看到向数据库发送四次以上语句的开销最大.根据索引深度和聚类因子,这里和那里可能存在一些变化,但是一个查询与四个查询仍将是最大的差异.

所以,希望这个帖子的读者不再说"IN条款已经很昂贵了":-)

问候,
Rob.