Join

Uno degli operatori fondamentali, data la query che segue

SELECT *
FROM Recensioni R, Sommelier S
WHERE R.sid=S.sid

Determinare la maniera più efficiente per rispondere non e banale in quanto l’operatore di join comporta potenzialmente un alto costo di esecuzione, negli anni sono state pensate diverse soluzioni la cui efficienza dipende una combinazione di diversi fattori tra cui:

  • indici utilizzabili
  • ordinamento dei dati
  • quantità di buffers
  • possibilità di restituire i risultati

lasciamo perdere l’opzione di fare il prodotto cartesiano e applicare una selezione

Nested loop join

La soluzione più semplice prevede il confronto fra i record delle due relazioni

for r in R:
	for s in S:
		if joinPred(r,s):
			# add to result

Il costo e tuttavia esorbitante in quanto si ha

è conveniente avere come relazione esterna quella con più tuple ma il guadagno sulle prestazioni non è significativo

il nested loop join conserva l'ordine della relazione esterna, ottimo per query che chiedono di essere ordinate

Paged nested loop join

Questa versione rinuncia alla proprietà di ordinamento per guadagnare in numero di operazioni di I/O

for Rpage in R:
	for Spage in S:
		for r, s in Rpage,Spage:
			if joinPred(r,s):
				# add to result

Sfruttando i buffer, Block nested loops join

Gli algoritmi sopracitati non tengono in conto la dimensione del buffer, in caso di pagine di buffer si possono utilizzare pagine per la relazione esterna per la relazione interna e per l’output

ci può essere un caso in cui la relazione interna a il maggior numero di buffer ovvero quando si può contenere interamente in memoria

Matching nel block nested loop join

Per effettuare il matching nel block nested loop join si può sfruttare una funzione di hash per allocare i record della relazione , le pagine di vengono lette sequenzialmente e le tuple sottoposte alla stessa funzione di hash per trovare il match

flowchart LR
A[(relazione R)]
subgraph central_memory
direction LR
F((H))
G[H1]
H[H2]
I[...]
J[HN]
K((H))
end
N[(relazione S)]
A ~~~ central_memory ~~~ N
 F --> G & H & I & J ~~~ K 
 K --> G

Sfruttando gli indici: index nested loop join

e possibile utilizzare un indice per accedere alla relazione interna, il costo dipende dal tipo di indice (b+tree oppure indici_hash) e da se l’indice e clustered o meno

image.png

si possono sfruttare operazioni di push down delle selezioni per alleggerire l'esecuzione di query di ricerca

SELECT * FROM SOMELIER S RECENSIONI R
WHERE R.SID=S.SID
AND R.RIVISTA= "sapore di vino"

Merge-scan join

si basa sul fatto che entrambe le relazioni siano ordinati sull’attributo di join (in caso non sia vero e necessario ordinarle). Le due relazioni vengono scandite e accoppiate secondo la chiave di join

result=[]
while !F.empty() and G.empty():
 
    if f.A > g.B
        g = G.next()
 
    else if f.A < g.B
        f = F.next()
 
	# after first match find all values with matches
    else
		result.append(f)
		f_1 = F.next()
        while ! F.empty() and f_1.A = g.B:
			result.append(f)
			f_1 = F.next()
      
        g_1 = G.next()
        while ! G.empty() and g_1.B = f.A
			result.append(g)
	        g_1 = G.next()
 
        f = F.next()
        g = G.next()

Il costo e la somma del numero di pagine di entrambe le relazioni

Se si ha un indice sugli attributi di join non e necessario ordinare le relazioni, se unclustered il costo e comunque elevato

Hash join

il vantaggio del Merge-scan join è che viene ridotto il numero di confronti fra i record delle relazioni, lo stesso obbiettivo si può ottenere con una funzione di hash sui valor degli attributi di join, la strategia e quella vista per la proiezione fatta con hashing, mentre la fase di matching può essere realizzata per mezzo del block nested loop join

sia hash join che merge scan join sono utilizzabili solo per equi join

Outer join

In caso di outer left/right join:

SELECT
FROM D.*, E.* Department D LEFT JOIN Employee E
ON (E.WorkDept = D.DeptNo)

Gli algoritmi precedenti si modificano come segue

  • Nested loop join: si usa come relazione esterna e si aggiunge all’output ogni tupla di che non trova match in
  • Merge-scan join: si usa come relazione esterna e, quando non si trova un match per una tupla di , la si aggiunge al risultato
  • Hash join: si usa come relazione esterna . Dopo aver partizionato e , per ogni partizione di si costruisce una hash table e si fa il probing per tutti i record di nella partizione omologa. Se il probing non trova match si aggiunge il record di all’output

In caso di full join:

  • Nested loop join: non è applicabile
  • Merge-scan join: aggiunge le tuple dangling di entrambi gli input
  • Hash join: per aggiungere anche le tuple dangling della relazione esterna, quando si costruisce la hash table si aggiunge un flag per tener traccia di quali tuple hanno trovato un match. Al termine si fa un passo finale sulla hash table per collezionare tutte le tuple dangling

PREVIOUS NEXT