PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Membandingkan Tabel Sementara untuk PostgreSQL &Oracle GTT

Tabel sementara adalah konsep berguna yang ada di sebagian besar SGBD, meskipun sering kali bekerja secara berbeda.

Blog ini menjelaskan fitur teknis untuk tabel semacam ini baik di database PostgreSQL (versi 11) atau Oracle (versi 12c) dengan beberapa contoh spesifik. Meskipun tujuan tabel ini bisa sama untuk semua SGBD, spesifikasinya, atau cara penerapan dan manipulasinya, sangat berbeda.

Fitur ini dapat digunakan baik oleh pengembang atau administrator basis data untuk menyimpan hasil antara yang akan diperlukan untuk pemrosesan lebih lanjut guna memberikan metrik kinerja yang baik.

Tabel Sementara di PostgreSQL

Di PostgreSQL, objek ini hanya valid untuk sesi saat ini:objek dibuat, digunakan, dan dijatuhkan di sepanjang sesi yang sama:struktur tabel dan data terkelola hanya terlihat untuk sesi saat ini, sehingga sesi lain tidak memiliki akses ke tabel sementara yang dibuat pada sesi lain.

Di bawah ini ditunjukkan contoh sederhana untuk membuat tabel sementara:

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

Tabel sementara dibuat dalam skema sementara:pg_temp_nn dan dimungkinkan untuk membuat indeks pada tabel ini:

creation index  tt_cusomer_idx_1 on tt_customer(customer_id)

Karena baris data pada tabel ini juga dapat dihapus, penyimpanan yang terisi dapat dilepaskan melalui eksekusi vaccum perintah:

VACUUM VERBOSE tt_customer

Analisis perintah dapat dieksekusi juga pada tabel sementara untuk mengumpulkan statistik:

ANALYZE VERBOSE tt_customer;

Kedua perintah dapat dieksekusi untuk tabel seperti ini sebagai perintah SQL, namun autovaccum daemon yang mengeksekusinya tidak bertindak pada tabel sementara.

Poin penting lainnya untuk dipertimbangkan terkait dengan tabel permanen dan sementara dengan nama yang sama:setelah terjadi, tabel permanen hanya diperhitungkan saat dipanggil dengan skema sebagai awalan.

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)

Dari contoh sebelumnya sementara tabel sementara ada, semua referensi ke pelanggan merujuk ke tabel ini, bukan ke tabel permanen.

Kiat Pengembang untuk Tabel Sementara

Tujuan dari contoh ini adalah untuk memberikan bonus kepada pelanggan yang belum melakukan pembelian atau login selama lebih dari satu tahun, sehingga skrip pengembang malah menggunakan sub-kueri dalam kueri sebagai solusi yang memungkinkan (atau penggunaan CTE pernyataan) dapat menggunakan tabel sementara (yang biasanya lebih cepat daripada menggunakan sub-kueri):

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)

Kiat DBA untuk Tabel Sementara

Tugas khas administrator database adalah membersihkan tabel besar yang berisi data yang tidak lagi diperlukan. Ini harus diselesaikan dengan sangat cepat dan sering terjadi. Pendekatan standar adalah memindahkan data ini ke tabel historis di skema lain atau ke database yang lebih jarang diakses.

Jadi, untuk melakukan pemindahan ini, karena masalah kinerja, solusi terbaik adalah menggunakan tabel sementara:

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

Dalam contoh ini, tabel sementara dibuat dengan opsi DROP, jadi ini berarti tabel sementara akan dihapus di akhir blok transaksi saat ini.

Berikut adalah beberapa info penting lainnya tentang tabel sementara PostgreSQL:

  • Tabel sementara secara otomatis dijatuhkan pada akhir sesi atau, seperti yang disajikan pada contoh sebelumnya, pada akhir transaksi saat ini
  • Tabel permanen dengan nama yang sama tidak terlihat oleh sesi saat ini saat tabel sementara ada, kecuali jika direferensikan dengan nama yang memenuhi syarat skema
  • Indeks yang dibuat pada tabel sementara secara otomatis juga sementara
  • ON COMMIT, pertahankan baris, ini adalah perilaku default
  • Opsional, GLOBAL atau LOCAL dapat ditulis sebelum SEMENTARA atau TEMP. Saat ini tidak ada bedanya dengan PostgreSQL dan sudah tidak digunakan lagi
  • autovakum daemon tidak dapat mengakses tabel ini dan oleh karena itu tidak dapat mengosongkan atau menganalisis tabel sementara, namun, seperti yang ditunjukkan sebelumnya, perintah autovacuum dan analisis dapat digunakan sebagai perintah SQL.

Tabel Sementara Global (GTT) di Oracle

Tabel semacam ini dikenal di dunia Oracle sebagai Tabel Sementara Global (atau GTT). Objek-objek ini tetap ada dalam database dan dapat diringkas dengan karakteristik berikut:

  • Strukturnya statis dan terlihat untuk semua pengguna, namun kontennya hanya terlihat untuk sesi saat ini
  • Dapat dibuat dalam skema tertentu (secara default akan dimiliki oleh pengguna yang mengeluarkan perintah) dan dibuat di tablespace TEMP
  • Setelah dibuat di database, tidak dapat dibuat lagi di setiap sesi, namun, data yang dikelola oleh satu sesi tidak terlihat untuk sesi lainnya
  • Ini memungkinkan pembuatan indeks dan pembuatan statistik
  • Karena struktur tabel ini juga didefinisikan dalam database, tidak mungkin untuk menetapkan namanya ke tabel permanen (di Oracle dua objek tidak dapat memiliki nama yang sama bahkan dari tipe yang berbeda)
  • Jangan menghasilkan terlalu banyak redo log dan overhead undo juga lebih sedikit dibandingkan dengan tabel permanen (hanya karena alasan ini penggunaan GTT lebih cepat) untuk versi apa pun sebelum 12c. Dari versi 12c ada konsep undo sementara, memungkinkan undo untuk GTT ditulis ke tablespace sementara, sehingga mengurangi undo dan redo.

Mengikuti contoh yang sama yang disajikan di PostgreSQL, pembuatan GTT sangat mirip:

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

Mungkin juga pembuatan indeks.

creation index  tt_cusomer_idx_1 on tt_customer(customer_id)

Sebelum Oracle 12c, generasi statistik untuk tabel sementara global memiliki perilaku secara global:statistik yang dihasilkan dalam sesi tertentu untuk GTT tertentu terlihat dan digunakan untuk sesi lain (hanya statistik bukan data!), namun, dari versi 12c, setiap sesi dapat menghasilkan statistiknya sendiri.

Pertama-tama perlu untuk mengatur preferensi global_temp_table_stats ke sesi :

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

dan kemudian pembuatan statistik:

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

Tabel sementara global yang ada dapat diperiksa dengan menjalankan kueri berikut:

select table_name from all_tables where temporary = 'Y';

Kiat Pengembang untuk Tabel Sementara Global (GTT)

Mengikuti contoh di bagian PostgreSQL:untuk memberikan bonus kepada pelanggan yang tidak melakukan pembelian atau login selama lebih dari satu tahun, penggunaan tabel sementara global di Oracle memiliki tujuan yang sama dengan di PostgreSQL:untuk mencapai kinerja yang lebih baik baik di penggunaan sumber daya atau dalam kecepatan eksekusi.

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>

Secara default di Oracle, blok/pernyataan SQL/PLSQL memulai transaksi secara implisit.

Kiat DBA untuk Tabel Sementara Global (GTT)

Saat pernyataan jatuhkan tidak ada untuk tabel sementara global perintah untuk membuat tabel itu sama dengan yang sebelumnya:

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

Cuplikan kode yang setara di Oracle untuk membersihkan pelanggan tabelnya sebagai berikut:

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.

Perpustakaan pg_global_temp_tables

Seperti disebutkan di atas, tabel sementara di PostgreSQL tidak dapat dipanggil menggunakan notasi schema.table , jadi pustaka pg_global_temp_tables (ada beberapa pustaka serupa yang tersedia di github) ini adalah solusi yang sangat berguna untuk digunakan dalam migrasi basis data dari Oracle ke PostgreSQL.

Untuk menjaga notasi Oracle schema.temporary_table dalam kueri atau prosedur tersimpan:

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

Ini memungkinkan untuk tetap menjadi tabel sementara di atas kode dengan notasi skema.

Pada dasarnya, ini terdiri dari tampilan:web_app.tt_customers dibuat di bawah skema yang seharusnya memiliki tabel sementara dan tampilan ini akan menanyakan tabel sementara tt_customers melalui fungsi yang disebut web_app.select_tt_customers :

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

Fungsi ini mengembalikan isi tabel sementara:

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;  

Ringkasan

Tabel sementara pada dasarnya digunakan untuk menyimpan hasil antara dan dengan demikian menghindari komputasi yang rumit dan berat,

Setelah itu terdaftar beberapa karakteristik tabel sementara baik di PostgreSQL atau Oracle:

  • Dapat digunakan pada tampilan
  • Dapat menggunakan perintah TRUNCATE
  • Tidak dapat dipartisi
  • Pembatasan kunci Asing pada tabel sementara tidak diizinkan
  • Tabel semacam ini merupakan alternatif untuk CTE (Common Table Expressions) yang juga dikenal oleh para profesional Oracle sebagai WITH klausa
  • Dalam hal keamanan dan privasi, tabel ini merupakan aset berharga karena data hanya terlihat untuk sesi saat ini
  • Tabel sementara secara otomatis dihapus (di PostgreSQL) atau dihapus (di Oracle) setelah sesi/transaksi berakhir.

Untuk tabel sementara di PostgreSQL disarankan untuk tidak menggunakan nama tabel permanen yang sama di tabel sementara. Di sisi Oracle, ini adalah praktik yang baik untuk menghasilkan statistik untuk sesi yang menyertakan volume data yang cukup besar di GTT untuk memaksa Pengoptimal Berbasis Biaya (CBO) untuk memilih paket terbaik untuk kueri yang menggunakan tabel semacam ini .


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana cara menginstal Postgis ke instalasi Tong [dilindungi email] menggunakan Homebrew?

  2. Dapatkan Nama Bulan Pendek di PostgreSQL

  3. Peningkatan otomatis pada kunci primer parsial dengan Entity Framework Core

  4. Bagaimana cara saya mendapatkan dukungan LISTEN/NOTIFY asynchronous/even-driven di Java menggunakan database Postgres?

  5. BeanCreationException:Kesalahan membuat kacang dengan nama 'flywayInitializer'