PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Come passare un insieme di righe da una funzione all'altra?

Funzioni tabella

Eseguo migrazioni di database complesse e ad altissima velocità per vivere, utilizzando SQL sia come linguaggio client che server (non viene utilizzato nessun altro linguaggio), tutto in esecuzione lato server, dove il codice raramente emerge dal motore di database. Le funzioni della tabella svolgono un ruolo ENORME nel mio lavoro . Non uso "cursori" poiché sono troppo lenti per soddisfare i miei requisiti di prestazioni e tutto ciò che faccio è orientato al set di risultati. Le funzioni della tabella sono state per me un aiuto immenso nell'eliminare completamente l'uso dei cursori, raggiungendo velocità molto elevate e hanno contribuito notevolmente a ridurre il volume del codice e migliorare la semplicità.

In breve, utilizzi una query che fa riferimento a due (o più) funzioni di tabella per passare i dati da una funzione di tabella a quella successiva. Il set di risultati della query di selezione che chiama le funzioni di tabella funge da canale per passare i dati da una funzione di tabella a quella successiva. Sulla piattaforma / versione DB2 su cui lavoro, e sulla base di una rapida occhiata al manuale di Postgres 9.1 sembra che lo stesso sia vero lì, puoi passare solo una singola riga di valori di colonna come input a qualsiasi chiamata di funzione della tabella, come hai scoperto. Tuttavia, poiché la chiamata alla funzione tabella avviene nel mezzo dell'elaborazione del set di risultati di una query, si ottiene lo stesso effetto passando un intero set di risultati a ciascuna chiamata alla funzione tabella, sebbene, nell'impianto idraulico del motore di database, i dati vengano passati solo una riga alla volta per ciascuna funzione della tabella.

Le funzioni tabella accettano una riga di colonne di input e restituiscono un unico set di risultati nella query chiamante (cioè seleziona) che ha chiamato la funzione. Le colonne del set di risultati restituite da una funzione tabella diventano parte del set di risultati della query chiamante e sono quindi disponibili come input per la funzione tabella successiva , a cui si fa riferimento più avanti nella stessa query, in genere come join successivo. Le colonne dei risultati della prima funzione di tabella vengono inviate come input (una riga alla volta) alla seconda funzione di tabella, che restituisce le colonne del set di risultati nel set di risultati della query chiamante. Sia la prima che la seconda colonna del set di risultati della funzione tabella ora fanno parte del set di risultati della query chiamante e sono ora disponibili come input (una riga alla volta) per una terza funzione di tabella. Ogni chiamata di funzione tabella amplia il set di risultati della query chiamante tramite le colonne che restituisce. Questo può andare avanti fino a quando non inizi a raggiungere i limiti sulla larghezza di un set di risultati, che probabilmente varia da un motore di database all'altro.

Considera questo esempio (che potrebbe non corrispondere ai requisiti o alle capacità della sintassi di Postgres mentre lavoro su DB2). Questo è uno dei tanti modelli di progettazione in cui utilizzo le funzioni della tabella, è uno dei più semplici, che penso sia molto illustrativo e che prevedo avrebbe un ampio appeal se le funzioni della tabella erano ampiamente utilizzate dal mainstream (per quanto ne so, non lo sono, ma penso che meritino più attenzione di quanta ne stiano ricevendo).

In questo esempio, le funzioni di tabella in uso sono:VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH e DATA_WAREHOUSE_TODAYS_ORDER_BATCH. Nella versione DB2 su cui lavoro, avvolgi la funzione tabella all'interno di "TABLE(place table function call and parameters here )", ma sulla base di una rapida occhiata a un manuale di Postgres sembra che tu ometta il wrapper "TABLE()".

create table TODAYS_ORDER_PROCESSING_EXCEPTIONS as (

select      TODAYS_ORDER_BATCH.*
           ,VALIDATION_RESULT.ROW_VALID
           ,POST_RESULT.ROW_POSTED
           ,WAREHOUSE_RESULT.ROW_WAREHOUSED

from        TODAYS_ORDER_BATCH

cross join  VALIDATE_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function]  ) 
              as VALIDATION_RESULT ( ROW_VALID )  --example: 1/0 true/false Boolean returned

left join   POST_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
              as POST_RESULT ( ROW_POSTED )  --example: 1/0 true/false Boolean returned
      on    ROW_VALIDATED = '1'

left join   DATA_WAREHOUSE_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
              as WAREHOUSE_RESULT ( ROW_WAREHOUSED )  --example: 1/0 true/false Boolean returned
      on    ROW_POSTED = '1'

where       coalesce( ROW_VALID,      '0' ) = '0'   --Capture only exceptions and unprocessed work.  
      or    coalesce( ROW_POSTED,     '0' ) = '0'   --Or, you can flip the logic to capture only successful rows.
      or    coalesce( ROW_WAREHOUSED, '0' ) = '0'

) with data
  1. Se la tabella TODAYS_ORDER_BATCH contiene 1.000.000 di righe, allora VALIDATE_TODAYS_ORDER_BATCH verrà chiamato 1.000.000 di volte, una volta per ogni riga.
  2. Se 900.000 righe superano la convalida all'interno di VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH verrà chiamato 900.000 volte.
  3. Se solo 850.000 righe vengono pubblicate correttamente, VALIDATE_TODAYS_ORDER_BATCH necessita di alcune scappatoie chiuse LOL e DATA_WAREHOUSE_TODAYS_ORDER_BATCH verrà chiamato 850.000 volte.
  4. Se 850.000 righe sono state inserite correttamente nel Data Warehouse (ovvero non sono state generate eccezioni aggiuntive), la tabella TODAYS_ORDER_PROCESSING_EXCEPTIONS verrà popolata con 1.000.000 - 850.000 =150.000 righe di eccezione.

Le chiamate alla funzione tabella in questo esempio restituiscono solo una singola colonna, ma potrebbero restituire molte colonne. Ad esempio, la funzione tabella che convalida una riga di ordine potrebbe restituire il motivo per cui un ordine non è riuscito a convalidare.

In questo progetto, praticamente tutte le chiacchiere tra un HLL e il database vengono eliminate, poiché il richiedente HLL chiede al database di elaborare l'intero batch in UNA richiesta. Ciò si traduce in una riduzione di milioni di richieste SQL al database, in un'ENORME rimozione di milioni di procedure HLL o chiamate di metodi e, di conseguenza, fornisce un ENORME miglioramento del runtime. Al contrario, il codice legacy, che spesso elabora una singola riga alla volta, invierebbe in genere 1.000.000 di richieste SQL di recupero, 1 per ogni riga in TODAYS_ORDER_BATCH, più almeno 1.000.000 di richieste HLL e/o SQL a scopo di convalida, più almeno 1.000.000 HLL e /o richieste SQL a scopo di registrazione, più 1.000.000 di richieste HLL e/o SQL per l'invio dell'ordine al data warehouse. È vero che, utilizzando questo progetto di funzione tabella, all'interno delle funzioni tabella le richieste SQL vengono inviate al database, ma quando il database effettua richieste a se stesso (cioè dall'interno di una funzione tabella), le richieste SQL vengono gestite molto più velocemente (soprattutto rispetto a uno scenario legacy in cui il richiedente HLL sta eseguendo l'elaborazione di una riga singola da un sistema remoto, con il caso peggiore su una WAN - OMG, per favore non farlo).

È possibile riscontrare facilmente problemi di prestazioni se si utilizza una funzione tabella per "recuperare un set di risultati" e quindi unire tale set di risultati ad altre tabelle. In tal caso, l'ottimizzatore SQL non può prevedere quale insieme di righe verrà restituito dalla funzione tabella e pertanto non può ottimizzare il join alle tabelle successive. Per questo motivo, li uso raramente per recuperare un set di risultati, a meno che non sappia che il set di risultati sarà un numero molto ridotto di righe, quindi non causerà problemi di prestazioni, o non ho bisogno di unirmi a tabelle successive.

A mio parere, uno dei motivi per cui le funzioni di tabella sono sottoutilizzate è che spesso sono percepite solo come uno strumento per recuperare un set di risultati, che spesso funziona male, quindi vengono cancellate come uno strumento "scarso" da utilizzare.

Le funzioni della tabella sono estremamente utili per trasferire più funzionalità al server, per eliminare la maggior parte delle chiacchiere tra il server di database ei programmi su sistemi remoti e persino per eliminare le chiacchiere tra il server di database ei programmi esterni sullo stesso server. Anche le chiacchiere tra i programmi sullo stesso server comportano un sovraccarico maggiore di quanto molte persone si rendano conto e gran parte di esse non è necessario. Il cuore del potere delle funzioni tabella sta nell'usarle per eseguire azioni all'interno dell'elaborazione del set di risultati.

Esistono modelli di progettazione più avanzati per l'utilizzo delle funzioni tabella che si basano sul modello sopra, in cui è possibile massimizzare ulteriormente l'elaborazione del set di risultati, ma questo post è già molto da assorbire per la maggior parte delle persone.