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

cmd*_*tos 1 sql oracle performance plsql sql-tuning

我有一个这种形式的查询,平均需要约100个子句元素,并且在极少数情况下> 1000个元素.如果大于1000个元素,我们将把in子句中的内容减少到1000(Oracle最大值).

SQL的形式是

SELECT * FROM tab WHERE PrimaryKeyID IN (1,2,3,4,5,...)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Jus*_*ave 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);
Run Code Online (Sandbox Code Playgroud)

要么

SELECT *
  FROM table_name
 WHERE primary_key IN (SELECT primary_key
                         FROM TABLE( nested_table ));
Run Code Online (Sandbox Code Playgroud)

要么

SELECT *
  FROM table_name
 WHERE primary_key IN (SELECT primary_key
                         FROM some_other_source);
Run Code Online (Sandbox Code Playgroud)

如果您只使用一个可共享的SQL语句,那么除了避免不断重新解析语句的成本之外,您还可以使用许多选项来强制执行不涉及修改SQL语句的特定计划.不同版本的Oracle具有不同的计划稳定性选项 - 根据您的版本,存储大纲,SQL计划管理SQL配置文件以及其他技术.您可以使用这些来强制特定SQL语句的特定计划.但是,如果继续生成必须重新解析的新SQL语句,则使用这些技术变得非常困难.