2009-04-06 14 views
15

Mamy Oracle 10g i potrzebujemy zapytać 1 tabelę (bez sprzężeń) i odfiltrować wiersze, w których 1 z kolumn ma wartość zerową. Kiedy to zrobimy - GDZIE Nasza kolumna NIE JEST NIETRZYLNA - otrzymujemy pełne skanowanie tabeli na bardzo dużym stole - ZŁE ZŁO ZŁE. Kolumna ma indeks, ale w tym przypadku zostanie zignorowany. Czy są jakieś rozwiązania?Oracle 10g - zoptymalizuj GDZIE NIE JEST NULL

Dzięki

Odpowiedz

20

Optymalizator uważa, że ​​pełne skanowanie tabeli będzie lepsze.

Jeśli jest tylko kilka wierszy NULL, optymalizator ma rację.

Jeśli jesteś absolutnie pewien, że dostęp wskaźnik będzie szybciej (czyli masz więcej niż 75% wiersze z col1 IS NULL), a następnie schować zapytanie:

SELECT /*+ INDEX (t index_name_on_col1) */ 
     * 
FROM mytable t 
WHERE col1 IS NOT NULL 

Dlaczego 75%?

Ponieważ użycie do pobrania wartości nieobjętych indeksem implikuje ukryte połączenie na ROWID, które kosztuje około 4 razy tyle, co skanowanie tabeli.

Jeśli zakres indeksu obejmuje więcej niż 25% wierszy, skanowanie tabeli jest zwykle szybsze.

Jak wspomniano przez Tony Andrews, współczynnik skupień jest bardziej dokładną metodą pomiaru tej wartości, ale 25% jest nadal dobrą zasadą.

+1

Quassnoi, gdzie dostaniesz 75%? Jeśli istnieje milion wierszy i tylko jeden ma wartość NULL, dlaczego użycie indeksu na tych kolumnach będzie wolniejsze niż skanowanie tabeli? – tpdi

+1

Ponieważ indeks implikuje ukryte sprzężenie na ROWID, co kosztuje około 4 razy więcej niż skanowanie tabeli. Czy selektywność indeksów jest mniejsza niż 25%, skanowanie tabeli jest zwykle szybsze. – Quassnoi

+2

W pełnym widoku tabeli po prostu przejrzyj wszystkie wiersze w tabeli; jeśli wykonasz skanowanie indeksu, najpierw musisz odczytać indeks, a potem odczytać tabelę. Od pewnego momentu koszt czytania indeksu jest wyższy niż po prostu odczytanie całej tabeli. – andri

2

Jeśli robisz select *, to miałoby sens, aby zrobić skan tabeli zamiast używać indeksu. Jeśli wiesz, które kolumny Cię interesują, możesz utworzyć indeks objęty listą z tymi kolumnami i tym, do którego stosujesz warunek IS NOT NULL.

0

Utwórz indeks tej kolumny.

Aby upewnić się, że indeks jest używany, powinien znajdować się w indeksie i innych kolumnach w miejscu.

ocdecio odpowiedział:

Jeśli robisz select *, wtedy byłoby sensu robić tabelę skanowania zamiast używać indeksu.

To nie jest ściśle prawda; indeks zostanie użyty, jeśli istnieje indeks pasujący do klauzuli where, a optymalizator zapytań zdecyduje, że indeks będzie szybszy niż skanowanie tabeli. Jeśli nie ma indeksu lub nie ma odpowiedniego indeksu, tylko wtedy należy wykonać skanowanie tabeli.

+0

Doskonałe komentarze na temat Select * Tylko do wyjaśnienia - nigdy nie wybieramy SELECT * z innych powodów - zawsze dołączamy naszą listę kolumn do klauzul SELECT. –

15

Optymalizator podejmie decyzję na podstawie względnego kosztu pełnego skanowania tabeli i korzystania z indeksu. To głównie sprowadza się do tego, ile bloków trzeba będzie przeczytać, aby spełnić zapytanie. Reguła 25%/75% wspomniana w innej odpowiedzi jest uproszczona: w niektórych przypadkach pełne skanowanie tabeli ma sens nawet w przypadku uzyskania 1% wierszy - tj. Jeśli te wiersze rozłożą się wokół wielu bloków.

Na przykład, rozważmy tę tabelę:

SQL> create table t1 as select object_id, object_name from all_objects; 

Table created. 
SQL> alter table t1 modify object_id null; 

Table altered. 

SQL> update t1 set object_id = null 
    2 where mod(object_id,100) != 0 
    3/

84558 rows updated. 

SQL> analyze table t1 compute statistics; 

Table analyzed. 

SQL> select count(*) from t1 where object_id is not null; 

    COUNT(*) 
---------- 
     861  

Jak widać, tylko około 1% wierszy T1 mają niezerową object_id.Ale ze względu na sposób, w jaki zbudowałem stół, te 861 rzędów rozłożą się mniej więcej równomiernie wokół stołu. Dlatego zapytanie:

select * from t1 where object_id is not null; 

może odwiedzić prawie każdy blok w T1, aby uzyskać dane, nawet jeśli optymalizator użył indeksu. Warto wtedy zrezygnować z indeksu i przejść do pełnego skanowania tabeli!

Kluczowym statystyka aby pomóc w identyfikacji tej sytuacji jest czynnikiem wskaźnik klastrów:

SQL> select clustering_factor from user_indexes where index_name='T1_IDX'; 

CLUSTERING_FACTOR 
----------------- 
       460 

Wartość 460 jest bardzo wysoka (w porównaniu do 861 wierszy w indeksie), i sugeruje, że pełne skanowanie tabela być użytym. Zobacz this DBAZine article on clustering factors.

1

Może to zależeć od rodzaju indeksu, który masz na stole.

Większość indeksów B-drzewa ma wartość , a nie. Indeksy bitmapowe do przechowywać puste wpisy.

Tak więc, jeśli masz:

select * from mojatabela gdzie mycolumn jest null

i masz standardowy indeks B-drzewo na mycolumn, wówczas zapytanie nie może użyj indeksu, ponieważ "null" nie znajduje się w indeksie.

(Jeśli indeks jest przeciwko wielu kolumn, a jeden z indeksowanych kolumnach nie jest null to nie będzie wpis w indeksie.)

+1

Pytanie dotyczyło sprawdzenia "nie jest puste", a nie "jest puste". – KajMagnus

+0

Niemniej jednak, jest to przydatna informacja. –

0

Warto również sprawdzenie, czy statystyki Oracle na stole są do data. Może nie wiedzieć, że pełne skanowanie tabeli będzie wolniejsze.

0

Oracle nie indeks wartości null w ogóle w regularnych (B-tree) indeksów, więc nie można go używać nie można” • zmusić bazę danych Oracle do jej użycia.

BR

+0

To pytanie dotyczy 'is NOT null'. Wszystkie odpowiednie wartości znajdą się w indeksie. –

0

Korzystanie z podpowiedzi powinno być wykonywane raczej jako praca, a nie rozwiązanie.

Jak wspomniano w innych odpowiedziach, wartość pusta nie jest dostępna w indeksach BREEF.

Ponieważ wiesz, że w tej kolumnie są w większości wartości zerowe, czy byłbyś w stanie zastąpić wartość pustą przez zakres na przykład.

To naprawdę zależy od kolumny i charakteru danych, ale zazwyczaj, jeśli kolumna jest typem data na przykład:

where mydatecolumn is not null mogą być tłumaczone w zasadzie mówiąc: Chcę, wszystkie wiersze, które mają działanie data.

Następnie można z całą pewnością to zrobić: gdzie mydatecolumn < = sysdate (Oracle)

ta zwróci wszystkie wiersze z datą i ommit wartości null przy jednoczesnym wykorzystaniu indeksu na tej kolumny bez użycia poradnik.

Powiązane problemy