25

我需要将为 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

GetCitizenRecModified()函数是表值函数。我不能放置这个函数的代码,因为它真的很大,计算起来很困难,我不能放弃它。

4

4 回答 4

34

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

SELECT v.col_a, v.col_b, f.*  -- no parentheses, 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;

为什么LEFT JOIN LATERAL ... ON true


对于旧版本,有一种非常简单的方法可以完成我认为您正在尝试使用 set-returning 函数(RETURNS TABLERETURNS SETOF recordORRETURNS record):

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

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

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

如果要应用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;

还有其他几种方法可以做到这一点或类似的事情。这完全取决于你想要什么。

于 2012-07-13T15:22:19.560 回答
22

死灵法:
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 
于 2016-03-08T16:49:48.170 回答
2

我喜欢 Erwin Brandstetter 的回答,但是,我发现了一个性能问题:运行时

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

f_citizen_rec_modified 函数将为它返回的每一列运行 1 次(乘以 v_citizenversions 中的每一行)。我没有找到这种效果的文档,但能够通过调试推断出来。现在问题变成了,我们如何才能获得这种效果(在 9.3 之前,横向连接可用)没有这种性能抢劫的副作用?

更新:我似乎找到了答案。重写查询如下:

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

关键区别是首先获取原始函数结果(内部子查询),然后将其包装在另一个选择中,将这些结果输出到列中。这是在 PG 9.2 上测试的

于 2013-11-21T16:55:48.393 回答
1

此链接似乎显示了如何在 Postgres 9.0+ 中执行此操作:

PostgreSQL:参数化递归 CTE

它位于标题为“使用集合返回函数模拟 CROSS APPLY”部分的页面下方。请务必注意示例后的限制列表。

于 2012-07-13T15:19:02.520 回答