sql - 从函数返回的记录具有连接的列

标签 sql postgresql join plpgsql set-returning-functions

我有一张表,用于存储帐户随时间的变化。我需要将它与另外两个表连接起来,以创建特定日期的一些记录(如果这些记录尚不存在的话)。

为了让事情变得更简单(我希望如此),我将返回正确历史数据的查询封装到一个接受帐户 ID 和日期的函数中。

如果我执行 "Select * account_servicetier_for_day(20424, '2014-08-12')",我会得到预期的结果(从单独列中的函数返回的所有数据)。如果我在另一个查询中使用该函数,我会将所有列合并为一个:

("2014-08-12 14:20:37",hollenbeck,691,12129,20424,69.95,"2Mb/1Mb 20GB Limit",2048,1024,20.000)

我正在使用“x86_64-slackware-linux-gnu 上的 PostgreSQL 9.2.4,由 gcc (GCC) 4.7.1,64 位编译”。

查询:

Select
    '2014-08-12' As day, 0 As inbytes, 0 As outbytes, acct.username, acct.accountid, acct.userid,
    account_servicetier_for_day(acct.accountid, '2014-08-12')
From account_tab acct
Where acct.isdsl = 1
    And acct.dslservicetypeid Is Not Null
    And acct.accountid Not In (Select accountid From dailyaccounting_tab Where Day = '2014-08-12')
Order By acct.username

功能:

CREATE OR REPLACE FUNCTION account_servicetier_for_day(_accountid integer, _day timestamp without time zone) RETURNS setof account_dsl_history_info AS
$BODY$
DECLARE _accountingrow record;
BEGIN
  Return Query
  Select * From account_dsl_history_info
  Where accountid = _accountid And timestamp <= _day + interval '1 day - 1 millisecond'
  Order By timestamp Desc 
  Limit 1;
END;
$BODY$ LANGUAGE plpgsql;

最佳答案

通常,分解从函数返回的行并获取单独的列:

<b>SELECT * FROM</b> account_servicetier_for_day(20424, '2014-08-12');


至于查询:

Postgres 9.3 或更新版本

使用 JOIN LATERAL 进行清理:

SELECT '2014-08-12' AS day, 0 AS inbytes, 0 AS outbytes
     , a.username, a.accountid, a.userid
     , f.*   -- but avoid duplicate column names!
FROM   account_tab a
     , account_servicetier_for_day(a.accountid, '2014-08-12') f  -- <-- HERE
WHERE  a.isdsl = 1
AND    a.dslservicetypeid IS NOT NULL
AND    NOT EXISTS (
   SELECT FROM dailyaccounting_tab
   WHERE  day = '2014-08-12'
   AND    accountid = a.accountid
   )
ORDER  BY a.username;

LATERAL 关键字在这里是隐含的,函数总是可以引用较早的 FROM 项目。 The manual:

LATERAL can also precede a function-call FROM item, but in this case it is a noise word, because the function expression can refer to earlier FROM items in any case.

相关:

FROM 列表中带逗号的短符号(大部分)等同于 CROSS JOIN LATERAL(与 [INNER] JOIN LATERAL ... ON TRUE) 并因此从函数调用不返回行的结果中删除行。要保留此类行,请使用 LEFT JOIN LATERAL ... ON TRUE:

...
FROM  account_tab a
LEFT  JOIN LATERAL account_servicetier_for_day(a.accountid, '2014-08-12') f ON TRUE
...

此外,如果可以避免,请不要使用 NOT IN (subquery)。这是执行此操作的几种方法中最慢和最棘手的方法:

我建议改为 NOT EXISTS

Postgres 9.2 或更早版本

您可以在 SELECT 列表中调用一个返回集合的函数(它是标准 SQL 的 Postgres 扩展)。出于性能原因,这最好在子查询中完成。分解外部查询中的(众所周知!)行类型以避免重复计算函数:

SELECT '2014-08-12' AS day, 0 AS inbytes, 0 AS outbytes
     , a.username, a.accountid, a.userid
     , (a.rec).*   -- but be wary of duplicate column names!
FROM  (
   SELECT *, account_servicetier_for_day(a.accountid, '2014-08-12') AS rec
   FROM   account_tab a
   WHERE  a.isdsl = 1
   AND    a.dslservicetypeid Is Not Null
   AND    NOT EXISTS (
       SELECT FROM dailyaccounting_tab
       WHERE  day = '2014-08-12'
       AND    accountid = a.accountid
      )
   ) a
ORDER  BY a.username;

Craig Ringer 的相关回答以及解释,为什么最好不要在同一查询级别上分解:

Postgres 10 移除了一些 SELECT 中集合返回函数行为的奇怪之处:<​​/p>

关于sql - 从函数返回的记录具有连接的列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25392371/

相关文章:

mysql - SQL 将连接结果连接到列中

sql - 获取每个员工缺勤记录的值

sql - last_value() 和分组依据

arrays - Postgres - 无法加入 UUID 上的 Json 数组值

sql - 我们可以在用于加入 Hive 查询的字段上使用 split 函数吗

Mysql 添加新列以供选择

c# - 错误消息 : "Only primitive types or enumeration types are supported in this context."

sql - 从 PostgreSQL 中的搜索中排除过时数据的最佳方法

python - 在数据库中存储标记化文本?

SQL:速度改进 - 在 cond1 或 cond2 上左连接