Функции на таблици
Извършвам много високоскоростни, сложни миграции на бази данни, за да си изкарвам прехраната, като използвам SQL както като клиентски, така и като сървърен език (не се използва друг език), всички работещи от страна на сървъра, където кодът рядко излиза от двигателя на базата данни. Функциите на таблицата играят ГОЛЯМА роля в моята работа . Не използвам „курсори“, тъй като те са твърде бавни, за да отговорят на изискванията ми за производителност и всичко, което правя, е ориентирано към набор от резултати. Функциите на таблиците ми помогнаха изключително много за пълното елиминиране на използването на курсори, постигането на много висока скорост и допринесоха драматично за намаляване на обема на кода и подобряване на опростеността.
Накратко, вие използвате заявка който препраща към две (или повече) таблични функции, за да прехвърли данните от една таблична функция към следващата. Наборът с резултати от заявката за избор, който извиква табличните функции, служи като канал за предаване на данните от една таблична функция към следващата. На платформата/версията на DB2, върху която работя, и изглежда въз основа на бърз преглед на ръководството на Postgres 9.1, че същото е вярно и там, можете да подадете само един ред от стойности на колони като вход към някое от извикванията на функцията на таблицата, както си открил. Въпреки това, тъй като извикването на табличната функция се случва по средата на обработката на набор от резултати на заявка, вие постигате същия ефект от предаване на цял набор от резултати към всяко извикване на таблична функция, въпреки че в системата на базата данни данните се предават само един ред наведнъж за всяка функция на таблица.
Табличните функции приемат един ред входни колони и връщат единичен набор от резултати обратно в извикващата заявка (т.е. изберете), която е извикала функцията. Колоните с набор от резултати, предадени обратно от функция на таблица, стават част от набора с резултати на извикващата заявка и следователно са достъпни като вход за следващата функция на таблица , споменат по-късно в същата заявка, обикновено като последващо присъединяване. Резултатните колони на първата таблична функция се подават като вход (един ред наведнъж) към втората таблична функция, която връща своите колони с резултати в набора с резултати на извикващата заявка. Както първата, така и втората колона за набор от резултати на таблична функция вече са част от набора с резултати на извикващата заявка и вече са достъпни като вход (ред по ред) към трета функция на таблица. Всяко извикване на таблична функция разширява набора от резултати на извикващата заявка чрез колоните, които връща. Това може да продължи, докато не започнете да достигате ограничения за ширината на набор от резултати, което вероятно варира от една база данни до друга.
Помислете за този пример (който може да не отговаря на синтаксисните изисквания или възможности на Postgres, тъй като работя върху DB2). Това е един от многото дизайнерски модели, в които използвам таблични функции, е един от по-простите, който мисля, че е много илюстративен, и такъв, който очаквам да има широка привлекателност ако табличните функции бяха широко използвани (доколкото ми е известно, не са, но мисля, че заслужават повече внимание, отколкото получават).
В този пример използваните таблични функции са:VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH и DATA_WAREHOUSE_TODAYS_ORDER_BATCH. Във версията на DB2, върху която работя, вие обгръщате табличната функция вътре в „TABLE( поставете извикването на табличната функция и параметрите тук )“, но въз основа на бърз преглед на ръководството на Postgres изглежда, че сте пропуснали обвивката „TABLE( )“.
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
- Ако таблицата TODAYS_ORDER_BATCH съдържа 1 000 000 реда, тогава VALIDATE_TODAYS_ORDER_BATCH ще бъде извикана 1 000 000 пъти, веднъж за всеки ред.
- Ако 900 000 реда преминат проверка във VALIDATE_TODAYS_ORDER_BATCH, тогава POST_TODAYS_ORDER_BATCH ще бъде извикан 900 000 пъти.
- Ако само 850 000 реда се публикуват успешно, тогава VALIDATE_TODAYS_ORDER_BATCH се нуждае от затваряне на някои вратички LOL и DATA_WAREHOUSE_TODAYS_ORDER_BATCH ще бъде извикан 850 000 пъти.
- Ако 850 000 реда са попаднали успешно в Data Warehouse (т.е. не са генерирани допълнителни изключения), тогава таблицата TODAYS_ORDER_PROCESSING_EXCEPTIONS ще бъде попълнена с 1 000 000 - 850 000 =150 000 реда за изключения.
Извикванията на таблични функции в този пример връщат само една колона, но може да връщат много колони. Например табличната функция, валидираща ред за поръчка, може да върне причината, поради която поръчката не е била валидирана.
В този дизайн на практика цялото бърборене между HLL и базата данни е елиминирано, тъй като HLL рикуестърът иска от базата данни да обработи цялата партида в ЕДНА заявка. Това води до намаляване на милиони SQL заявки към базата данни, до ГОЛЯМО премахване на милиони извиквания на процедури или методи на HLL и като резултат осигурява ГОЛЯМО подобрение на времето за изпълнение. За разлика от това наследеният код, който често обработва един ред наведнъж, обикновено изпраща 1 000 000 SQL заявки за извличане, по 1 за всеки ред в TODAYS_ORDER_BATCH, плюс поне 1 000 000 HLL и/или SQL заявки за целите на валидирането, плюс поне 1 000 000 HLL и /или SQL заявки за целите на публикуване, плюс 1 000 000 HLL и/или SQL заявки за изпращане на поръчката до хранилището на данни. Разбира се, използвайки този дизайн на таблична функция, вътре в табличните функции SQL заявките се изпращат към базата данни, но когато базата данни прави заявки към себе си (т.е. от вътрешността на таблична функция), SQL заявките се обслужват много по-бързо (особено в сравнение с наследен сценарий, при който HLL рикуестърът извършва обработка на един ред от отдалечена система, с най-лошия случай през WAN - OMG, моля, не правете това).
Можете лесно да се сблъскате с проблеми с производителността, ако използвате таблична функция, за да „извлечете набор от резултати“ и след това да присъедините този набор от резултати към други таблици. В този случай SQL оптимизаторът не може да предвиди какъв набор от редове ще бъде върнат от табличната функция и следователно не може да оптимизира присъединяването към следващите таблици. Поради тази причина рядко ги използвам за извличане на набор от резултати, освен ако не знам, че наборът от резултати ще бъде много малък брой редове, следователно няма да причини проблем с производителността, или няма нужда да се присъединявам към следващи таблици.
Според мен една от причините, поради които табличните функции се използват недостатъчно, е, че те често се възприемат само като инструмент за извличане на набор от резултати, който често се представя зле, така че биват отписвани като „лош“ инструмент за използване.
Функциите на таблиците са изключително полезни за прехвърляне на повече функционалност към сървъра, за елиминиране на по-голямата част от бърборенето между сървъра на базата данни и програмите на отдалечени системи и дори за елиминиране на бърборенето между сървъра на базата данни и външните програми на същия сървър. Дори бърборенето между програми на един и същи сървър носи повече разходи, отколкото много хора осъзнават, и голяма част от него е ненужно. Сърцето на силата на табличните функции се крие в използването им за извършване на действия в обработката на набор от резултати.
Има по-усъвършенствани шаблони за проектиране за използване на таблични функции, които се основават на горния шаблон, където можете да увеличите още повече обработката на набор от резултати, но тази публикация вече е много за усвояване от повечето.