Временните таблици са полезна концепция, присъстваща в повечето SGBD, въпреки че често работят по различен начин.
Този блог описва техническите характеристики за този вид таблици или в PostgreSQL (версия 11) или Oracle (версия 12c) с някои конкретни примери. Въпреки че целта на тези таблици може да е една и съща за всички SGBD, техните специфики или начинът на изпълнение и манипулиране са напълно различни.
Тази функция може да се използва както от разработчици, така и от администратори на бази данни за съхраняване на междинни резултати, които ще са необходими за по-нататъшна обработка, за да се осигурят добри показатели за производителност.
Временни таблици в PostgreSQL
В PostgreSQL тези обекти са валидни само за текущата сесия:те се създават, използват и пускат в една и съща сесия:структурата на таблицата и управляваните данни са видими само за текущата сесия, така че другите сесии нямат достъп до временните таблици, създадени в другите сесии.
По-долу е показан прост пример за създаване на временна таблица:
CREATE TEMPORARY TABLE tt_customer
(
customer_id INTEGER
)
ON COMMIT DELETE ROWS;
Временните таблици се създават във временна схема:pg_temp_nn и е възможно да се създават индекси на тези таблици:
creation index tt_cusomer_idx_1 on tt_customer(customer_id)
Тъй като редовете с данни в тези таблици също могат да бъдат изтрити, е възможно да освободите заетото хранилище чрез изпълнение на vaccum команда:
VACUUM VERBOSE tt_customer
Анализът командата може да се изпълни и във временните таблици, за да се съберат статистически данни:
ANALYZE VERBOSE tt_customer;
И двете команди могат да бъдат изпълнени за този вид таблица като SQL команда, но autovaccum демонът, който ги изпълнява, не действа върху временните таблици.
Друг важен момент, който трябва да вземете предвид, е свързан с постоянните и временните таблици със същото име:след като това се случи, само постоянната таблица се взема предвид, когато се извиква със своята схема като префикс.
web_db=# BEGIN TRANSACTION;
BEGIN
web_db=# SELECT COUNT(*) FROM customers;
count
---------
1030056
(1 row)
web_db=# CREATE TEMPORARY TABLE customers(
web_db(# id INTEGER
web_db(# )
web_db-# ON COMMIT PRESERVE ROWS;
CREATE TABLE
web_db=# INSERT INTO customers(id) VALUES(1023);
INSERT 0 1
web_db=# SELECT COUNT(*) FROM customers;
count
-------
1
(1 row)
web_db=# \dt *customers*
List of relations
Schema | Name | Type | Owner
-----------+----------------------+-------+----------
pg_temp_5 | customers | table | postgres
web_app | customers | table | postgres
web_app | customers_historical | table | postgres
(3 rows)
web_db=# DROP TABLE customers;
DROP TABLE
web_db=# \dt *customers*
List of relations
Schema | Name | Type | Owner
---------+----------------------+-------+----------
web_app | customers | table | postgres
web_app | customers_historical | table | postgres
(2 rows)
web_db=# SELECT COUNT(*) FROM web_app.customers;
count
---------
1030056
(1 row)
web_db=# SELECT COUNT(*) FROM customers;
count
---------
1030056
(1 row)
От предишния пример, докато временната таблица съществува, всички препратки към клиентите препраща към тази таблица вместо към постоянната.
Съвети за програмисти за временни таблици
Целта на този пример е да присвои бонус за клиентите, които не са правили покупки или влизат повече от година, така че скриптът на разработчика вместо това да използва подзаявки в заявки като възможно решение (или използването на CTEs изявление) може да използва временни таблици (че обикновено е по-бързо от използването на подзаявки):
web_db=# BEGIN TRANSACTION;
BEGIN
web_db=# CREATE TEMPORARY TABLE tt_customers(
web_db(# id INTEGER
web_db(# )
web_db-# ON COMMIT DELETE ROWS;
CREATE TABLE
web_db=# SELECT COUNT(*) FROM tt_customers;
count
-------
0
(1 row)
web_db=# INSERT INTO tt_customers(id)
web_db-# SELECT customer_id
web_db-# FROM web_app.orders
web_db-# WHERE order_dt <= NOW()-INTERVAL '6 MONTH';
INSERT 0 1030056
web_db=# SELECT COUNT(*) FROM tt_customers;
count
---------
1030056
(1 row)
web_db=# DELETE FROM tt_customers c
web_db-# WHERE EXISTS(SELECT 1
web_db(# FROM web_app.users u JOIN web_app.login l
web_db(# ON (l.user_id=u.user_id)
web_db(# WHERE u.customer_id=c.id
web_db(# AND l.login_dt > NOW()-INTERVAL '6 MONTH'
web_db(# );
DELETE 194637
web_db=# SELECT COUNT(*) FROM tt_customers;
count
--------
835419
(1 row)
web_db=# UPDATE web_app.customers as c SET BONUS=5
web_db-# FROM tt_customers t
web_db-# WHERE t.id = c.id;
UPDATE 835419
web_db=# SELECT COUNT(*) FROM tt_customers;
count
--------
835419
(1 row)
web_db=# COMMIT TRANSACTION;
COMMIT
web_db=# SELECT COUNT(*) FROM tt_customers;
count
-------
0
(1 row)
DBA съвети за временни таблици
Типична задача за администраторите на бази данни е да изчистят всички огромни таблици, които съдържат данни, които вече не са необходими. Това трябва да бъде завършено много бързо и се случва често. Стандартният подход е тези данни да се преместят в историческа таблица в друга схема или в база данни, която се осъществява по-рядко.
Така че, за да извършите това преместване, поради проблеми с производителността, най-доброто решение може да бъде използването на временни таблици:
CREATE TEMPORARY TABLE tt_customer
(
customer_id INTEGER
)
ON COMMIT DROP;
В този пример временната таблица е създадена с опцията DROP, така че това означава, че ще бъде премахната в края на текущия блок за транзакция.
Ето още малко важна информация за временните таблици на PostgreSQL:
- Временните таблици се отпадат автоматично в края на сесия или, както е представено в предишния пример, в края на текущата транзакция
- Постоянните таблици със същото име не се виждат от текущата сесия, докато временната таблица съществува, освен ако не са посочени с имена, квалифицирани по схема
- Всички индекси, създадени във временна таблица, също са автоматично временни
- ПРИ COMMIT запазват редовете това е поведението по подразбиране
- По избор GLOBAL или LOCAL могат да бъдат написани преди TEMPORARY или TEMP. Това в момента не прави разлика в PostgreSQL и е отхвърлено
- Автовакуум демонът няма достъп до тези таблици и следователно не може да вакуумира или анализира временни таблици, но, както беше показано по-горе, командите за автоматично вакуумиране и анализ могат да се използват като SQL команди.
Глобални временни таблици (GTT) в Oracle
Този вид таблици са известни в света на Oracle като глобална временна таблица (или GTT). Тези обекти са постоянни в базата данни и могат да бъдат обобщени със следните характеристики:
- Структурата е статична и видима за всички потребители, но съдържанието й е видимо само за текущата сесия
- Може да се създаде в конкретна схема (по подразбиране ще бъде собственост на потребителя, който издава командата) и те са вградени в пространството за таблици TEMP.
- Веднъж създадена в базата данни, тя не може да бъде създадена отново във всяка сесия, но данните, управлявани от сесия, не се виждат за другите сесии
- Възможно е създаването на индекси и генериране на статистика
- Тъй като структурата на тези таблици също е дефинирана в базата данни, не е възможно да се присвои нейното име на постоянна таблица (в Oracle два обекта не могат да имат едно и също име дори от различни типове)
- Не генерирайте твърде много регистрационни файлове за повторно изпълнение и допълнителните разходи за отмяна също са по-малко в сравнение с постоянна таблица (само поради тези причини използването на GTT е по-бързо) за всички версии преди 12c. От версия 12c има концепция за временно отмяна, позволяваща отмяната на GTT да бъде записана във временното пространство за таблици, като по този начин намалява отмяната и повторението.
Следвайки същия пример, представен в PostgreSQL, създаването на GTT е доста подобно:
CREATE GLOBAL TEMPORARY TABLE tt_customer
(
customer_id NUMBER
)
ON COMMIT DELETE ROWS;
Възможно е и създаване на индекси.
creation index tt_cusomer_idx_1 on tt_customer(customer_id)
Преди Oracle 12c генерирането на статистически данни за глобални временни таблици имаше поведение по глобален начин:статистическите данни, генерирани в конкретна сесия за конкретна GTT, бяха видими и използвани за другите сесии (само статистика, но не данните!), от версия 12c е възможно всяка сесия да генерира своя собствена статистика.
Преди всичко е необходимо да зададете предпочитанието global_temp_table_stats до сесия :
exec dbms_stats.set_table_prefs(USER,’TT_CUSTOMER’,’GLOBAL_TEMP_TABLE_STATS’,’SESSION’);
и след това генериране на статистически данни:
exec dbms_stats.gather_table_stats(USER,’TT_CUSTOMER’);
Съществуващата глобална временна таблица може да бъде проверена чрез изпълнение на следната заявка:
select table_name from all_tables where temporary = 'Y';
Съвети за програмисти за глобални временни таблици (GTT)
Следвайки примера в секцията PostgreSQL:за присвояване на бонус за клиенти, които не са правили покупки или влизат повече от година, използването на глобални временни таблици в Oracle има същата цел като в PostgreSQL:постигане на по-добра производителност или в използване на ресурс или скорост на изпълнение.
SQL> SELECT COUNT(*) FROM tt_customers;
COUNT(*)
----------
0
SQL>
SQL> INSERT INTO tt_customers(id)
2 SELECT customer_id
3 FROM orders
4 WHERE order_dt <= ADD_MONTHS(SYSDATE,-6);
1030056 rows created.
SQL>
SQL> SELECT COUNT(*) FROM tt_customers;
COUNT(*)
----------
1030056
SQL>
SQL> DELETE FROM tt_customers c
2 WHERE EXISTS(SELECT 1
3 FROM users u JOIN login l
4 ON (l.user_id=u.user_id)
5 WHERE u.customer_id=c.id
6 AND l.login_dt > ADD_MONTHS(SYSDATE,-6)
7 );
194637 rows deleted.
SQL>
SQL> SELECT COUNT(*) FROM tt_customers;
COUNT(*)
----------
835419
SQL>
SQL> UPDATE CUSTOMERS c SET BONUS=5
2 WHERE EXISTS(SELECT 1 FROM tt_customers tc WHERE tc.id=c.id);
835419 rows updated.
SQL>
SQL> SELECT COUNT(*) FROM tt_customers;
COUNT(*)
----------
835419
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> SELECT COUNT(*) FROM tt_customers;
COUNT(*)
----------
0
SQL>
По подразбиране в Oracle SQL/PLSQL блок/изявление стартира имплицитно транзакция.
DBA съвети за глобални временни таблици (GTT)
Като изявление drop не съществува за глобални временни таблици, командата за създаване на таблицата е същата като предишната:
CREATE GLOBAL TEMPORARY TABLE tt_customer
(
customer_id NUMBER
)
ON COMMIT DELETE ROWS;
Еквивалентният фрагмент от код в Oracle за прочистване на клиента таблица е следната:
SQL> INSERT INTO tt_customers(id)
2 SELECT l.user_id
3 FROM users u JOIN login l
4 ON (l.user_id=u.user_id)
5 WHERE l.login_dt < ADD_MONTHS(SYSDATE,-12);
194637 rows created.
SQL>
SQL> INSERT INTO tt_customers(id)
2 SELECT user_id
3 FROM web_deactive;
2143 rows created.
SQL>
SQL> INSERT INTO tt_customers(id)
2 SELECT user_id
3 FROM web_black_list;
4234 rows created.
SQL>
SQL> INSERT INTO customers_historical(id,name)
2 SELECT c.id,c.name
3 FROM customers c,
4 tt_customers tc
5 WHERE tc.id = c.id;
201014 rows created.
SQL>
SQL> DELETE FROM customers c
2 WHERE EXISTS (SELECT 1 FROM tt_customers tc WHERE tc.id = c.id );
201014 rows deleted.
Библиотеката pg_global_temp_tables
Както бе споменато по-горе, временните таблици в PostgreSQL не могат да бъдат извикани с помощта на нотацията schema.table , така че библиотеката pg_global_temp_tables (има някои подобни библиотеки, налични в github) е много полезно заобиколно решение, което да се използва при миграции на база данни от Oracle към PostgreSQL.
За да запазите нотацията на Oracle schema.temporary_table в заявки или съхранени процедури:
SELECT c.id,c.nam
FROM web_app.tt_customers tc,
Web_app.customers c
WHERE c.id = tc.id
Позволява да останат временните таблици над кода с нотация на схемата.
По принцип той се състои в изглед:web_app.tt_customers създадено под схемата, на която би трябвало да има временната таблица и този изглед ще поиска временната таблица tt_customers чрез функция, наречена web_app.select_tt_customers :
CREATE OR REPLACE VIEW WEB_APP.TT_CUSTOMERS AS
SELECT * FROM WEB_APP.SELECT_TT_CUSTOMERS();
Тази функция връща съдържанието на временна таблица:
CREATE OR REPLACE FUNCTION WEB_APP.SELECT_TT_CUSTOMERS() RETURNS TABLE(ID INR, NAME VARCHAR) AS $$
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS TT_CUSTOMERS(ID INT, NAME) ON COMMIT DROP;
RETURN QUERY SELECT * FROM TT_CUSTOMERS;
END;
$$ LANGUAGE PLPGSQL;
Резюме
Временните таблици се използват основно за съхраняване на междинни резултати и по този начин избягване на сложни и тежки изчисления,
След това са изброени някои характеристики на временните таблици в PostgreSQL или Oracle:
- Може да се използва при преглед
- Може да използва командата TRUNCATE
- Не може да бъде разделен на дялове
- Ограничението за външния ключ върху временните таблици не е позволено
- Този вид таблици са алтернатива за CTEs (Common Table Expressions), известни също на професионалистите на Oracle като клауза WITH
- По отношение на сигурността и поверителността тези таблици са ценен актив, тъй като данните се виждат само за текуща сесия
- Временните таблици се изтриват автоматично (в PostgreSQL) или се изтриват (в Oracle), след като сесията/транзакцията приключи.
За временните таблици в PostgreSQL е препоръчително да не използвате същото име на постоянна таблица във временна таблица. От страна на Oracle е добра практика генерирането на статистически данни за сесиите, които включват значителен обем данни в GTT, за да принуди Cost-Based Optimizer (CBO) да избере най-добрия план за заявките, които използват този вид таблици .