Tabelfuncties
Ik voer zeer snelle, complexe databasemigraties uit voor de kost, waarbij ik SQL gebruik als zowel de client- als de servertaal (er wordt geen andere taal gebruikt), allemaal aan de serverzijde, waar de code zelden uit de database-engine komt. Tafelfuncties spelen een ENORME rol in mijn werk . Ik gebruik geen "cursors" omdat ze te traag zijn om aan mijn prestatie-eisen te voldoen, en alles wat ik doe is resultaatgericht. Tabelfuncties zijn een enorme hulp voor mij geweest bij het volledig elimineren van het gebruik van cursors, het bereiken van een zeer hoge snelheid, en hebben enorm bijgedragen aan het verminderen van het codevolume en het verbeteren van de eenvoud.
Kortom, u gebruikt een query die verwijst naar twee (of meer) tabelfuncties om de gegevens van de ene tabelfunctie naar de volgende door te geven. De selectiequeryresultatenset die de tabelfuncties aanroept, dient als kanaal om de gegevens van de ene tabelfunctie naar de volgende door te geven. Op het DB2-platform / de versie waar ik aan werk, en het lijkt erop dat op basis van een snelle blik op de 9.1 Postgres-handleiding hetzelfde geldt daar, je kunt maar een enkele rij kolomwaarden doorgeven als invoer voor een van de tabelfunctieaanroepen, zoals je hebt ontdekt. Omdat de tabelfunctie-aanroep echter plaatsvindt in het midden van de verwerking van de resultatenset van een query, bereikt u hetzelfde effect van het doorgeven van een hele resultaatset aan elke tabelfunctie-aanroep, hoewel in de database-engine-leidingen de gegevens worden doorgegeven slechts één rij tegelijk voor elke tabelfunctie.
Tabelfuncties accepteren één rij invoerkolommen en retourneren een enkele resultaatset terug in de aanroepende query (d.w.z. selecteren) die de functie heeft aangeroepen. De resultaatsetkolommen die door een tabelfunctie worden doorgegeven, worden onderdeel van de resultatenset van de aanroepende query en zijn daarom beschikbaar als invoer voor de volgende tabelfunctie , waarnaar later in dezelfde query wordt verwezen, meestal als een volgende join. De resultaatkolommen van de eerste tabelfunctie worden als invoer (één rij per keer) ingevoerd in de tweede tabelfunctie, die de kolommen van de resultaatset retourneert in de resultatenset van de aanroepende query. Zowel de eerste als de tweede kolom met de resultatenset van de tabelfunctie maken nu deel uit van de resultatenset van de aanroepende query en zijn nu beschikbaar als invoer (één rij per keer) voor een derde tabelfunctie. Elke tabelfunctieaanroep verbreedt de resultatenset van de aanroepende query via de kolommen die het retourneert. Dit kan zo doorgaan totdat u de limieten bereikt voor de breedte van een resultatenset, die waarschijnlijk van database-engine tot database verschilt.
Overweeg dit voorbeeld (dat mogelijk niet overeenkomt met de syntaxisvereisten of mogelijkheden van Postgres terwijl ik aan DB2 werk). Dit is een van de vele ontwerppatronen waarin ik tabelfuncties gebruik, het is een van de eenvoudigere, waarvan ik denk dat het zeer illustratief is en waarvan ik verwacht dat het een brede aantrekkingskracht zal hebben als tabelfuncties werden intensief gebruikt (voor zover ik weet niet, maar ik denk dat ze meer aandacht verdienen dan ze krijgen).
In dit voorbeeld zijn de gebruikte tabelfuncties:VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH en DATA_WAREHOUSE_TODAYS_ORDER_BATCH. In de DB2-versie waar ik aan werk, wikkel je de tabelfunctie in "TABLE( place table function call and parameters here )", maar op basis van een snelle blik op een Postgres-handleiding lijkt het erop dat je de "TABLE()"-wrapper weglaat.
create table TODAYS_ORDER_PROCESSING_EXCEPTIONS as (
select TODAYS_ORDER_BATCH.*
,VALIDATION_RESULT.ROW_VALID
,POST_RESULT.ROW_POSTED
,WAREHOUSE_RESULT.ROW_WAREHOUSED
from TODAYS_ORDER_BATCH
cross join VALIDATE_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
as VALIDATION_RESULT ( ROW_VALID ) --example: 1/0 true/false Boolean returned
left join POST_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
as POST_RESULT ( ROW_POSTED ) --example: 1/0 true/false Boolean returned
on ROW_VALIDATED = '1'
left join DATA_WAREHOUSE_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
as WAREHOUSE_RESULT ( ROW_WAREHOUSED ) --example: 1/0 true/false Boolean returned
on ROW_POSTED = '1'
where coalesce( ROW_VALID, '0' ) = '0' --Capture only exceptions and unprocessed work.
or coalesce( ROW_POSTED, '0' ) = '0' --Or, you can flip the logic to capture only successful rows.
or coalesce( ROW_WAREHOUSED, '0' ) = '0'
) with data
- Als tabel TODAYS_ORDER_BATCH 1.000.000 rijen bevat, wordt VALIDATE_TODAYS_ORDER_BATCH 1.000.000 keer aangeroepen, één keer voor elke rij.
- Als 900.000 rijen validatie doorstaan binnen VALIDATE_TODAYS_ORDER_BATCH, wordt POST_TODAYS_ORDER_BATCH 900.000 keer aangeroepen.
- Als er maar 850.000 rijen succesvol zijn gepost, moet VALIDATE_TODAYS_ORDER_BATCH een aantal mazen in de wet sluiten LOL en wordt DATA_WAREHOUSE_TODAYS_ORDER_BATCH 850.000 keer aangeroepen.
- Als 850.000 rijen het datawarehouse met succes hebben bereikt (d.w.z. er zijn geen extra uitzonderingen gegenereerd), dan wordt de tabel TODAYS_ORDER_PROCESSING_EXCEPTIONS gevuld met 1.000.000 - 850.000 =150.000 uitzonderingsrijen.
De aanroepen van de tabelfunctie in dit voorbeeld retourneren slechts één kolom, maar ze kunnen veel kolommen retourneren. De tabelfunctie die een orderrij valideert, kan bijvoorbeeld de reden retourneren waarom de validatie van een order is mislukt.
In dit ontwerp wordt vrijwel alle chatter tussen een HLL en de database geëlimineerd, aangezien de HLL-aanvrager de database vraagt om de hele batch in ÉÉN verzoek te verwerken. Dit resulteert in een reductie van miljoenen SQL-verzoeken aan de database, in een ENORME verwijdering van miljoenen HLL-procedure- of methodeaanroepen, en als resultaat een ENORME runtime-verbetering. Daarentegen zou legacy-code die vaak een enkele rij tegelijk verwerkt, doorgaans 1.000.000 ophaal-SQL-verzoeken verzenden, 1 voor elke rij in TODAYS_ORDER_BATCH, plus ten minste 1.000.000 HLL- en/of SQL-verzoeken voor validatiedoeleinden, plus ten minste 1.000.000 HLL en /of SQL-verzoeken voor boekingsdoeleinden, plus 1.000.000 HLL- en/of SQL-verzoeken voor het verzenden van de bestelling naar het datawarehouse. Toegegeven, met behulp van dit tabelfunctieontwerp worden binnen de tabelfuncties SQL-verzoeken naar de database verzonden, maar wanneer de database verzoeken aan zichzelf doet (d.w.z. vanuit een tabelfunctie), worden de SQL-verzoeken veel sneller afgehandeld (vooral in vergelijking met een legacy-scenario waarbij de HLL-aanvrager verwerking van één rij uitvoert vanaf een extern systeem, in het ergste geval via een WAN - OMG, doe dat alstublieft niet).
U kunt gemakkelijk prestatieproblemen tegenkomen als u een tabelfunctie gebruikt om "een resultatenset op te halen" en die resultatenset vervolgens aan andere tabellen toevoegt. In dat geval kan de SQL-optimizer niet voorspellen welke reeks rijen wordt geretourneerd door de tabelfunctie, en daarom kan het de join naar volgende tabellen niet optimaliseren. Om die reden gebruik ik ze zelden voor het ophalen van een resultatenset, tenzij ik weet dat die resultatenset een heel klein aantal rijen zal zijn, en dus geen prestatieprobleem veroorzaakt, of ik niet hoef mee te doen aan volgende tabellen.
Naar mijn mening is een van de redenen waarom tabelfuncties onderbenut zijn, dat ze vaak worden gezien als slechts een hulpmiddel om een resultatenset op te halen, die vaak slecht presteert, zodat ze worden afgeschreven als een "slecht" hulpmiddel om te gebruiken.
Tabelfuncties zijn enorm handig om meer functionaliteit naar de server te sturen, voor het elimineren van de meeste chatter tussen de databaseserver en programma's op externe systemen, en zelfs voor het elimineren van chatter tussen de databaseserver en externe programma's op dezelfde server. Zelfs chatten tussen programma's op dezelfde server brengt meer overhead met zich mee dan veel mensen beseffen, en veel ervan is onnodig. De kern van de kracht van tabelfuncties ligt in het gebruik ervan om acties uit te voeren binnen de verwerking van resultaatsets.
Er zijn meer geavanceerde ontwerppatronen voor het gebruik van tabelfuncties die voortbouwen op het bovenstaande patroon, waar u de verwerking van resultaatsets nog verder kunt maximaliseren, maar dit bericht is voor de meesten al veel om te absorberen.