没有连接的Oracle IN子句有什么性能影响?

时间:2014-04-01 22:10:23

标签: sql performance oracle plsql sql-tuning

我有一个这种形式的查询,平均需要约100个子句元素,并且在某些罕见的时间> 1000个元素。如果大于1000个元素,我们会将in子句中的内容减少到1000(Oracle最大值)。

SQL采用

的形式
SELECT * FROM tab WHERE PrimaryKeyID IN (1,2,3,4,5,...)

我从中选择的表格很大,并且将包含比我的in子句中多出的数百万行。我担心的是优化器可能会选择进行表扫描(我们的数据库没有最新的统计数据 - 是的 - 我知道......)

我是否可以传递强制使用主键的提示 - 不知道主键的索引名称,可能是...... / * + DO_NOT_TABLE_SCAN * /?

是否有任何创造性的方法来撤回数据

  1. 我们执行最少的往返次数
  2. 我们读取的块数最少(在逻辑IO级别?)
  3. 这会更快..
  4. SELECT * FROM tab WHERE PrimaryKeyID = 1
      UNION
    SELECT * FROM tab WHERE PrimaryKeyID = 2
      UNION
    SELECT * FROM tab WHERE PrimaryKeyID = 2
      UNION ....
    

1 个答案:

答案 0 :(得分:5)

如果表上的统计信息是准确的,那么当WHERE中只有1000个硬编码元素时,优化器应该不太可能选择执行表扫描而不是使用主键索引条款。最好的方法是收集(或设置)对象的准确统计数据,因为这会导致好的事情自动发生,而不是尝试做大量的体操以解决不正确的统计数据。

如果我们假设统计数据不准确,优化程序会导致认为表扫描比使用主键索引更有效,那么您可以添加DYNAMIC_SAMPLING提示会强制优化器在优化语句之前收集更准确的统计信息,或强制CARDINALITY提示覆盖优化程序的默认基数估计值。这些都不需要知道有关可用索引的任何信息,它只需要知道表别名(或者如果没有别名则命名)。 DYNAMIC_SAMPLING将是更安全,更健壮的方法,但它会为解析步骤增加时间。

如果要在IN子句中构建具有可变数量的硬编码参数的SQL语句,则可能会通过使用不可共享的共享池充斥您自己的性能问题SQL并强制数据库花费大量时间分别解析每个变量。如果您创建一个可以解析一次的可共享SQL语句,效率会更高。取决于IN子句值的来源,可能类似于

SELECT *
  FROM table_name
 WHERE primary_key IN (SELECT primary_key
                         FROM global_temporary_table);

SELECT *
  FROM table_name
 WHERE primary_key IN (SELECT primary_key
                         FROM TABLE( nested_table ));

SELECT *
  FROM table_name
 WHERE primary_key IN (SELECT primary_key
                         FROM some_other_source);

如果您只使用一个可共享的SQL语句,那么除了避免不断重新解析语句的成本之外,您还有许多选项可用于强制执行不涉及修改SQL语句。不同版本的Oracle具有不同的计划稳定性选项 - 根据您的发布版本,其他技术中包含stored outlinesSQL plan managementSQL profiles。您可以使用这些来强制特定SQL语句的特定计划。但是,如果继续生成必须重新解析的新SQL语句,则使用这些技术变得非常困难。