我需要将为 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()
函数是一个表值函数。我无法放置这个函数的代码,因为它真的很大,它会进行一些困难的计算,我不能放弃它。
在 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
?
对于旧版本,有一种非常简单的方法可以完成我认为你正在尝试使用设置返回函数(
RETURNS TABLE
或RETURNS SETOF record
或RETURNS 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;
还有其他几种方法可以做到这一点或类似的事情。这完全取决于您到底想要什么。
PostgreSQL 9.3 中的新功能:
左|对|内部连接
横向
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 ON true
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 上测试的