Các bảng tạm thời là một khái niệm hữu ích có trong hầu hết các SGBD, mặc dù chúng thường hoạt động khác nhau.
Blog này mô tả các tính năng kỹ thuật cho loại bảng này trong cơ sở dữ liệu PostgreSQL (phiên bản 11) hoặc Oracle (phiên bản 12c) với một số ví dụ cụ thể. Mặc dù mục đích của các bảng này có thể giống nhau đối với tất cả các SGBD, nhưng thông tin chi tiết cụ thể hoặc cách thức triển khai và thao tác của chúng là hoàn toàn khác nhau.
Tính năng này có thể được sử dụng bởi cả nhà phát triển hoặc quản trị viên cơ sở dữ liệu để lưu trữ các kết quả trung gian cần thiết để xử lý thêm nhằm cung cấp các chỉ số hiệu suất tốt.
Bảng tạm thời trong PostgreSQL
Trong PostgreSQL, các đối tượng này chỉ hợp lệ cho phiên hiện tại:chúng được tạo, sử dụng và loại bỏ trong cùng một phiên:cấu trúc của bảng và dữ liệu được quản lý chỉ hiển thị cho phiên hiện tại, do đó các phiên khác không có quyền truy cập các bảng tạm thời được tạo trên các phiên khác.
Dưới đây là một ví dụ đơn giản để tạo một bảng tạm thời:
CREATE TEMPORARY TABLE tt_customer
(
customer_id INTEGER
)
ON COMMIT DELETE ROWS;
Các bảng tạm thời được tạo trong một giản đồ tạm thời: pg_temp_nn và có thể tạo chỉ mục trên các bảng này:
creation index tt_cusomer_idx_1 on tt_customer(customer_id)
Vì các hàng dữ liệu trên các bảng này cũng có thể bị xóa, nên có thể giải phóng bộ nhớ bị chiếm dụng thông qua việc thực hiện chân không lệnh:
VACUUM VERBOSE tt_customer
The phân tích lệnh cũng có thể được thực hiện trên các bảng tạm thời để thu thập số liệu thống kê:
ANALYZE VERBOSE tt_customer;
Cả hai lệnh đều có thể được thực thi cho loại bảng này dưới dạng lệnh SQL, tuy nhiên, autovaccum daemon thực thi chúng không hoạt động trên các bảng tạm thời.
Một điểm quan trọng khác cần xem xét là liên quan đến bảng vĩnh viễn và bảng tạm thời có cùng tên:khi nó xảy ra, bảng vĩnh viễn chỉ được tính đến khi được gọi với lược đồ của nó dưới dạng tiền tố.
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)
Từ ví dụ trước trong khi bảng tạm thời tồn tại tất cả các tham chiếu đến khách hàng đề cập đến bảng này thay vì tham chiếu đến bảng vĩnh viễn.
Mẹo dành cho nhà phát triển đối với bảng tạm thời
Mục đích của ví dụ này là chỉ định phần thưởng cho những khách hàng đã không mua hàng hoặc đăng nhập trong hơn một năm, vì vậy tập lệnh của nhà phát triển thay vào đó sử dụng các truy vấn phụ trong các truy vấn như một giải pháp khả thi (hoặc sử dụng CTE câu lệnh) có thể sử dụng các bảng tạm thời (thường nhanh hơn so với sử dụng các truy vấn phụ):
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)
Mẹo DBA cho Bảng Tạm thời
Một nhiệm vụ điển hình cho người quản trị cơ sở dữ liệu là thanh lọc bất kỳ bảng khổng lồ nào có chứa dữ liệu không còn cần thiết. Việc này cần được hoàn thành rất nhanh chóng và nó thường xuyên xảy ra. Cách tiếp cận tiêu chuẩn là di chuyển dữ liệu này sang một bảng lịch sử trong một lược đồ khác hoặc đến một cơ sở dữ liệu được truy cập ít thường xuyên hơn.
Vì vậy, để thực hiện việc di chuyển này, do các vấn đề về hiệu suất, giải pháp tốt nhất có thể là sử dụng các bảng tạm thời:
CREATE TEMPORARY TABLE tt_customer
(
customer_id INTEGER
)
ON COMMIT DROP;
Trong ví dụ này, bảng tạm thời đã được tạo bằng tùy chọn DROP, vì vậy, nó có nghĩa là bảng sẽ bị xóa ở cuối khối giao dịch hiện tại.
Dưới đây là một số thông tin quan trọng khác về bảng tạm thời PostgreSQL:
- Các bảng tạm thời sẽ tự động bị loại bỏ vào cuối phiên hoặc, như được trình bày trong ví dụ trước, khi kết thúc giao dịch hiện tại
- Các bảng vĩnh viễn có cùng tên sẽ không hiển thị với phiên hiện tại trong khi bảng tạm thời tồn tại, trừ khi chúng được tham chiếu với các tên đủ điều kiện giản đồ
- Mọi chỉ mục được tạo trên bảng tạm thời cũng tự động là chỉ mục tạm thời
- ON COMMIT giữ nguyên các hàng đó là hành vi mặc định
- Theo tùy chọn, GLOBAL hoặc LOCAL có thể được viết trước TEMPORARY hoặc TEMP. Điều này hiện không có gì khác biệt trong PostgreSQL và nó không còn được dùng nữa
- autovacuum daemon không thể truy cập vào các bảng này và do đó không thể hút chân không hoặc phân tích các bảng tạm thời, tuy nhiên, như đã trình bày trước đây, lệnh autovacuum và phân tích có thể được sử dụng như lệnh SQL.
Bảng tạm thời toàn cầu (GTT) trong Oracle
Loại bảng này được thế giới Oracle gọi là Bảng Tạm thời Toàn cầu (hay GTT). Các đối tượng này tồn tại lâu dài trong cơ sở dữ liệu và có thể được tóm tắt bằng các đặc điểm sau:
- Cấu trúc là tĩnh và hiển thị cho tất cả người dùng, tuy nhiên, nội dung của nó chỉ hiển thị cho phiên hiện tại
- Nó có thể được tạo trong một lược đồ cụ thể (theo mặc định sẽ do người dùng ban hành lệnh sở hữu) và chúng được tạo trong vùng bảng TEMP
- Sau khi được tạo trong cơ sở dữ liệu, nó không thể được tạo lại trong mỗi phiên, tuy nhiên, dữ liệu do một phiên quản lý sẽ không hiển thị cho các phiên khác
- Có thể tạo chỉ mục và tạo thống kê
- Vì cấu trúc của các bảng này cũng được xác định trong cơ sở dữ liệu nên không thể gán tên của nó cho một bảng vĩnh viễn (trong Oracle, hai đối tượng không thể có cùng tên ngay cả khi thuộc các loại khác nhau)
- Không tạo quá nhiều nhật ký làm lại và chi phí hoàn tác của nó cũng ít hơn so với bảng vĩnh viễn (chỉ vì những lý do này mà việc sử dụng GTT nhanh hơn) cho bất kỳ phiên bản nào trước 12c. Từ phiên bản 12c, có một khái niệm về hoàn tác tạm thời, cho phép hoàn tác đối với GTT được ghi vào vùng bảng tạm thời, do đó, nó giảm bớt việc hoàn tác và làm lại.
Theo ví dụ tương tự được trình bày trong PostgreSQL, việc tạo GTT khá giống nhau:
CREATE GLOBAL TEMPORARY TABLE tt_customer
(
customer_id NUMBER
)
ON COMMIT DELETE ROWS;
Cũng có thể tạo chỉ mục.
creation index tt_cusomer_idx_1 on tt_customer(customer_id)
Trước Oracle 12c, việc tạo thống kê cho các bảng tạm thời toàn cầu có hoạt động theo cách chung:thống kê được tạo trong một phiên cụ thể cho một GTT cụ thể được hiển thị và sử dụng cho các phiên khác (chỉ thống kê không phải dữ liệu!), Tuy nhiên, từ phiên bản 12c, mỗi phiên có thể tạo thống kê của riêng mình.
Trước hết, bạn cần đặt tùy chọn global_temp_table_stats đến phiên :
exec dbms_stats.set_table_prefs(USER,’TT_CUSTOMER’,’GLOBAL_TEMP_TABLE_STATS’,’SESSION’);
và sau đó tạo ra thống kê:
exec dbms_stats.gather_table_stats(USER,’TT_CUSTOMER’);
Bảng tạm thời toàn cục hiện có có thể được kiểm tra bằng cách thực hiện truy vấn sau:
select table_name from all_tables where temporary = 'Y';
Mẹo dành cho nhà phát triển về Bảng tạm thời toàn cầu (GTT)
Theo ví dụ về phần PostgreSQL:để chỉ định phần thưởng cho những khách hàng không mua hàng hoặc đăng nhập trong hơn một năm, việc sử dụng các bảng tạm thời toàn cầu trong Oracle có cùng mục tiêu với PostgreSQL:để đạt được hiệu suất tốt hơn trong sử dụng tài nguyên hoặc tốc độ thực thi.
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>
Theo mặc định trong Oracle, một khối / câu lệnh SQL / PLSQL bắt đầu hoàn toàn một giao dịch.
Mẹo DBA cho Bảng Tạm thời Toàn cầu (GTT)
Như tuyên bố drop không tồn tại cho các bảng tạm thời toàn cục, lệnh tạo bảng giống với bảng trước đó:
CREATE GLOBAL TEMPORARY TABLE tt_customer
(
customer_id NUMBER
)
ON COMMIT DELETE ROWS;
Đoạn mã tương đương trong Oracle để xóa khách hàng bảng nó như sau:
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.
Thư viện pg_global_temp_tables
Như đã đề cập ở trên, không thể gọi các bảng tạm thời trong PostgreSQL bằng ký hiệu schema.table , vì vậy, thư viện pg_global_temp_tables (có một số thư viện tương tự có sẵn trên github), đó là một giải pháp rất hữu ích được sử dụng trong quá trình di chuyển cơ sở dữ liệu từ Oracle sang PostgreSQL.
Để giữ ký hiệu Oracle schema.tempional_table trong các truy vấn hoặc thủ tục được lưu trữ:
SELECT c.id,c.nam
FROM web_app.tt_customers tc,
Web_app.customers c
WHERE c.id = tc.id
Nó cho phép giữ nguyên các bảng tạm thời trên mã với ký hiệu lược đồ.
Về cơ bản, nó bao gồm một chế độ xem: web_app.tt_customers được tạo trong lược đồ mà trên đó nó được cho là có bảng tạm thời và chế độ xem này sẽ truy vấn bảng tạm thời tt_customers thông qua một chức năng có tên là web_app.select_tt_customers :
CREATE OR REPLACE VIEW WEB_APP.TT_CUSTOMERS AS
SELECT * FROM WEB_APP.SELECT_TT_CUSTOMERS();
Hàm này trả về nội dung của bảng tạm thời:
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;
Tóm tắt
Các bảng tạm thời về cơ bản được sử dụng để lưu trữ các kết quả trung gian và do đó tránh tính toán phức tạp và nặng nhọc,
Sau đó, nó được liệt kê một số đặc điểm của các bảng tạm thời trong PostgreSQL hoặc Oracle:
- Nó có thể được sử dụng khi xem
- Nó có thể sử dụng lệnh TRUNCATE
- Nó không thể được phân vùng
- Ràng buộc khóa ngoài trên các bảng tạm thời không được phép
- Loại bảng này là một lựa chọn thay thế cho CTE (Biểu thức bảng thông thường) còn được các chuyên gia Oracle gọi là mệnh đề WITH
- Về mặt bảo mật và quyền riêng tư, các bảng này là một tài sản có giá trị vì dữ liệu chỉ hiển thị cho một phiên hiện tại
- Các bảng tạm thời sẽ tự động bị xóa (trong PostgreSQL) hoặc bị xóa (trong Oracle) sau khi phiên / giao dịch kết thúc.
Đối với các bảng tạm thời trong PostgreSQL, bạn không nên sử dụng cùng tên của một bảng vĩnh viễn trong một bảng tạm thời. Về phía Oracle, một phương pháp hay là tạo thống kê cho các phiên bao gồm khối lượng dữ liệu đáng kể trong GTT để buộc Trình tối ưu hóa dựa trên chi phí (CBO) chọn phương án tốt nhất cho các truy vấn đang sử dụng loại bảng này .