2012-07-13 18 views
12

Devo eseguire la migrazione delle query SQL scritte per MS SQL Server 2005 a Postgres 9.1.
Qual è il modo migliore per sostituire CROSS APPLY in questa query?Postgres analogico a CROSS APPLY in SQL Server

SELECT * 
FROM V_CitizenVersions   
CROSS APPLY  
     dbo.GetCitizenRecModified(Citizen, LastName, FirstName, MiddleName, 
BirthYear, BirthMonth, BirthDay, .....) -- lots of params 

GetCitizenRecModified() funzione è una funzione con valori di tabella. Non riesco a posizionare il codice di questa funzione perché è davvero enorme, fa alcuni calcoli complicati e non posso abbandonarlo.

+0

Non è necessario croce si applica in Postgres. È possibile utilizzare una funzione di tabella esattamente come una funzione. Semplicemente unisciti a loro. –

+0

@a_horse_with_no_name - 'CROSS APPLY' esegue di nuovo il TVF con parametri correlati anziché eseguirlo una volta e quindi unirsi al risultato. –

risposta

8

In Postgres 9.3 o poi utilizzare un LATERAL registrazione:

SELECT v.col_a, v.col_b, f.* -- no parentheses here, f is a table alias 
FROM v_citizenversions v 
LEFT JOIN LATERAL f_citizen_rec_modified(v.col1, v.col2) f ON true 
WHERE f.col_c = _col_c; 

Perché LEFT JOIN LATERAL ... ON true?


Per vecchie versioni, c'è un modo molto semplice per realizzare ciò che penso si sta cercando di con una funzione di set-ritorno (RETURNS TABLE or RETURNS SETOF record OR RETURNS record):

SELECT *, (f_citizen_rec_modified(col1, col2)).* 
FROM v_citizenversions v 

La funzione calcola i valori o nce per ogni riga della query esterna. Se la funzione restituisce più righe, le righe risultanti vengono moltiplicate di conseguenza. Tutte le parentesi sono sintatticamente richieste per decomporre un tipo di riga. La funzione tabella potrebbe essere simile a questa:

CREATE OR REPLACE FUNCTION f_citizen_rec_modified(_col1 int, _col2 text) 
    RETURNS TABLE(col_c integer, col_d text) AS 
$func$ 
SELECT s.col_c, s.col_d 
FROM some_tbl s 
WHERE s.col_a = $1 
AND s.col_b = $2 
$func$ LANGUAGE sql; 

È necessario avvolgere questo in un subquery o CTE se si desidera applicare una clausola WHERE perché le colonne non sono visibili sullo stesso livello. (Ed è meglio per le prestazioni in ogni caso, perché prevenire il ripetuto di valutazione per ogni colonna di output della funzione):

SELECT col_a, col_b, (f_row).* 
FROM (
    SELECT col_a, col_b, f_citizen_rec_modified(col1, col2) AS f_row 
    FROM v_citizenversions v 
    ) x 
WHERE (f_row).col_c = _col_c; 

Ci sono molti altri modi per fare questo o qualcosa di simile. Tutto dipende da ciò che vuoi esattamente.

+0

ho usato la query che hai proposto. ora sono scioccato: la query viene eseguita più di un minuto. in ms sql ci vuole meno di un secondo O_O. – user1178399

+1

@ user1178399: È praticamente impossibile commentarlo senza conoscere i numerosi fattori in gioco. Vorrei ipotizzare che le prestazioni possano essere migliorate. –

1

Questo link sembra mostrare come farlo in Postgres 9.0+:

PostgreSQL: parameterizing a recursive CTE

E 'più in basso nella pagina nella sezione intitolata "Emulazione CROSS Applicare con funzioni di set-ritorno". Assicurati di annotare l'elenco delle limitazioni dopo l'esempio.

1

mi piace la risposta di Erwin Brandstetter però, ho scoperto un problema di prestazioni: durante l'esecuzione

SELECT *, (f_citizen_rec_modified(col1, col2)).* 
FROM v_citizenversions v 

La funzione f_citizen_rec_modified verrà corse 1 volta per ogni colonna restituisce (moltiplicato per ogni riga in v_citizenversions) . Non ho trovato la documentazione per questo effetto, ma sono riuscito a dedurlo tramite il debug. Ora la domanda diventa, come possiamo ottenere questo effetto (prima di 9.3 dove sono disponibili i join laterali) senza questo effetto collaterale sulle prestazioni?

Aggiornamento: Sembra che abbia trovato una risposta. Riscrivere la query come segue:

select x.col1, x.col2, x.col3, (x.func).* 
FROM (select SELECT v.col1, v.col2, v.col3, f_citizen_rec_modified(col1, col2) func 
FROM v_citizenversions v) x 

La differenza chiave è ottenere i risultati funzionali crudo prima (subquery interna) allora incarto che altrove selezionare quel busti tali risultati fuori nelle colonne. Questo è stato testato su PG 9.2

9

Necromancing:
Nuovo in PostgreSQL 9.3:

La parola chiave LATERALE

sinistra | giusto | INNER JOIN LATERALE

INNER JOIN LATERAL è lo stesso di CROSS APPLY
e LEFT JOIN LATERAL è l'uso stesso di OUTER APPLY

Esempio:

SELECT * FROM T_Contacts 

--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1 
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989 


LEFT JOIN LATERAL 
(
    SELECT 
     --MAP_CTCOU_UID  
     MAP_CTCOU_CT_UID 
     ,MAP_CTCOU_COU_UID 
     ,MAP_CTCOU_DateFrom 
     ,MAP_CTCOU_DateTo 
    FROM T_MAP_Contacts_Ref_OrganisationalUnit 
    WHERE MAP_CTCOU_SoftDeleteStatus = 1 
    AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID 

    /* 
    AND 
    ( 
     (__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo) 
     AND 
     (__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom) 
    ) 
    */ 
    ORDER BY MAP_CTCOU_DateFrom 
    LIMIT 1 
) AS FirstOE