2009-09-23 2 views
9

C'è un modo per forzare uno specifico ordine di join in Postgres?Ordine di join tabella in postgres

Ho una query simile a questa. Ho eliminato un sacco di cose che erano nella vera query, ma questa semplificazione dimostra il problema. Ciò che rimane non deve essere troppo criptico: utilizzando un sistema di sicurezza ruolo/compito, sto cercando di determinare se un determinato utente ha i privilegi per eseguire una determinata attività.

select task.taskid 
from userlogin 
join userrole using (userloginid) 
join roletask using (roleid) 
join task using (taskid) 
where loginname='foobar' 
and taskfunction='plugh' 

Ma ho capito che il programma conosce già il valore di userlogin, così sembrava la query potrebbe essere reso più efficiente saltando la ricerca sul UserLogin e solo compilando l'userloginid, in questo modo:

select task.taskid 
from userrole 
join roletask using (roleid) 
join task using (taskid) 
where userloginid=42 
and taskfunction='plugh' 

Quando l'ho fatto - eliminando una tabella dalla query e codificando il valore recuperato da quella tabella - il tempo del piano di spiegazione è aumentato! Nella query originale, Postgres legge userlogin quindi userrole quindi roletask quindi task. Ma nella nuova query, ha deciso di leggere prima roletask e quindi di unirsi a userrole, anche se ciò richiedeva una scansione di file completi su roletask.

completa Spiegare i piani sono:

Versione 1:

Hash Join (cost=12.79..140.82 rows=1 width=8) 
    Hash Cond: (roletask.taskid = task.taskid) 
    -> Nested Loop (cost=4.51..129.73 rows=748 width=8) 
     -> Nested Loop (cost=4.51..101.09 rows=12 width=8) 
       -> Index Scan using idx_userlogin_loginname on userlogin (cost=0.00..8.27 rows=1 width=8) 
        Index Cond: ((loginname)::text = 'foobar'::text) 
       -> Bitmap Heap Scan on userrole (cost=4.51..92.41 rows=33 width=16) 
        Recheck Cond: (userrole.userloginid = userlogin.userloginid) 
        -> Bitmap Index Scan on idx_userrole_login (cost=0.00..4.50 rows=33 width=0) 
          Index Cond: (userrole.userloginid = userlogin.userloginid) 
     -> Index Scan using idx_roletask_role on roletask (cost=0.00..1.50 rows=71 width=16) 
       Index Cond: (roletask.roleid = userrole.roleid) 
    -> Hash (cost=8.27..8.27 rows=1 width=8) 
     -> Index Scan using idx_task_taskfunction on task (cost=0.00..8.27 rows=1 width=8) 
       Index Cond: ((taskfunction)::text = 'plugh'::text) 

Versione 2:

Hash Join (cost=96.58..192.82 rows=4 width=8) 
    Hash Cond: (roletask.roleid = userrole.roleid) 
    -> Hash Join (cost=8.28..104.10 rows=9 width=16) 
     Hash Cond: (roletask.taskid = task.taskid) 
     -> Seq Scan on roletask (cost=0.00..78.35 rows=4635 width=16) 
     -> Hash (cost=8.27..8.27 rows=1 width=8) 
       -> Index Scan using idx_task_taskfunction on task (cost=0.00..8.27 rows=1 width=8) 
        Index Cond: ((taskfunction)::text = 'plugh'::text) 
    -> Hash (cost=87.92..87.92 rows=31 width=8) 
     -> Bitmap Heap Scan on userrole (cost=4.49..87.92 rows=31 width=8) 
       Recheck Cond: (userloginid = 42) 
       -> Bitmap Index Scan on idx_userrole_login (cost=0.00..4.49 rows=31 width=0) 
        Index Cond: (userloginid = 42) 

(Sì, lo so che in entrambi i casi i costi sono bassi e la differenza doesn Sembra che sia importante, ma dopo aver eliminato un po 'di lavoro aggiuntivo dalla query per semplificare ciò che devo pubblicare, la query reale non è oltraggiosa, ma sono più interessata a . L principio)

+2

È possibile visualizzare i piani di query (spiega analisi) e le definizioni di tabella? – hgmnz

+0

Ok, hai chiesto, ho sostituito l'ipotetico semplice esempio con la query reale e ho aggiunto i risultati del piano di spiegazione. Oh, sono sicuro che potrei aggiungere alcuni indici aggiuntivi per accelerare la seconda query, ma non è questo il punto. Perché Postgres ha scelto un piano che era meno del meglio che poteva fare, date le domande che aveva? Soprattutto quando ha dimostrato che avrebbe potuto fare meglio se avessi reso la query più complicata? – Jay

+0

stai confrontando i tempi di esecuzione effettivi delle query o semplicemente guardando il costo principale nel piano di spiegazione? i costi? in particolare, hai appena pubblicato spiegare l'output, non spiegare analizzare. anche se ti aspetteresti un costo più alto da equiparare a una più lenta esecuzione della query, potrebbe non funzionare in questo modo. – araqnid

risposta

4

Questa pagina nella documentazione descrive come prevenire l'ottimizzatore di PostgreSQL da riordino tabelle unite, che consente di controllare l'ordine di te stesso unisce:

http://www.postgresql.org/docs/current/interactive/explicit-joins.html

+1

PostgreSQL ha davvero la migliore documentazione che ho visto su un RDBMS. – hgmnz

+1

Hai provato questo? Certamente non voglio cambiare questa impostazione per tutte le domande, solo per uno o due qua e là. Se lo cambio con un'istruzione set, influisce sull'intero motore del database, o solo sulla connessione corrente o sulla transazione corrente? Hmm, suppongo di poterlo verificare aprendo due connessioni, impostandola da una, e poi vedendo se spiegare i piani sull'altra modifica ... – Jay

+0

Non l'ho provato. Hai ragione che provare te stesso in un paio di sessioni simultanee è il modo migliore per essere sicuro. Doc potrebbe essere sbagliato (anche se come sottolinea @hgiminez, raramente nel doc di PostgreSQL). –

1

Sei sicuro le tue statistiche di tabella sono aggiornati? Quando l'ottimizzatore basato sui costi di PostgreSQL fallisce con cose così banali, è un buon segno che qualcosa è seriamente sbagliato nelle statistiche della tabella. È meglio correggere la causa principale piuttosto che aggirarla sostituendo l'ottimizzatore integrato perché il problema si presenterà inevitabilmente anche da qualche altra parte.

Eseguire ANALYZE sulle tabelle interessate e verificare se PostgreSQL seleziona un piano diverso. Se sceglie ancora qualcosa di stupido, sarebbe davvero interessante vedere i piani di query. L'ottimizzatore che non fa la cosa giusta è solitamente considerato un bug.

+0

Sì. Dopo i miei primi risultati sorprendenti, ho riesaminato l'analisi su quei tavoli e poi ri-fatto i piani di spiegare, ed i risultati erano simili. – Jay

+1

Sembra un po 'strano. Qual è la tua impostazione effective_cache_size? Il 128M predefinito potrebbe portare a scansioni sequenziali irragionevoli su tabelle di piccole e medie dimensioni. Inoltre, per i database pesantemente memorizzati nella cache la possibilità di abbassare random_page_cost potrebbe essere una buona idea. –