2016-01-21 70 views
1

我想抓取Oracle數據庫中給定索引列值的特定行。我選擇索引列,然後選擇整行數據以便於處理。桌子上大約有一億五千萬行,我選擇大約一萬到二十萬。我使用NVL()來替換IN子句中的空值,因爲我在JAVA中動態創建這些語句。 IN子句被分塊爲由OR拼湊在一起的1000個索引塊,以避免「列表中最大表達式數量爲1000」的錯誤。優化對巨大表格的選擇查詢?

正如你所想,這個查詢需要很長時間才能運行。有什麼我可以做的,以優化這個查詢,但保持相同的功能? (無視指標值,因爲它們只是一個例子)

select INDEX_COLUMN_A, INDEX_COLUMN_B, INDEX_COLUMN_C, TABLE.* from TABLE  
Where (NVL(INDEX_COLUMN_A,''), NVL(INDEX_COLUMN_B,''), NVL(INDEX_COLUMN_C,'')) 
IN (('1','1','1'), ('2','2','2'), ... ('1000','1000','1000')) 
OR (NVL(INDEX_COLUMN_A,''), NVL(INDEX_COLUMN_B,''), NVL(INDEX_COLUMN_C,'')) 
IN (('1001','1001','1001'), ('1002','1002','1002'), ... ('2000','2000','2000')) 
OR... 
+4

在像NVL這樣的函數調用中包裝索引列有效地防止Oracle使用索引。您可以創建一個基於函數的索引或者消除「NVL」調用。 –

+0

如果您選擇表格的1/1000行,索引可能甚至不會有用;無論如何,全表掃描可能會更快。 –

+0

@MickMnemonic啊我沒有意識到NVL會毀了索引。這是最不幸的。謝謝(你的)信息。 – JoshKni8

回答

2

可以安全地避免從條件nvl()如果沒有在測試值遇到null('1','1','1')('2000','2000','2000')的可能性:

select 
    INDEX_COLUMN_A, INDEX_COLUMN_B, INDEX_COLUMN_C, 
    TABLE.* 
from 
    TABLE  
where 
    (INDEX_COLUMN_A, INDEX_COLUMN_B, INDEX_COLUMN_C) 
    in (
    ('1','1','1'), 
    ('2','2','2'), 
    ..., 
    ('1000','1000','1000') 
) 
    OR 
    (INDEX_COLUMN_A, INDEX_COLUMN_B, INDEX_COLUMN_C) 
    in (
    ('1001','1001','1001'), 
    ('1002','1002','1002'), 
    ..., 
    ('2000','2000','2000') 
) 
    or 
    ... 

此外,像a in (x1,x2) or a in (x3,x4)表達意味着((a=x1 or a=x2) or (a=x3 or a=x4))。在這種情況下的括號可以省略而沒有後果:(a=x1 or a=x2 or a=x3 or a=x4)可以用in表達式縮短爲a in (x1, x2, x3, x4)。因此初始查詢(如果在值不爲空值,以測試)是相同如下:

select 
    INDEX_COLUMN_A, INDEX_COLUMN_B, INDEX_COLUMN_C, 
    TABLE.* 
from 
    TABLE  
where 
    (INDEX_COLUMN_A, INDEX_COLUMN_B, INDEX_COLUMN_C) 
    in (
    ('1','1','1'), 
    ('2','2','2'), 
    ..., 
    ('1000','1000','1000') 
    ..., 
    ('1001','1001','1001'), 
    ('1002','1002','1002'), 
    ..., 
    ('2000','2000','2000') 
) 

P.S.

a in (x,y,z)僅僅是一套由or連接的平等關係的快捷方式:((a=x) or (a=y or (a=z))null總是不等於null,所以表達null in (x,y,z)不會返回true不管xyz值。因此,如果您確實需要處理null值,則表達式必須更改爲nvl(a,'some_never_encountered_value') in (nvl('1', 'some_never_encountered_value'), nvl('2','some_never_encountered_value'),...)之類的內容。但是在這種情況下,你不能在表上使用簡單的索引。建立功能索引來處理這些表達式是可能的,但這是一個非常不同的故事。

P.P.S. 如果列包含數字,則必須使用數字進行測試:而不是('1','1','1')您應該使用(1,1,1)

+0

感謝您的詳細解答。我不確定是否可以確保不存在空值的可能性,因此我最初使用NVL。我需要保留Or's,因爲沒有它們,我得到'列表中最大表達式數量爲1000個錯誤'。最後,測試數字與數字相比,而不是字符串數字提高性能?還是僅僅爲了良好的編碼禮儀? – JoshKni8

0

where子句使用or還可以防止使用索引。你可以用

select * 
    from table 
where <condition1> 
union all 
select * 
    from table 
where <condition2> 

第二個查詢替換查詢像

select * 
    from table 
where <condition1> or <condition2> 

,反過來,會產生重複的行(這取決於數據)。爲防止重複,您可以使用union而不是union all,但使用union也會引起性能問題。您需要進行大量實驗,哪種方式最適合您的情況。

+0

運行*大約100 - 200,000 * SELECT/UNION代替? – dnoeth

+0

使用OR是否真的阻止索引的使用?我正在使用它來防止「列表中的最大表達數是1000錯誤」。 – JoshKni8

+1

@ JoshKni8並不總是,但有時可以預防。大多數情況下,當你有像a> 100或b <200'這樣的條件時。在你的情況下(使用'in(1,2,3,...)')索引將被使用。 – Dmitry