2012-03-22 12 views
2

我今天遇到了一個SQL語句的問題,我可以通過添加其他條件來解決問題,但是我真的很想知道爲什麼我的更改能夠解決問題。爲什麼更改此條件的where子句會大幅縮短執行時間?

問題查詢:

SELECT * 
FROM 
    (SELECT ah.*, 
    com.location, 
    ha.customer_number, 
    d.name applicance_NAME, 
    house.name house_NAME, 
    dr.name RULE_NAME 
FROM actionhistory ah 
INNER JOIN community com 
ON (t.city_id = com.city_id) 
INNER JOIN house_address ha 
ON (t.applicance_id = ha.applicance_id 
AND ha.status_cd = 'ACTIVE') 
INNER JOIN applicance d 
ON (t.applicance_id = d.applicance_id) 
INNER JOIN house house 
ON (house.house_id = t.house_id) 
LEFT JOIN the_rule tr 
ON (tr.the_rule_id = t.the_rule_id) 
WHERE actionhistory_id >= 'ACT100010000' 
ORDER BY actionhistory_id 
) 
WHERE rownum <= 30000; 

「修復」

SELECT * 
FROM 
    (SELECT ah.*, 
    com.location, 
    ha.customer_number, 
    d.name applicance_NAME, 
    house.name house_NAME, 
    dr.name RULE_NAME 
FROM actionhistory ah 
INNER JOIN community com 
ON (t.city_id = com.city_id) 
INNER JOIN house_address ha 
ON (t.applicance_id = ha.applicance_id 
AND ha.status_cd = 'ACTIVE') 
INNER JOIN applicance d 
ON (t.applicance_id = d.applicance_id) 
INNER JOIN house house 
ON (house.house_id = t.house_id) 
LEFT JOIN the_rule tr 
ON (tr.the_rule_id = t.the_rule_id) 
WHERE actionhistory_id >= 'ACT100010000' and actionhistory_id <= 'ACT100030000' 
ORDER BY actionhistory_id 
) 

所有_id的列被索引序列。 第一個查詢的解釋計劃的成本爲372,第二個爲14.這是在Oracle 11g數據庫上運行的。

此外,如果where子句中的actionhistory_id小於ACT100000000,則原始查詢立即返回。

回答

3

這是因爲actionhistory_id列上的索引。

在第一次查詢期間,Oracle必須返回包含索引的所有索引塊,以便在「ACT100010000」之後出現記錄,那麼它必須將索引與表匹配以獲取所有記錄,然後從中抽取29999條記錄結果集。

在第二個查詢期間,Oracle只需返回包含'ACT100010000'和'ACT100030000'之間記錄的索引塊。然後它從表中獲取索引塊中表示的那些記錄。在找到索引之後抓取記錄的步驟比在使用第一個查詢時少得多。

注意到你的最後一行關於id是否小於ACT100000000 - 聽起來這些記錄可能都在同一個內存塊(或在一組連續的塊中)。

編輯:也請考慮一下Justin所說的話 - 我在談論實際的表現,但他指出id是一個varchar極大地增加了潛在的價值(而不是一個數字),並且估計的計劃可能會反映比實際更大的時間,因爲優化器在執行之前不知道全部範圍。爲了進一步優化,考慮到他的觀點,你可以在ID列上放置一個基於函數的索引,或者你可以把它作爲一個組合鍵,一列中的varchar部分和另一列中的數字部分。

1
  • 這兩個查詢的計劃是什麼?
  • 表中的統計信息是最新的嗎?
  • 這兩個查詢是否返回相同的一組行?它們並不是很明顯,但可能ACT100030000是系統中最大的actionhistory_id。這也有點令人困惑,因爲第一個查詢的謂詞actionhistory_id的值爲TRA100010000,這與第二個查詢中的ACT值非常不同。我猜這是一個錯字?
  • 您是否正在測量獲取第一行所需的時間?或者獲取最後一行所需的時間?那些經過的時間是多少?

我沒有這種資料的猜測是,你似乎是使用了錯誤的數據類型,請actionhistory_id列影響Oracle優化的,生成適當的基數估計這可能導致優化器低估的選擇性能力的事實你的謂詞併產生表現不佳的計劃。一個人可能會猜到actionhistory_id是一個字符串,它以ACT10000開頭,然後有從0000130000的30,000個連續數值,但優化器不是那麼聰明。它看到一個13個字符的字符串,並且無法弄清楚最後10個字符總是數字,所以只有10個可能的值而不是256個(假設是8位字符),前8個字符總是將成爲相同的常數值。另一方面,如果actionhistory_id被定義爲NUMBER並且具有介於1和30000之間的值,則優化器對各種謂詞的選擇性作出合理的估計將顯着更容易。