sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur överför man en uppsättning rader från en funktion till en annan?

Tabellfunktioner

Jag utför mycket snabba, komplexa databasmigreringar för att leva, med SQL som både klient- och serverspråk (inget annat språk används), alla körs på serversidan, där koden sällan dyker upp från databasmotorn. Bordsfunktioner spelar en STOR roll i mitt arbete . Jag använder inte "markörer" eftersom de är för långsamma för att uppfylla mina prestandakrav, och allt jag gör är resultatinriktat. Tabellfunktioner har varit en enorm hjälp för mig att helt eliminera användningen av markörer, uppnå mycket hög hastighet och har bidragit dramatiskt till att minska kodvolymen och förbättra enkelheten.

Kort sagt, du använder en fråga som refererar till två (eller flera) tabellfunktioner för att överföra data från en tabellfunktion till nästa. Väljningsfrågeresultatuppsättningen som anropar tabellfunktionerna fungerar som kanalen för att överföra data från en tabellfunktion till nästa. På DB2-plattformen/versionen jag arbetar på, och det verkar baserat på en snabb titt i 9.1 Postgres-manualen att samma sak är sant där, du kan bara skicka en enda rad med kolumnvärden som indata till något av tabellfunktionsanropen, som du har upptäckt. Men eftersom tabellfunktionsanropet sker mitt i en frågas resultatuppsättningsbehandling, uppnår du samma effekt av att skicka en hel resultatuppsättning till varje tabellfunktionsanrop, även om data skickas i databasmotorns VVS. endast en rad åt gången till varje tabellfunktion.

Tabellfunktioner accepterar en rad med inmatningskolumner och returnerar en enda resultatuppsättning tillbaka till den anropande frågan (dvs. välj) som anropade funktionen. Resultatuppsättningens kolumner som skickas tillbaka från en tabellfunktion blir en del av den anropande frågans resultatuppsättning och är därför tillgängliga som indata till nästa tabellfunktion , refereras till senare i samma fråga, vanligtvis som en efterföljande koppling. Den första tabellfunktionens resultatkolumner matas som indata (en rad i taget) till den andra tabellfunktionen, som returnerar dess resultatuppsättningskolumner till den anropande frågans resultatuppsättning. Både den första och andra kolumnen för resultatuppsättningen för tabellfunktioner är nu en del av den anropande frågans resultatuppsättning och är nu tillgängliga som indata (en rad i taget) till en tredje tabellfunktion. Varje tabellfunktionsanrop breddar den anropande frågans resultatuppsättning via de kolumner som den returnerar. Detta kan fortsätta tills du börjar nå gränser för bredden på en resultatuppsättning, vilket sannolikt varierar från en databasmotor till nästa.

Tänk på det här exemplet (som kanske inte matchar Postgres syntaxkrav eller kapacitet när jag arbetar med DB2). Det här är ett av många designmönster där jag använder tabellfunktioner, är ett av de enklare, som jag tycker är väldigt illustrativt och ett som jag förväntar mig skulle ha bred tilltalande om bordsfunktioner användes flitigt (såvitt jag vet är de inte det, men jag tycker att de förtjänar mer uppmärksamhet än de får).

I det här exemplet är tabellfunktionerna som används:VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH och DATA_WAREHOUSE_TODAYS_ORDER_BATCH. På DB2-versionen jag jobbar med lindar du tabellfunktionen inuti "TABLE( placera tabellfunktionsanrop och parametrar här )", men baserat på en snabb titt i en Postgres-manual verkar det som att du utelämnar "TABELL( )"-omslaget.

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
  1. Om tabellen TODAYS_ORDER_BATCH innehåller 1 000 000 rader kommer VALIDATE_TODAYS_ORDER_BATCH att anropas 1 000 000 gånger, en gång för varje rad.
  2. Om 900 000 rader klarar valideringen inom VALIDATE_TODAYS_ORDER_BATCH kommer POST_TODAYS_ORDER_BATCH att anropas 900 000 gånger.
  3. Om bara 850 000 rader kan läggas upp, behöver VALIDATE_TODAYS_ORDER_BATCH några kryphål stängda LOL, och DATA_WAREHOUSE_TODAYS_ORDER_BATCH kommer att anropas 850 000 gånger.
  4. Om 850 000 rader lyckades komma in i Data Warehouse (dvs. inga ytterligare undantag genererades), kommer tabellen TODAYS_ORDER_PROCESSING_EXCEPTIONS att fyllas med 1 000 000 - 850 000 =150 000 undantagsrader.

Tabellfunktionsanropen i det här exemplet returnerar bara en enda kolumn, men de kan returnera många kolumner. Till exempel kan tabellfunktionen som validerar en orderrad returnera anledningen till att en order misslyckades med valideringen.

I denna design elimineras praktiskt taget allt snack mellan en HLL och databasen, eftersom HLL-begäraren ber databasen att behandla hela batchen i EN begäran. Detta resulterar i en minskning av miljontals SQL-förfrågningar till databasen, i en ENORM borttagning av miljontals HLL-procedurer eller metodanrop, och ger som ett resultat en ENORM förbättring av körtiden. Däremot skulle äldre kod, som ofta bearbetar en enstaka rad åt gången, vanligtvis skicka 1 000 000 hämta SQL-begäranden, 1 för varje rad i TODAYS_ORDER_BATCH, plus minst 1 000 000 HLL- och/eller SQL-begäranden för valideringsändamål, plus minst 1 000 HLL0,000 /eller SQL-begäranden för postningsändamål, plus 1 000 000 HLL- och/eller SQL-begäranden för att skicka beställningen till datalagret. Visst, med hjälp av denna tabellfunktionsdesign, inuti tabellfunktionerna skickas SQL-förfrågningar till databasen, men när databasen gör förfrågningar till sig själv (dvs inifrån en tabellfunktion), betjänas SQL-förfrågningarna mycket snabbare (särskilt i jämförelse med ett äldre scenario där HLL-begäraren bearbetar en rad från ett fjärrsystem, med det värsta fallet över ett WAN - OMG, gör inte det).

Du kan lätt stöta på prestandaproblem om du använder en tabellfunktion för att "hämta en resultatuppsättning" och sedan koppla den resultatuppsättningen till andra tabeller. I så fall kan SQL-optimeraren inte förutsäga vilken uppsättning rader som kommer att returneras från tabellfunktionen, och därför kan den inte optimera kopplingen till efterföljande tabeller. Av den anledningen använder jag dem sällan för att hämta en resultatuppsättning, såvida jag inte vet att resultatuppsättningen kommer att vara ett mycket litet antal rader, vilket därför inte orsakar prestandaproblem, eller så behöver jag inte gå med i efterföljande tabeller.

Enligt min mening är en anledning till att tabellfunktioner underutnyttjas att de ofta uppfattas som endast ett verktyg för att hämta en resultatuppsättning, som ofta presterar dåligt, så de avskrivs som ett "dåligt" verktyg att använda.

Tabellfunktioner är oerhört användbara för att överföra mer funktionalitet till servern, för att eliminera det mesta av chatter mellan databasservern och program på fjärrsystem, och till och med för att eliminera chatter mellan databasservern och externa program på samma server. Även chatt mellan program på samma server medför mer omkostnader än många inser, och mycket av det är onödigt. Hjärtat av kraften hos tabellfunktioner ligger i att använda dem för att utföra åtgärder i resultatuppsättningsbearbetningen.

Det finns mer avancerade designmönster för att använda tabellfunktioner som bygger på ovanstående mönster, där du kan maximera bearbetningen av resultatuppsättningen ytterligare, men det här inlägget är mycket för de flesta att ta till sig redan.




  1. FEL när tabell skickas som parameter i MySQL-lagringsprocedur

  2. Hur använder man en främmande nyckel i sqlite?

  3. Dubblettpost på INSERT efter DELETE från tabellen i transaktionen

  4. Används XA/JTA-transaktioner fortfarande?