sql >> Database >  >> RDS >> PostgreSQL

Functie-geretourneerde record splitsen in meerdere kolommen

In Postgres 9.3 of later kan dit het beste worden opgelost met een LATERAL doe mee:

SELECT *
FROM   actors a 
JOIN   movies_actors ma on a.actor_id = ma.movie_id 
LEFT   JOIN LATERAL hi_lo(a.actor_id, length(a.name), ma.movie_id) x ON true
LIMIT  10;

Voorkomt herhaalde evaluatie van de functie (voor elke kolom in de uitvoer - de functie moet hoe dan ook voor elke invoerrij worden aangeroepen).
LEFT JOIN LATERAL ... ON true om te voorkomen dat rijen aan de linkerkant worden weggelaten als de functie geen rij retourneert:

  • Wat is het verschil tussen LATERAL en een subquery in PostgreSQL?

Vervolg in je opmerking:

alleen de uitgebreide kolommen geproduceerd door de functieaanroep

SELECT x.*  -- that's all!
FROM   actors a 
JOIN   movies_actors ma on a.actor_id = ma.movie_id 
LEFT   JOIN LATERAL hi_lo(a.actor_id, length(a.name), ma.movie_id) x ON true
LIMIT  10;

Maar aangezien u niet om andere kolommen geeft, kunt u het vereenvoudigen tot:

SELECT x.*
FROM   actors a 
JOIN   movies_actors ma on a.actor_id = ma.movie_id 
     , hi_lo(a.actor_id, length(a.name), ma.movie_id) x
LIMIT  10;

Dat is een impliciete CROSS JOIN LATERAL . Als de functie af en toe "geen rij" kan retourneren, kan het resultaat anders zijn:we krijgen geen NULL-waarden voor de rijen, die rijen worden gewoon geëlimineerd - en LIMIT telt ze niet meer.

In oudere versies (of in het algemeen) je kunt het samengestelde type ook gewoon ontleden met de juiste syntaxis:

SELECT *, (hi_lo(a.actor_id, length(a.name), ma.movie_id)).*  -- note extra parentheses!
FROM   actors a 
JOIN   movies_actors ma on a.actor_id = ma.movie_id 
LIMIT  10;

Het nadeel is dat de functie één keer wordt geëvalueerd voor elke kolom in de functie-uitvoer vanwege een zwakte in de Postgres-queryplanner. Het is beter om de aanroep naar een subquery of CTE te verplaatsen en het rijtype te ontleden in de buitenste SELECT . Vind ik leuk:

SELECT actor_id, movie_id, (x).*  -- explicit column names for the rest
FROM  (
   SELECT *, hi_lo(a.actor_id, length(a.name), ma.movie_id) AS x
   FROM   actors a 
   JOIN   movies_actors ma on a.actor_id = ma.movie_id 
   LIMIT  10
   ) sub;

Maar je moet individuele kolommen een naam geven en je kunt niet wegkomen met SELECT * tenzij je in orde bent met het rijtype in het resultaat, redundant. Gerelateerd:

  • Vermijd meerdere aanroepen van dezelfde functie bij het uitbreiden van het samengestelde resultaat
  • Hoe vermijd je het evalueren van meerdere functies met de (func()).* syntaxis in een SQL-query?



  1. Hoe voeg ik een bestand in Oracle Database in?

  2. Uitvoeringsvolgorde van voorwaarden in SQL 'where'-clausule

  3. Vul ontbrekende datums in voor uitvoer van SQL Server-query's met behulp van CTE

  4. Hoe geef je een argument door aan een PL/SQL-blok in een sql-bestand met de naam START in sqlplus?