Tabele tymczasowe są użyteczną koncepcją obecną w większości SGBD, mimo że często działają inaczej.
Ten blog opisuje techniczne funkcje tego rodzaju tabel w bazach danych PostgreSQL (wersja 11) lub Oracle (wersja 12c) wraz z kilkoma konkretnymi przykładami. Chociaż cel tych tabel może być taki sam dla wszystkich SGBD, ich specyfika lub sposób implementacji i manipulacji są zupełnie inne.
Ta funkcja może być używana zarówno przez programistów, jak i administratorów baz danych do przechowywania wyników pośrednich, które będą potrzebne do dalszego przetwarzania w celu zapewnienia dobrych wskaźników wydajności.
Tymczasowe tabele w PostgreSQL
W PostgreSQL te obiekty są ważne tylko dla bieżącej sesji:są tworzone, używane i usuwane podczas tej samej sesji:struktura tabeli i zarządzane dane są widoczne tylko dla bieżącej sesji, dlatego inne sesje nie mają dostępu do tabele tymczasowe utworzone w innych sesjach.
Poniżej pokazano prosty przykład tworzenia tabeli tymczasowej:
CREATE TEMPORARY TABLE tt_customer
(
customer_id INTEGER
)
ON COMMIT DELETE ROWS;
Tabele tymczasowe są tworzone w tymczasowym schemacie:pg_temp_nn i możliwe jest tworzenie indeksów na tych tabelach:
creation index tt_cusomer_idx_1 on tt_customer(customer_id)
Ponieważ wiersze danych w tych tabelach również mogą zostać usunięte, możliwe jest zwolnienie zajętego miejsca poprzez wykonanie próżnia polecenie:
VACUUM VERBOSE tt_customer
analiza polecenie może być również wykonane na tabelach tymczasowych w celu zebrania statystyk:
ANALYZE VERBOSE tt_customer;
Oba polecenia można wykonać dla tego rodzaju tabeli jako polecenie SQL, jednak autovaccum demon, który je wykonuje, nie działa na tabelach tymczasowych.
Kolejna ważna kwestia, którą należy wziąć pod uwagę, jest związana z tabelami stałymi i tymczasowymi o tej samej nazwie:gdy to się stanie, tylko tabela stała jest brana pod uwagę przy wywołaniu z jej schematem jako przedrostkiem.
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)
Z poprzedniego przykładu, gdy istnieje tabela tymczasowa, wszystkie odniesienia do klientów odnosi się do tej tabeli zamiast do stałej.
Wskazówki dla programistów dotyczące tabel tymczasowych
Celem tego przykładu jest przypisanie premii dla klientów, którzy nie dokonywali zakupów lub nie logowali się od ponad roku, aby zamiast tego skrypt dewelopera wykorzystywał podzapytania w zapytaniach jako możliwe rozwiązanie (lub wykorzystanie CTE instrukcja) może używać tabel tymczasowych (co zwykle jest szybsze niż używanie podzapytań):
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)
Wskazówki DBA dotyczące tabel tymczasowych
Typowym zadaniem administratorów baz danych jest czyszczenie ogromnych tabel zawierających dane, które nie są już potrzebne. Trzeba to zrobić bardzo szybko i zdarza się to często. Standardowym podejściem jest przeniesienie tych danych do tabeli historycznej w innym schemacie lub do bazy danych, do której dostęp jest rzadszy.
Tak więc, aby wykonać to przenoszenie, ze względu na problemy z wydajnością najlepszym rozwiązaniem może być użycie tabel tymczasowych:
CREATE TEMPORARY TABLE tt_customer
(
customer_id INTEGER
)
ON COMMIT DROP;
W tym przykładzie tabela tymczasowa została utworzona z opcją DROP, co oznacza, że zostanie usunięta na końcu bieżącego bloku transakcji.
Oto kilka innych ważnych informacji na temat tabel tymczasowych PostgreSQL:
- Tabele tymczasowe są automatycznie usuwane na koniec sesji lub, jak przedstawiono w poprzednim przykładzie, na koniec bieżącej transakcji
- Stałe tabele o tej samej nazwie nie są widoczne dla bieżącej sesji, gdy istnieje tabela tymczasowa, chyba że odwołują się do nich nazwy kwalifikowane według schematu
- Wszelkie indeksy utworzone w tabeli tymczasowej są również automatycznie tymczasowe
- ON COMMIT zachowuje wiersze, jest to zachowanie domyślne
- Opcjonalnie, GLOBAL lub LOKAL można wpisać przed TEMPORARY lub TEMP. Obecnie nie ma to żadnego znaczenia w PostgreSQL i jest przestarzałe
- Autoodkurzacz demon nie może uzyskać dostępu do tych tabel i dlatego nie może odkurzać ani analizować tabel tymczasowych, jednak, jak pokazano wcześniej, polecenia autoodkurzania i analizy mogą być używane jako polecenia SQL.
Globalne tabele tymczasowe (GTT) w Oracle
Ten rodzaj tabel jest znany w świecie Oracle jako Global Temporary Table (GTT). Obiekty te są trwałe w bazie danych i można je podsumować według następujących cech:
- Struktura jest statyczna i widoczna dla wszystkich użytkowników, jednak jej zawartość jest widoczna tylko dla bieżącej sesji
- Może być utworzony w określonym schemacie (domyślnie będzie własnością użytkownika wydającego polecenie) i jest zbudowany w przestrzeni tabel TEMP
- Po utworzeniu w bazie danych nie można go ponownie utworzyć w każdej sesji, jednak dane zarządzane przez sesję nie są widoczne dla innych sesji
- Możliwe tworzenie indeksów i generowanie statystyk
- Ponieważ struktura tych tabel jest również zdefiniowana w bazie danych, nie można przypisać ich nazwy do stałej tabeli (w Oracle dwa obiekty nie mogą mieć tej samej nazwy nawet z różnych typów)
- Nie generuj zbyt wielu dzienników przeróbek, a narzut cofania jest również mniejszy w porównaniu ze stałą tabelą (tylko z tych powodów użycie GTT jest szybsze) dla dowolnych wersji przed 12c. Od wersji 12c istnieje koncepcja tymczasowego cofania, pozwalająca na zapisanie cofania dla GTT w tymczasowym obszarze tabel, co ogranicza cofanie i ponawianie.
Idąc za tym samym przykładem przedstawionym w PostgreSQL, tworzenie GTT jest dość podobne:
CREATE GLOBAL TEMPORARY TABLE tt_customer
(
customer_id NUMBER
)
ON COMMIT DELETE ROWS;
Możliwe jest również tworzenie indeksów.
creation index tt_cusomer_idx_1 on tt_customer(customer_id)
Przed Oracle 12c generowanie statystyk dla globalnych tabel tymczasowych zachowywało się w sposób globalny:statystyki wygenerowane w konkretnej sesji dla konkretnego GTT były widoczne i wykorzystywane dla innych sesji (tylko statystyki, a nie dane!), jednak od wersji 12c każda sesja może generować własne statystyki.
Przede wszystkim należy ustawić preferencję global_temp_table_stats na sesję :
exec dbms_stats.set_table_prefs(USER,’TT_CUSTOMER’,’GLOBAL_TEMP_TABLE_STATS’,’SESSION’);
a następnie generowanie statystyk:
exec dbms_stats.gather_table_stats(USER,’TT_CUSTOMER’);
Istniejącą globalną tabelę tymczasową można sprawdzić, wykonując następujące zapytanie:
select table_name from all_tables where temporary = 'Y';
Wskazówki dla programistów dotyczące globalnych tabel tymczasowych (GTT)
Idąc za przykładem z sekcji PostgreSQL:aby przyznać premię klientom, którzy nie dokonali zakupów lub nie logowali się od ponad roku, użycie globalnych tabel tymczasowych w Oracle ma ten sam cel, co w PostgreSQL:osiągnięcie lepszej wydajności zarówno w wykorzystanie zasobów lub szybkość wykonywania.
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>
Domyślnie w Oracle blok/instrukcja SQL/PLSQL rozpoczyna niejawnie transakcję.
Wskazówki DBA dotyczące globalnych tabel tymczasowych (GTT)
Jak stwierdzenie upuść nie istnieje dla globalnych tabel tymczasowych polecenie utworzenia tabeli jest takie samo jak poprzednie:
CREATE GLOBAL TEMPORARY TABLE tt_customer
(
customer_id NUMBER
)
ON COMMIT DELETE ROWS;
Równoważny fragment kodu w Oracle służący do wyczyszczenia klienta tabela jest następująca:
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.
Biblioteka pg_global_temp_tables
Jak wspomniano powyżej, tymczasowe tabele w PostgreSQL nie mogą być wywoływane przy użyciu notacji schema.table , więc biblioteka pg_global_temp_tables (jest kilka podobnych bibliotek dostępnych na github) jest to obejście bardzo przydatne do użycia w migracjach baz danych z Oracle do PostgreSQL.
Aby zachować notację Oracle schema.temporary_table w zapytaniach lub procedurach składowanych:
SELECT c.id,c.nam
FROM web_app.tt_customers tc,
Web_app.customers c
WHERE c.id = tc.id
Pozwala to na pozostawienie tymczasowych tabel nad kodem z notacją schematu.
Zasadniczo składa się z widoku:web_app.tt_customers utworzony w ramach schematu, na którym ma znajdować się tabela tymczasowa, a ten widok prześle zapytanie do tabeli tymczasowej tt_customers za pomocą funkcji o nazwie web_app.select_tt_customers :
CREATE OR REPLACE VIEW WEB_APP.TT_CUSTOMERS AS
SELECT * FROM WEB_APP.SELECT_TT_CUSTOMERS();
Ta funkcja zwraca zawartość tabeli tymczasowej:
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;
Podsumowanie
Tabele tymczasowe służą zasadniczo do przechowywania wyników pośrednich, co pozwala uniknąć skomplikowanych i ciężkich obliczeń,
Następnie wymieniono niektóre cechy tabel tymczasowych w PostgreSQL lub Oracle:
- Może być używany na widoku
- Może użyć polecenia OBCIĄĆ
- Nie można go podzielić na partycje
- Ograniczenie klucza obcego w tabelach tymczasowych jest niedozwolone
- Tego rodzaju tabele są alternatywą dla CTE (Common Table Expressions) znanej również profesjonalistom Oracle jako klauzula WITH
- Pod względem bezpieczeństwa i prywatności tabele te są cennym zasobem, ponieważ dane są widoczne tylko dla bieżącej sesji
- Tabele tymczasowe są automatycznie usuwane (w PostgreSQL) lub usuwane (w Oracle) po zakończeniu sesji/transakcji.
W przypadku tabel tymczasowych w PostgreSQL nie zaleca się używania tej samej nazwy tabeli stałej w tabeli tymczasowej. Po stronie Oracle dobrą praktyką jest generowanie statystyk dla sesji zawierających znaczną ilość danych w GTT w celu wymuszenia na Cost-Based Optimizer (CBO) wyboru najlepszego planu dla zapytań wykorzystujących tego rodzaju tabele .