Come è implementata una o più funzioni di aggregazione nella maggior parte dei motori SQL?

3

Nel libro Database Fundamentals, Silberschatz. Si spiega che le funzioni aggregate possono essere calcolate durante la marcia.

Questo ha senso. Ciò che significa è che per calcolare il massimo, calcolare la media o contare gli elementi di un set, non è necessario passare una copia del set alle procedure di aggregazione, si elabora solo ogni record nel frattempo si attraversa il set.

Un'implementazione ingenua potrebbe essere quella di mantenere una variabile per ogni aggregato desiderato. Ad esempio, un SELECT sum(a_field), count(a_field), max(a_field) FROM a_set potrebbe essere implementato come:

sum_ = 0
count_ = 0
max_ = -INF

for record in a_set:
    sum_ = sum_ + record.a_field
    count_ = count_ + 1
    max_ = max(max_, record.a_field)

return (sum_, count_, max_)

Ovviamente, questo è impensabile in quanto il loop sul set non dovrebbe essere così legato al calcolo aggregato. Suppongo che il ciclo deleghi l'aggregazione a una specie di coroutine.

Supponendo che una coroutine sia un tipo di oggetto con due metodi:

  • feed: dove puoi passare un valore alla coroutine
  • get: che ti dà il risultato di un calcolo

Il ciclo dovrebbe essere qualcosa del tipo:

# Given a set C of aggregation coroutines
for record in a_set:
    for c in C:
        c.feed(record.a_field)

return (c.get() for c in C)

In questo caso, immagino una coroutine come max come:

max_ = -INF
while item = consume():
    max_ = max(max_, item)
yield max_

Qui, suppongo che quando la coroutine invoca consume , aspetta che qualcuno chiami il suo metodo feed . E quando chiama yield , quel valore viene raccolto successivamente da colui che invoca il suo metodo get .

Solo per divertimento, implementiamo sum :

sum_ = 0
while item = consume():
    sum_ = sum_ + item
yield sum_

Quindi, questo è ampiamente quello che imagine sta accadendo dietro le quinte, ma non posso esserne sicuro, quindi:

  1. In che modo questo processo viene effettivamente implementato nella maggior parte dei motori SQL?.
  2. Che cosa accadrebbe con un'aggregazione che richiede due o più transversioni nel set di dati, come la deviazione standard?.

Nota: lo pseudo è una specie di pseudo Python.

    
posta jgomo3 04.04.2016 - 22:44
fonte

1 risposta

7

Credo che la maggior parte delle implementazioni RDBMS "moderne" siano basate su Cascades framework di ottimizzazione.

Parlerò di come Microsoft SQL Server gestisce questo, poiché questo è il DBMS con cui sono più familiare. SQL Server è un'implementazione del framework di ottimizzazione Cascades , quindi funziona e quelli per altri RDBMS "moderni" dovrebbero essere simili.

L'SQL ricevuto dal server viene convertito in una serie di operatori fisici dall'ottimizzatore. Gli operatori fisici inizializzano, raccolgono dati e chiudono. Nello specifico, l'operatore fisico può rispondere alle seguenti tre chiamate di metodo:

Init(): The Init() method causes a physical operator to initialize itself and set up any required data structures. The physical operator may receive many Init() calls, though typically a physical operator receives only one.

GetNext(): The GetNext() method causes a physical operator to get the first, or subsequent row of data. The physical operator may receive zero or many GetNext() calls.

Close(): The Close() method causes a physical operator to perform some clean-up operations and shut itself down. A physical operator only receives one Close() call.

Il piano di esecuzione per questa query

select 
    count(*),
    SUM(Number),
    MAX(Number),
    STDEV(Number)
from dbo.t1

può assomigliare a questo:

Hoomessoalcunioperatoripersemplicità.

L'esecuzioneprocededall'operatoredilivellosuperiore(piùasinistraneldiagramma).Unavoltachetuttoèstatoinizializzato(tramiteunacatenadichiamateInit())SELECTchiameràGetNext()suStreamAggregateeattenderàunarisposta.StreamAggregateinvieràunGetNext()all'operatoreIndexSeekcluster.LasuaimplementazionedicedirestituireunarigadallostoragepersistenteinrispostaaGetNext().StreamAggregateaggiungeràquindiivaloridiquellariganeisuoiregistriinterniperciascunodeivaloriaggregatichestatracciando(somma,conteggio,mediaoqualsiasialtracosa).Lasuaimplementazioneinternahalacapacitàdicontenereciascunodeivaloririchiesti.

StreamAggregatenonrispondeimmediatamenteaGetNext()diSELECT.PiuttostolasuaimplementazionedicedichiamarecontinuamenteGetNext()disuofiglio.SinoticheStreamAggregatenonsipreoccupadell'operatoredicuièfiglio.Capitadiessereunaricercadiindiceinclusterinquestoesempio,mapotrebbeessereunascansioneditabelle,unjoin,unacostanteoqualsiasialtracosa.Nonimportainquantotuttiglioperatoriimplementanolastessainterfacciaatremetodierispondonoesternamenteallostessomodoaquestetrechiamate.Inquestomodolafunzione"aggregazione" è isolata dalla funzione "lettura" e la funzione "looping" fa parte dell'implementazione di Stream Aggregate. L'ottimizzatore è libero di sostituire diverse implementazioni per ciascuna funzione come meglio ritiene ad es. utilizzando una ricerca indice o una scansione tabella.

Alla fine, Index Seek raggruppato risponderà a GetNext () con "nessun altro dato". A questo punto Stream Aggregate può fare i calcoli per restituire i valori richiesti. Per qualcosa di semplice come COUNT () il registro interno corrispondente viene passato come è. Per valori complessi come STDEV () Stream Aggregate contiene internamente i valori "semplici" equivalenti che possono essere calcolati con un singolo passaggio.

È sufficiente eseguire il calcolo finale della deviazione standard una volta che è sicuro che non ci sono ulteriori dati.

    
risposta data 21.04.2016 - 04:44
fonte

Leggi altre domande sui tag