De midlertidige tabeller er et nyttigt koncept, der findes i de fleste SGBD'er, selvom de ofte fungerer anderledes.
Denne blog beskriver de tekniske funktioner for denne type tabeller enten i PostgreSQL (version 11) eller Oracle (version 12c) databaser med nogle specifikke eksempler. Selvom formålet med disse tabeller kunne være det samme for alle SGBD'er, er deres detaljer eller måden at implementere og manipulere på, helt forskellige.
Denne funktion kan bruges både af udviklere eller databaseadministratorer til at gemme mellemresultater, som vil være nødvendige for yderligere behandling for at give gode præstationsmålinger.
Midlertidige tabeller i PostgreSQL
I PostgreSQL er disse objekter kun gyldige for den aktuelle session:de oprettes, bruges og slettes i samme session:strukturen af tabellen og administrerede data er kun synlige for den aktuelle session, så de andre sessioner har ikke adgang til de midlertidige tabeller oprettet på de andre sessioner.
Nedenfor er det vist et simpelt eksempel på at oprette en midlertidig tabel:
CREATE TEMPORARY TABLE tt_customer
(
customer_id INTEGER
)
ON COMMIT DELETE ROWS;
De midlertidige tabeller oprettes i et midlertidigt skema:pg_temp_nn og det er muligt at oprette indekser på disse tabeller:
creation index tt_cusomer_idx_1 on tt_customer(customer_id)
Da datarækkerne på disse tabeller også kan slettes, er det muligt at frigive det optagede lager ved at udføre vaccum kommando:
VACUUM VERBOSE tt_customer
analysen kommandoen kan også udføres på de midlertidige tabeller for at indsamle statistikken:
ANALYZE VERBOSE tt_customer;
Begge kommandoer kan udføres for denne type tabel som SQL-kommando, dog er autovaccum dæmon, der udfører dem, virker ikke på de midlertidige tabeller.
Et andet vigtigt punkt at overveje, er relateret til de permanente og midlertidige tabeller med samme navn:Når det først sker, tages den permanente tabel kun i betragtning, når den kaldes med dens skema som præfiks.
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)
Fra det forrige eksempel, mens den midlertidige tabel eksisterer, er alle referencer til kunderne henviser til denne tabel i stedet for den permanente.
Udviklertip til midlertidige tabeller
Formålet med dette eksempel er at tildele en bonus til de kunder, der ikke har foretaget køb eller login i mere end et år, så udviklerens script i stedet for at bruge underforespørgsler i forespørgsler som en mulig løsning (eller brug af CTE'er) sætning) kan bruge midlertidige tabeller (som normalt er hurtigere end at bruge underforespørgsler):
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-tip til midlertidige tabeller
En typisk opgave for databaseadministratorer er at rense alle store tabeller, der indeholder data, der ikke længere er nødvendige. Dette skal gennemføres meget hurtigt, og det sker ofte. Standardmetoden er at flytte disse data til en historisk tabel i et andet skema eller til en database, der er sjældnere adgang til.
Så for at udføre denne flytning kan den bedste løsning på grund af ydeevneproblemer være at bruge midlertidige tabeller:
CREATE TEMPORARY TABLE tt_customer
(
customer_id INTEGER
)
ON COMMIT DROP;
I dette eksempel blev den midlertidige tabel oprettet med indstillingen DROP, så det betyder, at den vil blive slettet i slutningen af den aktuelle transaktionsblok.
Her er nogle andre vigtige oplysninger om PostgreSQL midlertidige tabeller:
- Midlertidige tabeller slettes automatisk i slutningen af en session eller, som vist i det foregående eksempel, i slutningen af den aktuelle transaktion
- Permanente tabeller med samme navn er ikke synlige for den aktuelle session, mens den midlertidige tabel eksisterer, medmindre de refereres med skema-kvalificerede navne
- Alle indekser, der er oprettet på en midlertidig tabel, er også automatisk midlertidige
- PÅ COMMIT bevare rækker er det standardadfærden
- Valgfrit kan GLOBAL eller LOCAL skrives før TEMPORARY eller TEMP. Dette gør i øjeblikket ingen forskel i PostgreSQL, og det er forældet
- autovakuum daemon kan ikke få adgang til disse tabeller og kan derfor ikke støvsuge eller analysere midlertidige tabeller, men som tidligere vist kan autovacuum- og analysekommandoerne bruges som SQL-kommandoer.
Globale midlertidige tabeller (GTT) i Oracle
Denne slags borde er kendt i Oracle-verdenen som en Global Temporary Table (eller GTT). Disse objekter er persistente i databasen og kan opsummeres med følgende karakteristika:
- Strukturen er statisk og synlig for alle brugere, men dens indhold er kun synlig for den aktuelle session
- Det kan oprettes i et specifikt skema (vil som standard ejes af den bruger, der udsteder kommandoen), og de er bygget i TEMP tablespacet
- Når den først er oprettet i databasen, kan den ikke oprettes igen i hver session, men dataene, der administreres af en session, er ikke synlige for de andre sessioner
- Det er muligt at oprette indekser og generere statistik
- Da strukturen af disse tabeller også er defineret i databasen, er det ikke muligt at tildele dens navn til en permanent tabel (i Oracle kan to objekter ikke have det samme navn, selv fra forskellige typer)
- Generer ikke for mange redo-logs, og fortryd-overheaden er også mindre sammenlignet med en permanent tabel (kun af disse grunde er brugen af GTT hurtigere) for alle versioner før 12c. Fra 12c-versionen er der et koncept med midlertidig fortrydelse, som tillader fortryd for en GTT at blive skrevet til det midlertidige tablespace, og dermed reducerer det fortryd og fortryd.
Efter det samme eksempel præsenteret i PostgreSQL, er oprettelsen af en GTT ret ens:
CREATE GLOBAL TEMPORARY TABLE tt_customer
(
customer_id NUMBER
)
ON COMMIT DELETE ROWS;
Det er også muligt at oprette indekser.
creation index tt_cusomer_idx_1 on tt_customer(customer_id)
Før Oracle 12c havde genereringen af statistikker for globale midlertidige tabeller en adfærd på en global måde:Statistikken genereret i en specifik session for en specifik GTT var synlige og brugte til de andre sessioner (kun statistikker ikke dataene!), men, fra version 12c er det muligt for hver session at generere sin egen statistik.
Først og fremmest er det nødvendigt at indstille præferencen global_temp_table_stats til session :
exec dbms_stats.set_table_prefs(USER,’TT_CUSTOMER’,’GLOBAL_TEMP_TABLE_STATS’,’SESSION’);
og derefter generering af statistik:
exec dbms_stats.gather_table_stats(USER,’TT_CUSTOMER’);
Den eksisterende globale midlertidige tabel kunne kontrolleres ved at udføre følgende forespørgsel:
select table_name from all_tables where temporary = 'Y';
Udviklertip til globale midlertidige tabeller (GTT)
Efter eksemplet på PostgreSQL-sektionen:at tildele en bonus til kunder, der ikke har foretaget køb eller logget ind i mere end et år, har brugen af globale midlertidige tabeller i Oracle det samme mål som i PostgreSQL:at opnå bedre ydeevne enten i brug af ressource eller i eksekveringshastighed.
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>
Som standard i Oracle starter en SQL/PLSQL blok/udsagn implicit en transaktion.
DBA-tip til globale midlertidige tabeller (GTT)
Som udsagnet drop eksisterer ikke for globale midlertidige tabeller kommandoen til at oprette tabellen er den samme som den forrige:
CREATE GLOBAL TEMPORARY TABLE tt_customer
(
customer_id NUMBER
)
ON COMMIT DELETE ROWS;
Det tilsvarende kodestykke i Oracle til at rense kunden tabel er det følgende:
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-biblioteket
Som nævnt ovenfor kan de midlertidige tabeller i PostgreSQL ikke fremkaldes ved hjælp af notationen schema.table , så pg_global_temp_tables-biblioteket (der er nogle lignende biblioteker tilgængelige på github) er det en løsning, der er meget nyttig at bruge i databasemigreringer fra Oracle til PostgreSQL.
For at beholde Oracle-notationen schema.temporary_table i forespørgsler eller lagrede procedurer:
SELECT c.id,c.nam
FROM web_app.tt_customers tc,
Web_app.customers c
WHERE c.id = tc.id
Det giver mulighed for at forblive de midlertidige tabeller over koden med skemanotationen.
Grundlæggende består det af en visning:web_app.tt_customers oprettet under det skema, som den skal have den midlertidige tabel på, og denne visning vil forespørge den midlertidige tabel tt_customers gennem en funktion kaldet web_app.select_tt_customers :
CREATE OR REPLACE VIEW WEB_APP.TT_CUSTOMERS AS
SELECT * FROM WEB_APP.SELECT_TT_CUSTOMERS();
Denne funktion returnerer indholdet af den midlertidige tabel:
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;
Oversigt
De midlertidige tabeller bruges hovedsageligt til at gemme mellemresultater og dermed undgå kompleks og tung databehandling,
Derefter er det opført nogle karakteristika for midlertidige tabeller enten i PostgreSQL eller Oracle:
- Den kan bruges på visning
- Den kan bruge TRUNCATE-kommandoen
- Den kan ikke partitioneres
- Fremmednøglebegrænsningen på midlertidige tabeller er ikke tilladt
- Denne slags tabeller er et alternativ til CTE'er (Common Table Expressions), også kendt for Oracle-professionelle som WITH-klausul
- Med hensyn til sikkerhed og privatliv er disse tabeller et værdifuldt aktiv, fordi dataene kun er synlige for en aktuel session
- De midlertidige tabeller slettes automatisk (i PostgreSQL) eller slettes (i Oracle), når sessionen/transaktionen slutter.
For de midlertidige tabeller i PostgreSQL er det tilrådeligt ikke at bruge det samme navn på en permanent tabel i en midlertidig tabel. På Oracle-siden er det en god praksis at generere statistik for sessioner, der inkluderer en betydelig mængde data i GTT for at tvinge den omkostningsbaserede optimeringsmaskine (CBO) til at vælge den bedste plan for de forespørgsler, der bruger denne slags tabeller .