2012-07-13 16 views
12

Necesito migrar las consultas SQL escritas para MS SQL Server 2005 a Postgres 9.1.
¿Cuál es la mejor manera de sustituir CROSS APPLY en esta consulta?Postgres análogo a CROSS APPLY en SQL Server

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

GetCitizenRecModified() función es una función de tabla valorado. No puedo colocar el código de esta función porque es realmente enorme, hace algunos cálculos difíciles y no puedo abandonarlo.

+0

No es necesario aplicar cruz en Postgres. Puede usar una función de tabla como una función. Simplemente únete a ellos. –

+0

@a_horse_with_no_name - 'CROSS APPLY' vuelve a ejecutar el TVF con parámetros correlacionados en lugar de ejecutarlo una vez y luego unir el resultado. –

Respuesta

8

En Postgres 9.3 o temprano utilizar un LATERAL Ingreso:

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; 

Por qué LEFT JOIN LATERAL ... ON true?


Para versiones anteriores, hay una manera muy simple de lograr lo creo que está intentando con una función de puesta a punto de regresar (RETURNS TABLE or RETURNS SETOF record OR RETURNS record):

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

La función calcula valores o nce para cada fila de la consulta externa. Si la función devuelve varias filas, las filas resultantes se multiplican en consecuencia. Todos los paréntesis se requieren sintácticamente para descomponer un tipo de fila. La función de tabla podría ser algo como esto:

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; 

Necesitas terminar con esto en una subconsulta o CTE si desea aplicar una cláusula WHERE debido a que las columnas no son visibles en el mismo nivel. (Y es mejor para el rendimiento de todos modos, porque prevenir la repetición de la evaluación para cada columna de salida de la función):

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; 

Hay varias otras maneras de hacer esto o algo similar. Todo depende de lo que quieras exactamente.

+0

utilicé la consulta que usted propuso. ahora estoy sorprendido: la consulta se ejecuta más de un minuto. en ms sql toma menos de un segundo O_O. – user1178399

+1

@ user1178399: Es prácticamente imposible comentar sobre eso sin conocer los muchos factores en juego. Especulo que el rendimiento puede mejorarse. –

1

Este enlace aparece para mostrar cómo hacerlo en Postgres 9.0+:

PostgreSQL: parameterizing a recursive CTE

¡Es final de la página en la sección titulada "Emulando CROSS APPLY con funciones de puesta a regresar". Asegúrese de anotar la lista de limitaciones después del ejemplo.

1

me gusta la respuesta de Erwin Brandstetter Sin embargo, he descubierto un problema de rendimiento: cuando se ejecuta

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

La función f_citizen_rec_modified se corrió 1 hora para cada columna que devuelve (multiplicado por cada fila de v_citizenversions) . No encontré documentación para este efecto, pero pude deducirlo mediante la depuración. Ahora la pregunta es: ¿cómo podemos obtener este efecto (antes de 9.3 donde las uniones laterales están disponibles) sin este efecto secundario de robo de rendimiento?

Actualización: Parece que encontré una respuesta. Reescribir la consulta de la siguiente manera:

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 diferencia clave es conseguir los resultados de la función prima primero (subconsulta interior) y luego envolver que en otro que seleccionar esos resultados bustos a cabo en las columnas. Esto fue probado en PG 9.2

9

Necromancing:
nuevo en PostgreSQL 9.3:

La palabra clave LATERAL

left | derecho | combinación interna LATERAL

INNER JOIN LATERAL es lo mismo que CROSS APPLY
y LEFT JOIN LATERAL es el mismo que el uso OUTER APPLY

Ejemplo:

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