I allmänhet för att dekomponera rader returneras från en funktion och få individuella kolumner:
SELECT * FROM account_servicetier_for_day(20424, '2014-08-12');
När det gäller frågan:
Postgres 9.3 eller senare
Rengöringsmedel med 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;
LATERAL
nyckelordet är implicit här, funktioner kan alltid referera till tidigare FROM
föremål. Manualen:
LATERAL
kan också föregå ett funktionsanropFROM
objekt, men i det här fallet är det ett brusord, eftersom funktionsuttrycket kan referera till tidigareFROM
föremål i alla fall.
Relaterat:
- Infoga flera rader i en tabell baserat på antalet i en annan tabell
Kort notation med kommatecken i FROM
listan motsvarar (för det mesta) en CROSS JOIN LATERAL
(samma som [INNER] JOIN LATERAL ... ON TRUE
) och tar därmed bort rader från resultatet där funktionsanropet inte returnerar någon rad. För att behålla sådana rader, använd LEFT JOIN LATERAL ... ON TRUE
:
...
FROM account_tab a
LEFT JOIN LATERAL account_servicetier_for_day(a.accountid, '2014-08-12') f ON TRUE
...
Använd inte heller NOT IN (subquery)
när du kan undvika det. Det är det långsammaste och mest knepiga av flera sätt att göra det:
- Välj rader som inte finns i andra tabeller
Jag föreslår att NOT EXISTS
istället.
Postgres 9.2 eller äldre
Du kan anropa en set-returfunktion i SELECT
list (som är en Postgres-förlängning av standard SQL). Av prestandaskäl görs detta bäst i en underfråga. Dekomponera den (välkända!) radtypen i den yttre frågan för att undvika upprepad utvärdering av funktionen:
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;
Relaterat svar av Craig Ringer med en förklaring, varför vi bättre bryts ner i den yttre frågan:
- Hur undviker man flera funktionsevaler med syntaxen (func()).* i en SQL-fråga?
Postgres 10 tog bort konstigheter i beteendet hos set-returnerande funktioner i SELECT
:
- Vad är det förväntade beteendet för flera set-returfunktioner i SELECT-satsen?