Domanda

Ho creato un Oracolo indice di Testo come il seguente:

create index my_idx on my_table (text) indextype is ctxsys.context; 

E poi posso fare la seguente:

select * from my_table where contains(text, '%blah%') > 0;

Ma supponiamo di avere un hanno un'altra colonna di questa tabella, dire group_id, e io che volevo fare la seguente query:

select * from my_table where contains(text, '%blah%') > 0 and group_id = 43;

Con l'indice precedente, Oracle dovrà cercare tutti gli elementi che contengono 'blah', e quindi controllare tutti i loro group_ids.

Idealmente, io preferisco cercare solo gli elementi con group_id = 43, così vorrei un indice di simile a questo:

create index my_idx on my_table (group_id, text) indextype is ctxsys.context; 

Un po ' come un normale indice, in modo separato in un testo di ricerca può essere eseguita per ogni group_id.

C'è un modo per fare qualcosa di simile a questo in Oracle (sto usando 10g se è importante)?

Edit (chiarimenti)

Si consideri una tabella con un milione di righe e i seguenti due colonne, tra gli altri, A e B, sia numerico.Diciamo che ci sono 500 diversi valori di A e 2000 valori diversi di B, e ogni riga è unico.

Ora andiamo a considerare select ... where A = x and B = y

Un indice A e B separatamente per quanto mi riguarda posso dire che fare un indice di ricerca sul B, che sarà di ritorno di 500 righe, e poi fare un join/scansione in queste righe.In ogni caso, almeno 500 righe devono essere guardato (a parte il database di essere fortunato e trovare la riga richiesta anticipata.

Mentre un indice su (A,B) è molto più efficace, si trova una riga in un indice di ricerca.

Mettendo indici separati su group_id e il testo che mi sento solo lascia il generatore di query con due opzioni.

(1) Utilizzare il group_id indice, e la scansione di tutti i conseguenti righe per il testo.
(2) Utilizzare l'indice di testo, e la scansione di tutte le righe risultanti per l' group_id.
(3) l'Uso di entrambi gli indici, e fare un join.

Considerando che voglio:

(4) Utilizzare il (group_id, "text") indice a trovare il testo indice sotto il particolare group_id e la scansione dell'indice di testo per la particolare riga/righe di cui ho bisogno.No scansione e controllo o di entrare richiesto, proprio come quando si utilizza un indice (A,B).

È stato utile?

Soluzione

Testo Oracle

1 - Puoi migliorare le prestazioni creando l'indice di contesto con Filtro di:

create index my_idx on my_table(text) indextype is ctxsys.context filter by group_id;

Nei miei test il filter by Sicuramente migliorato le prestazioni, ma era ancora leggermente più veloce usare solo un indice BTREE su Group_ID.

2-Gli indici CTXCAT usano "sottoindici" e sembrano funzionare in modo simile a un indice multi-colonna. Questa sembra essere l'opzione (4) che stai cercando:

begin
  ctx_ddl.create_index_set('my_table_index_set');
  ctx_ddl.add_index('my_table_index_set', 'group_id');
end;
/

create index my_idx2 on my_table(text) indextype is ctxsys.ctxcat
    parameters('index set my_table_index_set');

select * from my_table where catsearch(text, 'blah', 'group_id = 43') > 0

Questo è probabilmente l'approccio più veloce. Usando la query sopra contro 120 MB di testo casuale simile allo scenario A e B richiesto solo 18 ottimi coerenti. Ma il rovescio della medaglia, la creazione dell'indice CTXCAT ha richiesto quasi 11 minuti e ha usato 1,8 GB di spazio.

(Nota: Oracle Testo sembra funzionare correttamente qui, ma non ho familiarità con il testo e non riesco a fare questo non è un uso inappropriato di questi indici come ha detto @nulluserexception.)

Indici multi-colonna vs. indice join

Per la situazione che descrivi nella tua modifica, normalmente Non ci sarebbe una differenza significativa tra l'utilizzo di un indice su (a, b) e l'iscrizione di indici separati su A e B. Ho creato alcuni test con dati simili a quelli che hai descritto e un indice richiedeva solo 7 ottimi coerenti rispetto a 2 ottimi coerenti Per l'indice multi-colonna.

La ragione di ciò è perché Oracle recupera i dati in blocchi. Un blocco è di solito 8K e un blocco indice è già ordinato, quindi probabilmente puoi adattarsi ai valori da 500 a 2000 in pochi blocchi. Se sei preoccupato per le prestazioni, di solito l'IO per leggere e scrivere blocchi è l'unica cosa che conta. Il fatto che Oracle debba unirsi o meno a qualche migliaio di righe è una quantità insignificante di tempo della CPU.

Tuttavia, questo non si applica agli indici di testo Oracle. Puoi unirti a un indice di contesto con un indice BTREE (un "bitmap e"?), Ma la performance è scarsa.

Altri suggerimenti

Metterei un indice group_id E vedi se è abbastanza buono. Non dici di quante righe stiamo parlando o di quali prestazioni hai bisogno.

Ricorda, l'ordine in cui vengono gestiti i predicati non è necessariamente l'ordine in cui li hai scritti nella query. Non provare a superare in astuzia l'ottimizzatore a meno che tu non abbia un vero motivo.

Versione breve: Non c'è bisogno di farlo. L'ottimizzatore di query è abbastanza intelligente da decidere qual è il modo migliore per selezionare i tuoi dati. Basta creare un indice btree su group_id, cioè:

CREATE INDEX my_group_idx ON my_table (group_id); 

Versione lunga: Ho creato uno script (testperf.sql) che inserisce 136 righe di dati fittizi.

DESC my_table;

Name     Null     Type      
-------- -------- --------- 
ID       NOT NULL NUMBER(4) 
GROUP_ID          NUMBER(4) 
TEXT              CLOB      

C'è un indice Btree su group_id. Per garantire che l'indice venga effettivamente utilizzato, esegui questo come utente DBA:

EXEC DBMS_STATS.GATHER_TABLE_STATS('<YOUR USER HERE>', 'MY_TABLE', cascade=>TRUE);

Ecco quante righe ciascuna group_id ha e la percentuale corrispondente:

GROUP_ID               COUNT                  PCT                    
---------------------- ---------------------- ---------------------- 
1                      1                      1                      
2                      2                      1                      
3                      4                      3                      
4                      8                      6                      
5                      16                     12                     
6                      32                     24                     
7                      64                     47                     
8                      9                      7         

Si noti che l'ottimizzatore query utilizzerà un indice solo se pensa che sia una buona idea, ovvero stai recuperando fino a una certa percentuale di righe. Quindi, se gli chiedi un piano di query su:

SELECT * FROM my_table WHERE group_id = 1;
SELECT * FROM my_table WHERE group_id = 7;

Vedrai che per la prima query utilizzerà l'indice, mentre per la seconda query eseguirà una scansione a tabella completa, poiché ci sono troppe righe per essere efficaci l'indice quando group_id = 7.

Ora, considera una condizione diversa - WHERE group_id = Y AND text LIKE '%blah%' (Dal momento che non ho molto familiarità con ctxsys.context).

SELECT * FROM my_table WHERE group_id = 1 AND text LIKE '%ipsum%';

Guardando il piano di query, lo vedrai volere Usa l'indice su group_id. Si noti che l'ordine delle tue condizioni non è importante:

SELECT * FROM my_table WHERE text LIKE '%ipsum%' AND group_id = 1;

Genera lo stesso piano di query. E se provi a eseguire la stessa domanda su group_id = 7, vedrai che torna alla scansione completa del tavolo:

SELECT * FROM my_table WHERE group_id = 7 AND text LIKE '%ipsum%';

Si noti che le statistiche vengono raccolte automaticamente da Oracle ogni giorno (è programmato per funzionare ogni sera e nei fine settimana), per migliorare continuamente l'efficacia dell'ottimizzatore di query. In breve, Oracle fa del suo meglio per ottimizzare l'ottimizzatore, quindi non è necessario.

Non ho un'istanza di Oracle è a portata di mano per prova, e non hanno usato l'indicizzazione full-text in Oracle, ma mi hanno generalmente avuto buone prestazioni con inline vista, che potrebbe essere un'alternativa per il tipo di indice che si aveva in mente.È la seguente sintassi legit quando contiene() è coinvolto?

Questa vista in linea si ottiene il PK valori delle righe in gruppo 43:

             (
             select T.pkcol
             from T
             where group = 43
             )

Se il gruppo ha un normale indice, e non ha cardinalità bassa, il recupero di questa serie dovrebbe essere veloce.Poi si sarebbe inner join che insieme con il nuovo T:

           select * from T
           inner join
            (
             select T.pkcol
             from T
             where group = 43
             ) as MyGroup

           on T.pkcol = MyGroup.pkcol
           where contains(text, '%blah%') > 0

Speriamo che l'ottimizzatore sarebbe in grado di utilizzare il PK indice di ottimizzare il join e poi appy il contiene predicato solo per il gruppo di 43 righe.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top