2013-07-04 196 views
3

我知道這是一個常見問題,我已經閱讀了其他幾篇文章和論文,但是我找不到一個考慮索引字段和兩個查詢都可能返回的記錄數量的問題。嵌套選擇的性能

我的問題確實很簡單。在這裏推薦使用類似於SQL的語法(在性能方面)編寫這兩者中的哪一個。

首先查詢:

Select * 
from someTable s 
where s.someTable_id in 
        (Select someTable_id 
        from otherTable o 
        where o.indexedField = 123) 

第二個查詢:

Select * 
from someTable 
where someTable_id in 
        (Select someTable_id 
        from otherTable o 
        where o.someIndexedField = s.someIndexedField 
        and o.anotherIndexedField = 123) 

我的理解是,第二個查詢將在數據庫中查詢每一個元組外查詢將返回在第一個查詢將評估先選擇內部,然後將過濾器應用於外部查詢。

現在,第二個查詢可能會查詢數據庫超快,考慮到someIndexedField字段已編入索引,但說我們有成千上萬或數百萬記錄不會更快使用第一個查詢嗎?

注意:在Oracle數據庫中。

+1

。 。一般來說,沒有指定數據庫的性能問題是毫無意義的。 SQL是一種描述性語言,而不是過程語言,所以優化器(引擎的一部分)可以自由選擇任何查詢計劃對於給定查詢的最佳選擇。 –

+0

@GordonLinoff好點。該數據庫是一個Oracle數據庫。這個語言應該不是很重要,我想只是用SQL語法來編寫它。 – mixkat

+1

。 。根據Tom Kyte的說法,Oracle優化器足夠智能,可以識別相關的子查詢並將它們轉換爲適當的連接(http://asktom.oracle.com/pls/apex/f?p=100:11:0::NO :: P11_QUESTION_ID:3167884300346662300)。 Oracle有一個非常好的優化器。有趣的是,第一個版本在MySQL中性能完全可觀。 。 。直到解決問題的版本5.6。 –

回答

2

關於第一個查詢:

第一個查詢將評估內選擇第一,然後 過濾器適用於外部查詢。

不那麼簡單。

在SQL中,大多數不可能告訴先執行什麼以及稍後執行什麼。

因爲SQL - 聲明式語言。

你的「嵌套選擇」 - 只是在視覺上,而不是技術上。

示例1 - 在「someTable」中,您有10行,在「otherTable」中有10000行。

在大多數情況下,數據庫優化器將先讀取「someTable」,然後再檢查otherTable以匹配。爲此,它可以或不可以根據情況使用索引,在這種情況下填充 - 它將使用「indexedField」索引。

示例2 - 在「someTable」中,您有10000行,在「otherTable」中--10行。

在大多數情況下,數據庫優化器將從內存中的「otherTable」讀取所有行,將它們過濾爲123,並且會在someTable PK(someTable_id)索引中找到匹配項。結果 - 不會從「otherTable」中使用索引。

關於第二個查詢:

它從第一完全不同。所以,我不知道如何比較它們:

  • 第一次查詢鏈接兩個表一對:s.someTable_id = o。someTable_id
  • 第二個查詢通過兩對鏈接兩個表:s.someTable_id = o.someTable_id AND o.someIndexedField = s.someIndexedField。

鏈接兩個表的常見做法 - 是您的第一個查詢。 但是,o.someTable_id應該被索引。

如此普遍的規則是:

  • 所有PK - 應該被索引(它們默認索引)
  • 過濾(如在WHERE部分使用)的所有列應被索引
  • 所有用於提供表格之間匹配的列(包括IN,JOIN等) - 也是過濾的,所以 - 應該被索引。
  • 數據庫引擎將自行選擇最佳訂單操作(或並行)。在大多數情況下,你不能確定這一點。
  • 使用Oracle EXPLAIN PLAN(類似於大多數數據庫)來比較實際數據上不同查詢的執行計劃。
4

在MySQL中,如果嵌套選擇在同一個表上,那麼查詢的執行時間可能是地獄。

提高MySQL性能的一個好方法是爲嵌套select創建一個臨時表,並將main選擇應用於該表。

例如:

Select * 
from someTable s1 
where s1.someTable_id in 
        (Select someTable_id 
        from someTable s2 
        where s2.Field = 123); 

能有更好的表現:

create temporary table 'temp_table' as (
    Select someTable_id 
    from someTable s2 
    where s2.Field = 123 
); 

Select * 
from someTable s1 
where s1.someTable_id in 
        (Select someTable_id 
        from tempTable s2); 

我不知道有關的大量數據的性能。