Oracle SQL:选择一个非常大的表的子集的最佳方法是什么

时间:2018-02-02 07:57:55

标签: sql oracle performance

我已经在这些论坛漫游了几年,而且我总是发现我的问题已经被提出,并且已经有了合适的答案。

我现在有一个非常通用(也许很容易)的问题,但我还没有能够找到一个问同一个问题的线程。

情况:

  • 我有一张付款表,每天有10-50万条记录,10天的历史记录和数百列。索引大约10-20列。其中一个指数是batch_id。
  • 我有一个批处理表,其记录和列数相当少,例如每天10k和30列。

如果我想从一位特定发件人中选择所有付款,我可以这样做:

Select * from payments p 
where p.sender_id = 'SenderA'

这会运行一段时间,即使sender_id也被编入索引。所以我认为,首先选择批次更好,然后使用batch_id进入付款表:

select * from payments p
where p.batch_id in 
(select b.batch_id from batches where b.sender_id = 'SenderA')
--and p.sender_id = 'SenderA'

现在,我的问题是:

  • 在第二个脚本中,我应该在支付表的where子句中取消注释Sender_id吗?对sender_id进行两次过滤并不是非常有效,即使它在不同的表中也是如此。
  • 如果我将其设为内连接而不是嵌套查询,会更好吗?
  • 如果我将它设为公共表表达式而不是嵌套查询或内连接,那会更好吗?

我想这一切都可以归结为一个问题:查询此问题的最佳方式是什么?

2 个答案:

答案 0 :(得分:2)

在最坏的情况下,两个查询应该在同一时间运行,在最好的情况下,我希望第一个查询运行得更快。如果运行速度较慢,则其他地方会出现问题。在第二个查询中,您不需要其他条件。

第一个查询将检索单个值的索引条目,因此将访问比第二个查询更少的块,第二个查询必须查找多个批次的索引条目(以及执行子查询,但这可能不重要)。

但与Oracle一样的危险是,有很多因素决定优化器选择哪个查询计划。我会立即验证您的索引列的统计信息是最新的。如果不是,这可能是您的问题,您不需要再进一步阅读。

下一步是获取查询执行计划。我的猜测是,这会告诉您查询正在运行全表扫描。

Oracle是否选择对此类查询执行全表扫描取决于返回的行数以及Oracle是否认为使用索引或仅读取整个表更有效。在两者之间翻转的阈值不是固定的数字:它取决于很多事情,其中​​一个是名为DB_FILE_MULTIBLOCK_READ_COUNT的参数。

这是由Orale设置的,理论上它应该配置为索引和全表扫描查询之间的转换应该是平滑的。换句话说,在您的查询返回足够的行以使整个表扫描更有效的转换点,索引扫描和表扫描应该花费大致相同的时间。

不幸的是,我已经看到这样的系统已经解决了,并且Oracle转向过快地执行全表扫描,一旦行数超过某个阈值就会导致查询时间过长。

正如我之前所说,首先检查您的统计数据。如果这不起作用,请获取QEP并开始调整Oracle实例。

调整Oracle是一个非常复杂的主题,在这里无法全面解答,所以我不得不推荐链接。以下是有关参数的有用页面:减少它可能会有所帮助:Why Change the Oracle DB_FILE_MULTIBLOCK_READ_COUNT

除此之外,一般的Oracle性能调优指南在这里:(Oracle) Configuring a Database for Performance

如果您仍然遇到问题,则需要进一步调查,然后提出更具体的问题。

修改 根据您的评论,您说您的查询返回表中10M-50M的4M行。如果它是10M中的4M,那么索引就没有任何用处。即使有50M的4M,仍然可以肯定全表扫描是最有效的方法。

你说你有很多列,所以这个4M行获取可能会返回大量数据。

您可以考虑拆分一些不需要的列并将它们放入子表中。特别是,如果您的列包含大量数据(例如,某些文本注释或其他内容),则最好将它们保留在主表之外。

记住 - 小的很快,不仅在行数方面,而且在每行的大小方面。

答案 1 :(得分:-3)

  1. SQL是一种声明性语言。这意味着,您指定了您喜欢的内容。
  2. 检查您的索引主要和“正常”...