Postgres类似于SQL Server中的CROSS APPLY

use*_*399 16 sql postgresql cross-apply postgresql-9.1 lateral

我需要将为MS SQL Server 2005编写的SQL查询迁移到Postgres 9.1.在此查询中
替换的最佳方法是什么CROSS APPLY

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

GetCitizenRecModified()function是一个表值函数.我不能放置这个函数的代码,因为它真的很大,它会使一些困难的计算,我不能放弃它.

Erw*_*ter 18

在Postgres 9.3或更高版本中使用LATERAL连接:

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;
Run Code Online (Sandbox Code Playgroud)

为什么LEFT JOIN LATERAL ... ON true


对于旧版本,有一种非常简单的方法来实现我认为您尝试使用set-returns函数(RETURNS TABLERETURNS SETOF recordORRETURNS record):

SELECT *, (f_citizen_rec_modified(col1, col2)).*
FROM   v_citizenversions v
Run Code Online (Sandbox Code Playgroud)

该函数为外部查询的每一行计算一次值.如果函数返回多行,则相应地乘以结果行.语法上需要所有括号来分解行类型.表函数看起来像这样:

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;
Run Code Online (Sandbox Code Playgroud)

如果要应用WHERE子句,则需要将其包装在子查询或CTE中,因为列在同一级别上不可见.(无论如何,它对性能更好,因为您可以防止对函数的每个输出列重复进行评估):

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;
Run Code Online (Sandbox Code Playgroud)

还有其他几种方法可以做到这一点或类似的东西.这一切都取决于你想要什么.


Ste*_*ger 14

Necromancing:
PostgreSQL 9.3中的新功能:

LATERAL关键字

离开| 对| 内在的JOIN LATERAL

INNER JOIN LATERAL与...相同CROSS APPLY
并且LEFT JOIN LATERAL相同OUTER APPLY

用法示例:

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 
Run Code Online (Sandbox Code Playgroud)