sql >> Database >  >> RDS >> PostgreSQL

Tijdelijke tabellen vergelijken voor PostgreSQL en Oracle GTT

De tijdelijke tabellen zijn een handig concept dat in de meeste SGBD's aanwezig is, ook al werken ze vaak anders.

Deze blog beschrijft de technische kenmerken voor dit soort tabellen in PostgreSQL (versie 11) of Oracle (versie 12c) databases met enkele specifieke voorbeelden. Hoewel het doel van deze tabellen voor alle SGBD's hetzelfde kan zijn, zijn hun specifieke kenmerken, of de manier van implementatie en manipulatie, totaal verschillend.

Deze functie kan zowel door ontwikkelaars als door databasebeheerders worden gebruikt om tussentijdse resultaten op te slaan die nodig zijn voor verdere verwerking om goede prestatiestatistieken te leveren.

Tijdelijke tabellen in PostgreSQL

In PostgreSQL zijn deze objecten alleen geldig voor de huidige sessie:ze worden gemaakt, gebruikt en neergezet tijdens dezelfde sessie:de structuur van de tabel en beheerde gegevens zijn alleen zichtbaar voor de huidige sessie, dus de andere sessies hebben geen toegang tot de tijdelijke tabellen die tijdens de andere sessies zijn gemaakt.

Hieronder wordt een eenvoudig voorbeeld getoond om een ​​tijdelijke tabel te maken:

CREATE TEMPORARY TABLE tt_customer
(
     customer_id INTEGER
)
ON COMMIT DELETE ROWS;

De tijdelijke tabellen worden gemaakt in een tijdelijk schema:pg_temp_nn en het is mogelijk om indexen te maken op deze tabellen:

creation index  tt_cusomer_idx_1 on tt_customer(customer_id)

Aangezien de gegevensrijen op deze tabellen ook kunnen worden verwijderd, is het mogelijk om de bezette opslagruimte vrij te geven door het uitvoeren van vacuüm commando:

VACUUM VERBOSE tt_customer

De analyse commando kan ook worden uitgevoerd op de tijdelijke tabellen om de statistieken te verzamelen:

ANALYZE VERBOSE tt_customer;

Beide commando's kunnen voor dit soort tabellen worden uitgevoerd als SQL-commando, maar de autovaccum daemon die ze uitvoert, werkt niet op de tijdelijke tabellen.

Een ander belangrijk punt om te overwegen is dat het gerelateerd is aan de permanente en tijdelijke tabellen met dezelfde naam:zodra dit gebeurt, wordt alleen rekening gehouden met de permanente tabel wanneer deze wordt aangeroepen met het schema als voorvoegsel.

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)

Uit het vorige voorbeeld, terwijl de tijdelijke tabel bestaat, alle verwijzingen naar de klanten verwijst naar deze tabel in plaats van naar de permanente.

Ontwikkelaarstips voor tijdelijke tabellen

Het doel van dit voorbeeld is om een ​​bonus toe te kennen aan de klanten die al meer dan een jaar geen aankopen hebben gedaan of niet hebben ingelogd, zodat het script van de ontwikkelaar in plaats daarvan subquery's in queries kan gebruiken als mogelijke oplossing (of het gebruik van CTE's statement) tijdelijke tabellen kunnen gebruiken (dat is meestal sneller dan het gebruik van subquery's):

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-tips voor tijdelijke tabellen

Een typische taak voor databasebeheerders is het opschonen van grote tabellen die gegevens bevatten die niet langer nodig zijn. Dit moet zeer snel worden voltooid en het gebeurt vaak. De standaardaanpak is om deze gegevens te verplaatsen naar een historische tabel in een ander schema of naar een database die minder vaak wordt gebruikt.

Dus om deze verplaatsing uit te voeren, vanwege prestatieproblemen, zou de beste oplossing het gebruik van tijdelijke tabellen kunnen zijn:

CREATE TEMPORARY TABLE tt_customer
(
     customer_id INTEGER
)
ON COMMIT DROP;

In dit voorbeeld is de tijdelijke tabel gemaakt met de DROP-optie, dus dit betekent dat deze aan het einde van het huidige transactieblok wordt verwijderd.

Hier is nog wat andere belangrijke informatie over tijdelijke PostgreSQL-tabellen:

  • Tijdelijke tabellen worden automatisch verwijderd aan het einde van een sessie of, zoals in het vorige voorbeeld, aan het einde van de huidige transactie
  • Permanente tabellen met dezelfde naam zijn niet zichtbaar voor de huidige sessie zolang de tijdelijke tabel bestaat, tenzij er naar schema-gekwalificeerde namen wordt verwezen
  • Alle indexen die op een tijdelijke tabel zijn gemaakt, zijn ook automatisch tijdelijk
  • BIJ COMMIT rijen behouden, is dit het standaardgedrag
  • Optioneel kan GLOBAL of LOCAL worden geschreven vóór TIJDELIJK of TEMP. Dit maakt momenteel geen verschil in PostgreSQL en het is verouderd
  • Het autovacuüm daemon heeft geen toegang tot deze tabellen en kan daarom geen tijdelijke tabellen opzuigen of analyseren, maar zoals eerder getoond kunnen de autovacuüm- en analyse-opdrachten worden gebruikt als SQL-opdrachten.

Global Temporary Tables (GTT) in Oracle

Dit soort tabellen staat in de Oracle-wereld bekend als een Global Temporary Table (of GTT). Deze objecten zijn persistent in de database en kunnen worden samengevat door de volgende kenmerken:

  • De structuur is statisch en zichtbaar voor alle gebruikers, maar de inhoud is alleen zichtbaar voor de huidige sessie
  • Het kan worden gemaakt in een specifiek schema (standaard is het eigendom van de gebruiker die de opdracht heeft gegeven) en ze zijn gebouwd in de TEMP-tabelruimte
  • Eenmaal aangemaakt in de database kan het niet opnieuw aangemaakt worden in elke sessie, maar de gegevens die beheerd worden door een sessie zijn niet zichtbaar voor de andere sessies
  • Het is mogelijk indexen te maken en statistieken te genereren
  • Omdat de structuur van deze tabellen ook in de database is gedefinieerd, is het niet mogelijk om de naam aan een permanente tabel toe te wijzen (in Oracle kunnen twee objecten niet dezelfde naam hebben, zelfs niet van verschillende typen)
  • Genereer niet te veel redo-logs en de overhead voor ongedaan maken is ook minder in vergelijking met een permanente tabel (alleen om deze redenen is het gebruik van GTT sneller) voor alle versies ouder dan 12c. Vanaf de 12c-versie is er een concept van tijdelijk ongedaan maken, waardoor het ongedaan maken van een GTT naar de tijdelijke tabelruimte kan worden geschreven, waardoor het ongedaan maken en opnieuw uitvoeren wordt verminderd.

In navolging van hetzelfde voorbeeld gepresenteerd in PostgreSQL, is het maken van een GTT vrij gelijkaardig:

CREATE GLOBAL TEMPORARY TABLE tt_customer
(
     customer_id NUMBER
)
ON COMMIT DELETE ROWS;

Het is ook mogelijk om indexen te maken.

creation index  tt_cusomer_idx_1 on tt_customer(customer_id)

Voorafgaand aan Oracle 12c had het genereren van statistieken voor globale tijdelijke tabellen een globaal gedrag:de statistieken gegenereerd in een specifieke sessie voor een specifieke GTT waren zichtbaar en gebruikt voor de andere sessies (alleen statistieken, niet de gegevens!), echter, vanaf versie 12c is het voor elke sessie mogelijk om zijn eigen statistieken te genereren.

Allereerst is het nodig om de voorkeur global_temp_table_stats in te stellen naar sessie :

exec dbms_stats.set_table_prefs(USER,’TT_CUSTOMER’,’GLOBAL_TEMP_TABLE_STATS’,’SESSION’);

en dan het genereren van statistieken:

exec dbms_stats.gather_table_stats(USER,’TT_CUSTOMER’);

De bestaande globale tijdelijke tabel kan worden gecontroleerd door de volgende query uit te voeren:

select table_name from all_tables where temporary = 'Y';

Ontwikkelaarstips voor wereldwijde tijdelijke tabellen (GTT)

In navolging van het voorbeeld in de PostgreSQL-sectie:om een ​​bonus toe te kennen aan klanten die meer dan een jaar geen aankopen hebben gedaan of niet hebben ingelogd, heeft het gebruik van globale tijdelijke tabellen in Oracle hetzelfde doel als in PostgreSQL:betere prestaties behalen, hetzij in de gebruik van middelen of in uitvoeringssnelheid.

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>

Standaard start in Oracle een SQL/PLSQL-blok/statement impliciet een transactie.

DBA-tips voor wereldwijde tijdelijke tabellen (GTT)

Zoals de uitspraak drop bestaat niet voor globale tijdelijke tabellen de opdracht om de tabel te maken is hetzelfde als de vorige:

CREATE GLOBAL TEMPORARY TABLE tt_customer
(
     customer_id NUMBER
)
ON COMMIT DELETE ROWS;

Het equivalente codefragment in Oracle om de klant te verwijderen tabel is het de volgende:

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.

De bibliotheek pg_global_temp_tables

Zoals hierboven vermeld, kunnen de tijdelijke tabellen in PostgreSQL niet worden aangeroepen met de notatie schema.table , dus de pg_global_temp_tables-bibliotheek (er zijn enkele vergelijkbare bibliotheken beschikbaar op github), het is een tijdelijke oplossing die erg handig is om te gebruiken bij databasemigraties van Oracle naar PostgreSQL.

Om de Oracle-notatie schema.temporary_table te behouden in query's of opgeslagen procedures:

SELECT c.id,c.nam
    FROM web_app.tt_customers tc,
                 Web_app.customers c
    WHERE c.id = tc.id

Het maakt het mogelijk om de tijdelijke tabellen over de code te laten staan ​​met de schemanotatie.

Het bestaat eigenlijk uit een weergave:web_app.tt_customers gemaakt onder het schema waarop het de tijdelijke tabel zou moeten hebben en deze weergave zal de tijdelijke tabel tt_customers opvragen via een functie genaamd web_app.select_tt_customers :

CREATE OR REPLACE VIEW WEB_APP.TT_CUSTOMERS AS 
  SELECT * FROM WEB_APP.SELECT_TT_CUSTOMERS();

Deze functie retourneert de inhoud van de tijdelijke 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;  

Samenvatting

De tijdelijke tabellen worden hoofdzakelijk gebruikt om tussenresultaten op te slaan en zo ingewikkeld en zwaar rekenwerk te voorkomen,

Daarna worden enkele kenmerken van tijdelijke tabellen in PostgreSQL of Oracle vermeld:

  • Het kan worden gebruikt om te zien
  • Het kan het TRUNCATE-commando gebruiken
  • Het kan niet worden gepartitioneerd
  • De beperking van de Foreign key op tijdelijke tabellen is niet toegestaan
  • Dit soort tabellen zijn een alternatief voor CTE's (Common Table Expressions) ook bekend onder de Oracle-professionals als WITH-clausule
  • In termen van veiligheid en privacy zijn deze tabellen een waardevol bezit omdat de gegevens alleen zichtbaar zijn voor een huidige sessie
  • De tijdelijke tabellen worden automatisch verwijderd (in PostgreSQL) of verwijderd (in Oracle) zodra de sessie/transactie eindigt.

Voor de tijdelijke tabellen in PostgreSQL is het raadzaam om niet dezelfde naam van een permanente tabel in een tijdelijke tabel te gebruiken. Aan de Oracle-kant is het een goede gewoonte om statistieken te genereren voor de sessies die een aanzienlijke hoeveelheid gegevens in GTT bevatten om de Cost-Based Optimizer (CBO) te dwingen het beste plan te kiezen voor de query's die dit soort tabellen gebruiken .


  1. MySQL draaitabelquery met dynamische kolommen

  2. ORA-12514 fout na herstarten van de server

  3. PostgreSQL-replicatie voor noodherstel

  4. Gebruik Oracle om drie tabellen te combineren tot één met PIVOT