Record returned from function has columns concatenated

后端 未结 2 1955
失恋的感觉
失恋的感觉 2020-11-28 16:09

I have a table which stores account changes over time. I need to join that up with two other tables to create some records for a particular day, if those records don\'t alre

相关标签:
2条回答
  • 2020-11-28 16:46

    Generally, to decompose rows returned from a function and get individual columns:

    SELECT * FROM account_servicetier_for_day(20424, '2014-08-12')



    As for the query:

    Postgres 9.3+

    Cleaner with 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 1
       FROM   dailyaccounting_tab
       WHERE  day = '2014-08-12'
       AND    accountid = a.accountid
       )
    ORDER  BY a.username;
    

    The LATERAL keyword is implicit here, functions can always refer earlier FROM items. 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.

    Related:

    • Insert multiple rows in one table based on number in another table

    The short notations with a comma in the FROM list is (mostly) equivalent to a CROSS JOIN LATERAL (same as [INNER] JOIN LATERAL ... ON TRUE) and thus removes rows from the result where the function call returns no row. To retain such rows, use LEFT JOIN LATERAL ... ON TRUE:

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

    Also, don't use NOT IN (subquery) when you can avoid it. It's the slowest and most tricky of several ways to do that:

    • Select rows which are not present in other table

    I suggest NOT EXISTS instead.

    Postgres 9.2 or older

    You can call a set-returning function in the SELECT list (which is a Postgres extension of standard SQL). For performance reasons, this is best done in a subquery. Decompose the (well-known!) row type in the outer query to avoid repeated evaluation of the function:

    SELECT '2014-08-12' AS day, 0 AS inbytes, 0 AS outbytes
         , a.username, a.accountid, a.userid
         , (a.rec).*   -- but avoid 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 1
           FROM   dailyaccounting_tab
           WHERE  day = '2014-08-12'
           AND    accountid = a.accountid
          )
       ) a
    ORDER  BY a.username;
    

    Related answer by Craig Ringer with an explanation, why we better decompose in the outer query:

    • How to avoid multiple function evals with the (func()).* syntax in an SQL query?

    Postgres 10 finally re-implemented set-returning functions in the SELECT list to fix unexpected side effects.

    0 讨论(0)
  • 2020-11-28 16:55

    Use the function in the from clause

    Select
        '2014-08-12' As day,
        0 As inbytes,
        0 As outbytes,
        acct.username,
        acct.accountid,
        acct.userid,
        asfd.*
    From
        account_tab acct
        cross join lateral
        account_servicetier_for_day(acct.accountid, '2014-08-12') asfd
    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
    
    0 讨论(0)
提交回复
热议问题