1

考虑一个主键作为列“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;

2)

Select * from Table where id in ( 1, 2, 3, 4 );

两者中哪一个在 Oracle 作为数据库和使用 Spring JDBC 模板进行持久性方面的性能更好。假设数据集 1、2、3、4 将在 Java 数据结构的限制范围内,则从应用程序的角度抛开内存限制。

它使用数据库连接池。

4

5 回答 5

5

我宁愿使用第二个查询,因为它只查询数据库一次。除此之外,您也只能在数据库上连接一次

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

于 2013-03-02T17:35:46.050 回答
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.

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

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.

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

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

四次 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

在这里,您可以看到向数据库发送四倍多的语句的开销最多。根据索引深度和聚类因子,这里和那里可能存在一些变化,但一个查询与四个查询仍然是最大的区别。

所以,希望这个线程的读者不会再说“已知 IN 子句很昂贵”:-)

问候,
罗布。

于 2013-03-03T09:06:19.330 回答
3

我认为这个问题是过早优化的一个很好的例子,为什么应该避免它。

您正在尝试从表中获取多个 ID 的行。要使用的明显查询应该是以最简单的方式满足要求的查询。除非有很好的理由,否则我总是会考虑使用:

Select * from Table where id in ( 1, 2, 3, 4 );

这使后来的开发人员清楚地知道发生了什么以及为什么。它还将优化任务留给数据库来处理,这就是它的用途。

如果在编写此代码后您发现存在性能问题,那么是时候考虑优化了。

对于任何类型的代码问题,最简单的解决方案通常是最好的。

我确实意识到我在这里有点离题并且没有很多知识 JDBC 但确实有很多 Oracle 知识

于 2013-03-02T22:44:59.423 回答
3

我将假设您正在使用 PreparedStatment

select * from TABLE where ID=?;

在 Oracle 中,使用 PreparedStatement 将允许 Oracle 缓存 SQL 的执行计划生成。因此传递 1 或 2 或 1000 的 ID 将使用相同的缓存 Oracle 计划。

甲骨文将对待

select * from TABLE where ID in (1, 2, 3);

select * from TABLE where ID in (4, 8, 15, 16, 23, 42);

作为两个不同的查询并为两者生成不同的执行计划。因此,从 Oracle 的角度来看,类型 1 更可取。

实际上,如果您在 ID 上放置索引,您将不会注意到两种样式之间的实质性差异。

与性能调优一样,“不要猜测,做测试”。使用 YourKit 甚至 System.currentTimeMillis() 来查看真实世界的数字。也不要为了微不足道的性能提升而牺牲代码清晰度。

于 2013-03-02T18:04:26.187 回答
0

您可能有一个选项3...

我的问题是您是如何获得这些 ID 值的?

我发现必须使用一组唯一标识符查询表的模式,这是一种奇怪的模式。

您更有可能在其他列上有 (a) 谓词,这些列标识您要检索的完全相同的行集。

于 2013-03-03T09:52:59.617 回答