2010-04-13 1 views
12

Ho una tabella le cui colonne sono varchar(50) e float. Ho bisogno (molto rapidamente) di ottenere il float associato a una determinata stringa. Anche con l'indicizzazione, questo è piuttosto lento.Indicizzazione SQL su varchar

So, tuttavia, che ogni stringa è associata a un numero intero, che conosco al momento della ricerca, in modo che ogni stringa si associ a un numero intero univoco, ma ogni numero intero non esegue il mapping su una stringa univoca. Si potrebbe pensare ad esso come una struttura ad albero.

C'è qualcosa da guadagnare con l'aggiunta di questo intero al tavolo, l'indicizzazione su di esso, e l'utilizzo di una query come:

SELECT floatval FROM mytable WHERE phrase=givenstring AND assoc=givenint 

Questo è Postgres, e se non si poteva dire, ho molto poco esperienza con i database.

risposta

14

Le chiavi sulle colonne VARCHAR possono essere molto lunghe, il che si traduce in un numero inferiore di record per pagina e maggiore profondità (più livelli in B-Tree). Gli indici più lunghi aumentano anche il rapporto miss della cache.

Quante stringhe nella mappa media a ogni intero?

Se ci sono relativamente pochi, è possibile creare un indice solo sulla colonna integer e PostgreSQL farà la multa di filtraggio sui record:

CREATE INDEX ix_mytable_assoc ON mytable (assoc); 

SELECT floatval 
FROM mytable 
WHERE assoc = givenint 
     AND phrase = givenstring 

si può anche considerare la creazione dell'indice sui hash stringa:

Ogni hash è solo 16 byte, quindi le chiavi dell'indice saranno molto più corte pur conservando quasi perfettamente la selettività.

+0

Anche il confronto delle chiavi di indice è molto più costoso con varchar, dato che sono a conoscenza delle impostazioni internazionali. L'indice intero sarà sicuramente molto più veloce di qualsiasi altra opzione. –

+0

@Magnus: il confronto dovrebbe essere eseguito solo con 'log (n)' volte, quindi non definirei questo "molto" più costoso, ma hai ragione, aggiunge anche alcuni cicli 'CPU'. – Quassnoi

-1

Dichiarando un indice su (phrase, assoc, floatval) si ottiene un "indice di copertura", che consente di eseguire la query pubblicata nella domanda senza nemmeno accedere alla tabella. Supponendo che sia phrase o assoc sia altamente selettivo (non molte righe condividono lo stesso valore per il campo), la creazione di un indice su quel campo da solo dovrebbe produrre quasi le stesse prestazioni.

Generalmente, si desidera limitare il numero di indici al set più piccolo che ottiene le query frequenti fino alla prestazione desiderata. Per ogni indice aggiunto a una tabella, si paga dello spazio su disco, ma soprattutto si paga il prezzo di avere il DBMS fare più lavoro su ogni INSERT nella tabella.

+0

PostgreSQL non ha indici di copertura, quindi l'indice sarebbe sicuramente una perdita. –

+0

@Magnus: Quindi, anche se un indice copre tutti i campi necessari per rispondere a una query, PostgreSQL dovrà accedere alla tabella effettiva per recuperare i valori? Hai qualche riferimento per questo? Sono un po 'curioso di sapere * perché * :) –

+0

A partire da 9.2, PostgreSQL ora ha scansioni di solo indice: https://wiki.postgresql.org/wiki/Index-only_scans#Covering_indexes Dettagli nella parte superiore di quel post per quanto riguarda perché non lo ha fatto in precedenza: con gli indici PostgreSQL, "non è direttamente possibile accertare se una determinata tupla è visibile alla transazione corrente". – jwadsack

-1

Non poteva fare male provare ad aggiungere l'int e rendere l'indice su int, varchar e includere float - questo sarebbe di copertura e piuttosto efficiente - non so se Postgres ha incluso le colonne - se non lo aggiunge semplicemente all'indice stesso.

Ci sono diverse altre tecniche si poteva guardare in (io non sono a conoscenza di tutte le caratteristiche Postgres, quindi darò loro per nome SQL Server):

vista indicizzati - si può materializzarsi in modo efficace una visione che unisce diverse tabelle - quindi puoi unire il tuo varchar al tuo int e avere il tuo indice su int e varchar e float

Colonne incluse - puoi includere le colonne in un indice per assicurarti che l'indice copra - cioè avere un indice su varchar include (float): se l'indice non copre, il Query Optimizer dovrà ancora utilizzare l'indice e quindi eseguire una ricerca nei segnalibri per ottenere i dati rimanenti.

+1

'PostgreSQL' non supporta viste indicizzate o colonne incluse, ma supporta indici basati su funzioni (non è necessario materializzare un'espressione per farlo indicizzare). – Quassnoi

3

mi consiglia semplicemente un indice di hash:

create index mytable_phrase_idx on mytable using hash(phrase); 

In questo modo le query come

select floatval from mytable where phrase='foo bar'; 

sarà molto veloce. Prova questo:

create temporary table test (k varchar(50), v float); 
insert into test (k, v) select 'foo bar number '||generate_series(1,1000000), 1; 
create index test_k_idx on test using hash (k); 
analyze test; 
explain analyze select v from test where k='foo bar number 634652'; 
 
                QUERY PLAN              
----------------------------------------------------------------------------------------------------------------- 
Index Scan using test_k_idx on test (cost=0.00..8.45 rows=1 width=8) (actual time=0.201..0.206 rows=1 loops=1) 
    Index Cond: ((k)::text = 'foo bar number 634652'::text) 
Total runtime: 0.265 ms 
(3 rows) 
+1

In questa tabella di test, non riesco a vedere la differenza tra btree e hash. – hiroshi

0

Risposta breve: sì, ci sarà molto da guadagnare. Almeno fino a quando non si hanno molti aggiornamenti, ma è abbastanza probabile che anche il sovraccarico non sarà evidente.