Schema trong PostgreSQL
Xem thửTình huống mở đầu
Cuối năm 2025, Chú Tư ở Cà Phê Chú Tư muốn cất sổ đơn cũ vào một bảng riêng nhưng vẫn gọi nó là orders, để các câu lệnh báo cáo cũ khỏi phải sửa tên bảng. Trong database shop đã có bảng orders với 2 đơn. Chú gõ CREATE TABLE orders (...) lần nữa và nhận lỗi bảng đã tồn tại. Chú băn khoăn: lập database mới thì phải kết nối lại và không nối được với bảng cũ, còn đổi tên thì lại phải sửa mọi câu lệnh.
Hãy đoán trước hai điều. Một, có cách nào để hai bảng cùng tên orders tồn tại trong cùng một database không. Hai, nếu có, câu SELECT * FROM orders sẽ đọc bảng nào.
Ôn nhanh bài trước: self join cần alias vì lý do gì, và lỗi nào xuất hiện khi thiếu?
Đáp án: hai lần nhắc cùng một tên bảng phải có hai alias khác nhau, nếu không PostgreSQL báo table name "staff" specified more than once. Bài này nói về chuyện ngược lại: cùng một tên nhưng ở hai nơi khác nhau, và nơi đó gọi là schema.
Mục tiêu bài học
Sau bài này bạn có thể:
- Giải thích schema là một không gian tên trong database, và liệt kê các schema có sẵn:
public,pg_catalog,information_schema. - Dự đoán bảng nào được chọn khi dùng tên không kèm schema, dựa vào
search_path, và viết tên đủ tiêu chuẩnschema.bảng. - Nêu hai quyền
USAGEvàCREATEtrên schema, và hiệu ứng của PostgreSQL 15 trên schemapublic. - Phân biệt schema với database.
Bức tranh lớn
Xuyên suốt bài, database shop là một tủ hồ sơ, mỗi schema là một ngăn kéo có nhãn. Hai ngăn kéo có thể cùng chứa một tập hồ sơ tên orders mà không lẫn vào nhau. Dự án xuyên suốt là Cà Phê Chú Tư, database shop, với hai bảng cùng tên orders ở hai ngăn:
CREATE SCHEMA demo_archive;
CREATE TABLE orders (
order_id int PRIMARY KEY,
total numeric(10,2) NOT NULL
);
CREATE TABLE demo_archive.orders (
order_id int PRIMARY KEY,
total numeric(10,2) NOT NULL,
archived_on date NOT NULL
);
INSERT INTO orders VALUES (1, 80.00), (2, 45.00);
INSERT INTO demo_archive.orders VALUES (1, 999.00, '2025-12-31');
Hình dưới trả lời một câu hỏi: một database chứa những schema nào, và đâu là phần do bạn tạo ra?
Đang tải sơ đồ…
Cách đọc hình: ô xanh dương ở trên là database shop, một kết nối duy nhất. Nhánh tím là các schema hệ thống luôn có: pg_catalog chứa bảng hệ thống và hàm có sẵn, information_schema chứa các view chuẩn SQL mô tả cấu trúc database. Nhánh xanh lá là schema của người dùng: public có sẵn khi tạo database, demo_archive do ta tạo thêm. Hai ô xanh ngọc ở cuối cho thấy mỗi schema của người dùng có một bảng orders riêng.
Chỗ ẩn dụ không còn đúng: ngăn kéo ngoài đời ngăn cách hoàn toàn, còn schema thì không. Hai bảng ở hai schema nối JOIN với nhau được trong cùng một câu lệnh, và schema chỉ chia tên, không chia kết nối.
Khái niệm cốt lõi
Bài này có bốn khái niệm.
1. Schema là không gian tên trong một database
Một database PostgreSQL chứa nhiều schema, và mỗi schema chứa bảng, view, hàm, kiểu, sequence. Tên đối tượng chỉ cần không trùng trong cùng schema. Vì vậy public.orders và demo_archive.orders là hai bảng khác nhau, và tên đầy đủ của bảng là schema.bảng.
2. search_path quyết định tên không kèm schema trỏ về đâu
search_path là danh sách schema được tìm lần lượt khi bạn viết tên trần. Mặc định là "$user", public: tìm schema trùng tên người dùng (thường không có), rồi public. Schema pg_catalog luôn được tìm đầu tiên dù không ghi trong danh sách. CREATE TABLE không kèm schema tạo bảng ở schema đầu tiên tồn tại trong search_path.
3. Quyền trên schema: USAGE và CREATE
Để dùng đối tượng trong schema, bạn cần quyền USAGE trên schema đó, cộng với quyền trên chính đối tượng (ví dụ SELECT trên bảng). Để tạo đối tượng trong schema, bạn cần quyền CREATE trên schema. Từ PostgreSQL 15, người dùng thường không còn quyền CREATE trên schema public mặc định.
4. Schema không phải database
Mọi schema của một database dùng chung một kết nối, một transaction và có thể JOIN với nhau. Cách ly mạnh hơn (kết nối riêng, sao lưu riêng) cần database riêng. Schema chỉ cô lập tên và quyền truy cập.
Ví dụ đầu tiên
Ví dụ nhỏ nhất: xem search_path mặc định và schema nào đang được dùng. Gõ trong psql:
\c shop
SHOW search_path;
SELECT current_schema() AS current_schema, current_schemas(true) AS resolved_path;
Dự đoán trước khi xem kết quả: current_schema() trả gì, và pg_catalog có nằm trong danh sách đường dẫn không?
Kết quả [Đã chạy: PostgreSQL 16.15]:
search_path
-----------------
"$user", public
(1 row)
current_schema | resolved_path
----------------+---------------------
public | {pg_catalog,public}
(1 row)
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
SHOW search_path; | In giá trị cấu hình đường dẫn tìm tên | "$user" là tên người dùng hiện tại, được thay bằng schema trùng tên nếu có |
current_schema() | Trả schema đầu tiên tồn tại trong đường dẫn | Đây là nơi CREATE TABLE không kèm schema sẽ đặt bảng |
current_schemas(true) | Liệt kê đường dẫn thực tế, kể cả schema ngầm | Cờ true thêm pg_catalog, luôn được tìm trước |
"$user" không có schema nào mang tên người dùng nên bị bỏ qua, và public là schema hiện hành. Tiếp theo, xem các schema trong database mới tạo [Đã chạy: PostgreSQL 16.15]:
SELECT nspname AS schema, pg_get_userbyid(nspowner) AS owner
FROM pg_namespace
WHERE nspname NOT LIKE 'pg\_toast%' AND nspname NOT LIKE 'pg\_temp%'
ORDER BY nspname;
schema | owner
--------------------+-------------------
information_schema | postgres
pg_catalog | postgres
public | pg_database_owner
(3 rows)
Chỉ có ba schema. public thuộc về vai trò đặc biệt pg_database_owner, nghĩa là chủ database. Hai schema hệ thống thuộc về chủ cụm máy chủ. Bây giờ đến bài toán của Chú Tư: tạo thêm schema demo_archive và hai bảng cùng tên orders (xem setup ở trên). Kiểm tra hai bảng độc lập bằng tên đủ tiêu chuẩn [Đã chạy: PostgreSQL 16.15]:
SELECT 'public' AS from_schema, order_id, total FROM public.orders
UNION ALL
SELECT 'demo_archive', order_id, total FROM demo_archive.orders
ORDER BY 1, 2;
from_schema | order_id | total
--------------+----------+--------
demo_archive | 1 | 999.00
public | 1 | 80.00
public | 2 | 45.00
(3 rows)
PostgreSQL ghi nhận hai bảng cùng tên ở hai schema [Đã chạy: PostgreSQL 16.15]:
SELECT schemaname, tablename
FROM pg_tables
WHERE tablename = 'orders'
ORDER BY schemaname;
schemaname | tablename
--------------+-----------
demo_archive | orders
public | orders
(2 rows)
Còn câu SELECT * FROM orders không kèm schema thì đọc bảng nào? Với search_path mặc định [Đã chạy: PostgreSQL 16.15]:
SELECT * FROM orders ORDER BY order_id;
order_id | total
----------+-------
1 | 80.00
2 | 45.00
(2 rows)
Đọc public.orders (hai đơn), vì public là schema đầu tiên có bảng này. Đổi đường dẫn để demo_archive đứng trước [Đã chạy: PostgreSQL 16.15]:
SET search_path = demo_archive, public;
SHOW search_path;
SELECT current_schema() AS current_schema;
SELECT * FROM orders;
SELECT * FROM public.orders ORDER BY order_id;
SET
search_path
----------------------
demo_archive, public
(1 row)
current_schema
----------------
demo_archive
(1 row)
order_id | total | archived_on
----------+--------+-------------
1 | 999.00 | 2025-12-31
(1 row)
order_id | total
----------+-------
1 | 80.00
2 | 45.00
(2 rows)
Cùng chữ orders nhưng bảng khác: đơn cũ 999.00 thay vì hai đơn. Tên đủ tiêu chuẩn public.orders vẫn đọc đúng bảng cũ, bất kể đường dẫn.
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
SET search_path = demo_archive, public; | Đặt đường dẫn cho phiên hiện tại | Chỉ đổi trong phiên, các kết nối khác không bị ảnh hưởng |
SELECT * FROM orders | Tìm orders lần lượt qua pg_catalog, demo_archive, public | Dừng ngay ở schema đầu tiên có bảng |
SELECT * FROM public.orders | Đi thẳng tới public | Tên đủ tiêu chuẩn không dùng đường dẫn |
CREATE TABLE không kèm schema cũng theo đường dẫn. Với search_path = demo_archive, bảng notes nằm ở demo_archive [Đã chạy: PostgreSQL 16.15]:
SET search_path = demo_archive;
CREATE TABLE notes (note_id int, body text);
SELECT schemaname, tablename FROM pg_tables WHERE tablename = 'notes';
SET
CREATE TABLE
schemaname | tablename
--------------+-----------
demo_archive | notes
(1 row)
Muốn tra cứu cấu trúc bằng truy vấn, SQL chuẩn có information_schema, còn PostgreSQL có thêm pg_catalog riêng. Cả hai nằm ở hai schema hệ thống [Đã chạy: PostgreSQL 16.15]:
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_name = 'orders'
ORDER BY table_schema;
SELECT relnamespace::regnamespace AS schema_of_pg_class
FROM pg_class
WHERE relname = 'pg_class';
table_schema | table_name
--------------+------------
demo_archive | orders
public | orders
(2 rows)
schema_of_pg_class
--------------------
pg_catalog
(1 row)
Bảng pg_class nằm trong schema pg_catalog, đó là lý do bạn gọi pg_class mà không cần ghi schema. Nếu tên không có ở schema nào trong đường dẫn, PostgreSQL báo lỗi quen thuộc [Đã chạy: PostgreSQL 16.15]:
SELECT * FROM notes;
ERROR: relation "notes" does not exist
LINE 1: SELECT * FROM notes;
^
Tạo bảng trong schema chưa có thì báo một lỗi khác, 3F000 [Đã chạy: PostgreSQL 16.15]:
CREATE TABLE demo_ghost.notes (note_id int);
\echo SQLSTATE: :LAST_ERROR_SQLSTATE
SQLSTATE: 3F000
ERROR: schema "demo_ghost" does not exist
LINE 1: CREATE TABLE demo_ghost.notes (note_id int);
^
Cuối cùng là quyền. Từ PostgreSQL 15, public không còn cho PUBLIC (mọi vai trò) quyền CREATE. Ta thử với vai trò schema_clerk mới tạo, chưa cấp quyền gì [Đã chạy: PostgreSQL 16.15]:
SET ROLE schema_clerk;
CREATE TABLE public.notes (note_id int);
\echo SQLSTATE: :LAST_ERROR_SQLSTATE
SET
SQLSTATE: 42501
ERROR: permission denied for schema public
LINE 1: CREATE TABLE public.notes (note_id int);
^
Quyền USAGE trên public vẫn còn nhưng CREATE thì không [Đã chạy: PostgreSQL 16.15]:
SELECT has_schema_privilege('schema_clerk', 'public', 'USAGE') AS can_use_public,
has_schema_privilege('schema_clerk', 'public', 'CREATE') AS can_create_in_public;
\dn+ public
can_use_public | can_create_in_public
----------------+----------------------
t | f
(1 row)
List of schemas
Name | Owner | Access privileges | Description
--------+-------------------+----------------------------------------+------------------------
public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema
| | =U/pg_database_owner |
(1 row)
Cột Access privileges của public đọc là: chủ (pg_database_owner) có UC (USAGE và CREATE), còn mục bắt đầu bằng dấu bằng (nghĩa là PUBLIC, tức mọi vai trò) chỉ có U (USAGE). Giờ là chuỗi lỗi quen thuộc khi cấp quyền thiếu. Schema demo_desk có bảng menu. Vai trò chưa có gì:
SET ROLE schema_clerk;
SELECT * FROM demo_desk.menu;
SET
ERROR: permission denied for schema demo_desk
LINE 1: SELECT * FROM demo_desk.menu;
^
Cấp USAGE trên schema, nhưng chưa cấp quyền bảng:
GRANT USAGE ON SCHEMA demo_desk TO schema_clerk;
SET ROLE schema_clerk;
SELECT * FROM demo_desk.menu;
GRANT
SET
ERROR: permission denied for table menu
Cấp thêm SELECT trên bảng thì chạy được:
GRANT USAGE ON SCHEMA demo_desk TO schema_clerk;
GRANT SELECT ON demo_desk.menu TO schema_clerk;
SET ROLE schema_clerk;
SELECT * FROM demo_desk.menu;
GRANT
GRANT
SET
item
-------
latte
(1 row)
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
permission denied for schema demo_desk | Vai trò chưa có USAGE nên không "bước vào" schema được | Quyền schema được kiểm tra trước quyền bảng |
GRANT USAGE ON SCHEMA demo_desk TO schema_clerk; | Cho phép vai trò nhìn thấy tên bên trong schema | USAGE chưa cho đọc dữ liệu bảng nào |
permission denied for table menu | Đã vào schema nhưng chưa có SELECT trên bảng | Hai tầng quyền độc lập nhau |
GRANT SELECT ON demo_desk.menu TO schema_clerk; | Cho phép đọc bảng menu | Chỉ khi có đủ hai quyền mới đọc được |
Xem từng bước
Animation dưới đây chạy lại thứ tự tìm tên trần theo search_path, rồi so với tên đủ tiêu chuẩn. Hãy trả lời câu dự đoán trước khi bấm bước đầu tiên.
Đang tải Animation…
Bảng trace bằng chữ [Đã chạy: PostgreSQL 16.15, hai bảng orders ở hai schema]:
| Cách viết | search_path | Bảng được đọc | Dữ liệu |
|---|---|---|---|
orders | mặc định (public) | public.orders | 2 đơn: 80.00 và 45.00 |
orders | demo_archive, public | demo_archive.orders | 1 đơn cũ 999.00 |
public.orders | bất kỳ | public.orders | 2 đơn: 80.00 và 45.00 |
demo_archive.orders | bất kỳ | demo_archive.orders | 1 đơn cũ 999.00 |
Bạn vừa làm được: nhìn một tên bảng và search_path rồi nói trước bảng nào sẽ được đọc.
Concept Check
Dùng dữ liệu của bài: public.orders có 2 đơn, demo_archive.orders có 1 đơn cũ.
Câu 1. search_path là demo_archive, public. SELECT count(*) FROM orders trả bao nhiêu?
- A. 2, vì
publicluôn được đọc trước mọi schema khác trong đường dẫn. - B. 3, vì PostgreSQL tự gộp cả hai bảng cùng tên vào một kết quả.
- C. Lỗi, vì hai bảng cùng tên làm tên
orderstrở nên mơ hồ. - D. 1, vì
demo_archiveđứng trước và có bảngordersnên dừng tìm.
Câu 2. Ai cũng gõ CREATE TABLE notes (...) không kèm schema khi search_path là demo_archive, public. Bảng nằm ở đâu?
- A.
public, vìpubliclà schema mặc định của mọi bảng mới. - B.
demo_archive, vì đó là schema đầu tiên tồn tại trong đường dẫn. - C.
pg_catalog, vì schema hệ thống luôn được tìm và dùng đầu tiên. - D. Không bảng nào, vì phải ghi rõ schema khi tạo bảng.
Câu 3. Vai trò có USAGE trên schema demo_desk nhưng chưa có SELECT trên bảng menu. SELECT * FROM demo_desk.menu cho kết quả nào?
- A. Đọc được, vì
USAGEtrên schema đủ để đọc mọi bảng bên trong. - B. Lỗi
permission denied for table menu, vì còn thiếu quyền trên bảng. - C. Lỗi
permission denied for schema demo_desk, vìUSAGEchưa đủ để vào. - D. Trả 0 dòng, vì quyền thiếu làm bảng trông như trống.
Đáp án và giải thích:
- Câu 1: D. A sai vì thứ tự trong
search_pathlà thứ tự tìm. B sai vì PostgreSQL không gộp bảng. C sai vì tên trần được phân giải theo đường dẫn nên không mơ hồ. - Câu 2: B. A sai vì
publicchỉ là mặc định khi đường dẫn mặc định. C sai vì bảng người dùng không được tạo ởpg_catalog. D sai vì tạo bảng theo đường dẫn là hợp lệ. - Câu 3: B. A sai vì quyền schema và quyền bảng là hai tầng. C sai vì
USAGEđã đủ để vào schema. D sai vì thiếu quyền là lỗi, không phải bảng trống.
Làm cùng mình
Chú Tư muốn một báo cáo đọc cùng đơn số 1 theo ba cách viết: tên trần, public.orders và demo_archive.orders, khi search_path là demo_archive, public. Mục tiêu là thấy tận mắt tên trần trỏ về đâu.
Bước 1. Vẽ ra giấy
Vẽ hai ngăn kéo public và demo_archive, mỗi ngăn có một bảng orders. Viết dưới mỗi ngăn tổng tiền của đơn số 1. Dự đoán ba giá trị total sẽ ra.
Bước 2. Điền chỗ trống
SET search_path = demo_archive, public;
SELECT 'bare name' AS how, total FROM ... WHERE order_id = 1 -- TODO 1: viết tên trần
UNION ALL
SELECT 'public.orders', total FROM ... WHERE order_id = 1 -- TODO 2: tên đủ tiêu chuẩn của bảng ở public
UNION ALL
SELECT 'demo_archive.orders', total FROM ... WHERE order_id = 1; -- TODO 3: tên đủ tiêu chuẩn của bảng ở demo_archive
Gợi ý: chỗ trống thứ hai và thứ ba đều có dạng schema.bảng.
Bước 3. Chạy và đối chiếu
Chạy xong bạn sẽ thấy [Đã chạy] ba dòng với hai giá trị khác nhau. Đáp án ở mục Lời giải và đáp án.
Bước 4. Đảo thứ tự
Đổi dòng đầu thành SET search_path = public, demo_archive;. Dự đoán trước: dòng bare name thay đổi thế nào.
Thử phá nó đi
Ba lỗi người mới thường gặp với schema [Đã chạy: PostgreSQL 16.15]:
| Code lỗi | Thông báo lỗi | Nguyên nhân | Cách sửa |
|---|---|---|---|
SELECT * FROM notes; | relation "notes" does not exist | Bảng nằm ở schema không có trong search_path | Viết demo_archive.notes hoặc thêm schema vào đường dẫn |
CREATE TABLE demo_ghost.notes (...) | schema "demo_ghost" does not exist (3F000) | Schema chưa được tạo | CREATE SCHEMA demo_ghost; trước |
CREATE TABLE public.notes (...) bởi vai trò thường | permission denied for schema public (42501) | Từ PostgreSQL 15, vai trò thường không còn CREATE trên public | Dùng schema riêng được cấp quyền, hoặc GRANT CREATE |
Hiểu lầm quan trọng nhất của bài là coi schema như một database con. Hình dưới đối chiếu mô hình sai với mô hình đúng.
Đang tải sơ đồ…
Cách đọc hình: hai chuỗi độc lập. Chuỗi đỏ là niềm tin sai, dẫn đến việc chờ kết nối riêng và không nối được giữa các schema. Chuỗi xanh là thực tế: một kết nối, nối JOIN thoải mái, và quyền được kiểm soát bằng USAGE và CREATE trên từng schema.
Tự tay làm
Dùng chung dữ liệu của bài: public.orders (2 đơn) và demo_archive.orders (1 đơn).
Cơ bản
Bài 1. Dự đoán số dòng. Đầu vào: ba nhóm lệnh dưới đây. Điền kết quả mỗi count(*) trước khi chạy.
SET search_path = public, demo_archive;
SELECT count(*) AS a FROM orders;
SET search_path = demo_archive;
SELECT count(*) AS b FROM orders;
SET search_path = demo_archive;
SELECT count(*) AS c FROM public.orders;
Tiêu chí đạt: đúng cả ba số. Gợi ý: tên trần theo search_path, tên đủ tiêu chuẩn bỏ qua nó.
Vận dụng
Bài 2. Đối tượng nằm ở đâu. Đầu vào: tạo schema demo_reports, bảng daily (day date, revenue numeric(10,2)) trong đó, rồi đặt search_path = demo_reports và tạo view totals không kèm schema. Dùng pg_views và pg_tables để chứng minh view và bảng nằm ở schema nào. Tiêu chí đạt: cả hai đều ở demo_reports. Gợi ý: CREATE VIEW không kèm schema cũng theo đường dẫn.
Thử thách (tùy chọn)
Bài 3. Bản đồ bảng hai schema. Đầu vào: dữ liệu của bài. Viết một truy vấn dùng pg_class và pg_namespace liệt kê mọi bảng thường ở hai schema public và demo_archive, mỗi dòng gồm tên schema và tên bảng. Tiêu chí đạt: 2 dòng. Gợi ý: nối hai bảng hệ thống qua relnamespace và oid, và lọc relkind = 'r' (bảng thường).
Tự kiểm tra
Trả lời bằng lời của bạn, không nhìn lại bài. Mỗi câu ghi mục tiêu được kiểm tra.
- (Mục tiêu 1, giải thích) Vì sao
public.ordersvàdemo_archive.orderscùng tồn tại được trong một database? - (Mục tiêu 1, nhận dạng) Kể ba schema có sẵn trong database mới và vai trò của từng cái.
- (Mục tiêu 2, dự đoán)
search_pathlàdemo_archive, public:SELECT * FROM ordersđọc bảng nào, vàpublic.ordersđọc bảng nào? - (Mục tiêu 3, phân tích) Vì sao vai trò chỉ có
USAGEtrên schema vẫn không đọc được bảng bên trong? - (Mục tiêu 4, so sánh) Schema khác database ở điểm nào về kết nối và về việc nối bảng?
Điều cần nhớ
- Schema là không gian tên trong một database; tên đầy đủ là
schema.bảng. search_pathquyết định tên trần trỏ về đâu;pg_catalogluôn được tìm trước;CREATE TABLEtrần tạo ở schema đầu tiên tồn tại trong đường dẫn.- Dùng đối tượng cần
USAGEtrên schema cộng quyền trên đối tượng; tạo đối tượng cầnCREATE. Từ PostgreSQL 15, vai trò thường không cóCREATEtrênpublic. - Schema không phải database: một kết nối, nối
JOINđược giữa các schema.
Cheat sheet:
| Muốn | Viết | Ghi chú |
|---|---|---|
| Xem đường dẫn | SHOW search_path; | Mặc định "$user", public |
| Biết schema hiện hành | SELECT current_schema(); | Nơi CREATE TABLE trần đặt bảng |
| Chỉ định bảng chắc chắn | schema.bảng | Bỏ qua search_path |
| Đổi đường dẫn phiên | SET search_path = a, b; | Chỉ trong phiên hiện tại |
| Cấp quyền vào schema | GRANT USAGE ON SCHEMA s TO r; | Cần thêm quyền trên bảng |
Hình tóm tắt của bài là sơ đồ ở mục Bức tranh lớn.
Câu hỏi để dành cho ngày mai: ta đã tạo schema demo_archive bằng một lệnh ngắn. Nếu muốn tạo schema kèm luôn các bảng bên trong, đặt chủ sở hữu cho nó và cấp quyền ngay trong một câu lệnh thì viết thế nào?
Đào sâu
Phần này dành cho bạn tò mò.
- Cơ chế bên trong. Mỗi đối tượng trong catalog mang một
oidcủa schema (cộtrelnamespace), vàsearch_pathchỉ là danh sách tên dùng lúc phân giải. Kiểm chứng ở Bài 3 bên dưới. - So sánh với lựa chọn khác. MySQL dùng từ schema và database như một:
CREATE SCHEMAchỉ là tên khác củaCREATE DATABASE. PostgreSQL phân biệt rõ: một cụm máy chủ có nhiều database, mỗi database có nhiều schema. - Hiểu lầm nâng cao. Đừng tin vào tên bảng trần trong mã chạy bằng vai trò có đặc quyền. Ai có quyền
CREATEtrên một schema đứng trướcpublictrong đường dẫn có thể tạo bảng trùng tên để đón câu lệnh của bạn. Cách phòng là viết tên đủ tiêu chuẩn hoặc đặtsearch_pathrõ ràng trong hàm quan trọng. - Giới hạn của bài. Bài này chưa dạy
CREATE SCHEMAđầy đủ (chủ sở hữu, các đối tượng con),DROP SCHEMAvàALTER SCHEMA; ba bài sau sẽ làm từng việc. - Đọc thêm. Tài liệu PostgreSQL 16, mục Schemas: https://www.postgresql.org/docs/16/ddl-schemas.html
Bài tiếp theo
Bài sau là CREATE SCHEMA: tạo schema kèm chủ sở hữu và các đối tượng con trong một lệnh, và cấp quyền cho từng schema. Mini-dự án nối tiếp: dựng hai schema cho hai chi nhánh của Chú Tư với cùng bộ bảng.
Lời giải và đáp án
Làm cùng mình, Bước 2. Ba chỗ trống điền như sau:
SET search_path = demo_archive, public;
SELECT 'bare name' AS how, total FROM orders WHERE order_id = 1
UNION ALL
SELECT 'public.orders', total FROM public.orders WHERE order_id = 1
UNION ALL
SELECT 'demo_archive.orders', total FROM demo_archive.orders WHERE order_id = 1;
SET
how | total
---------------------+--------
bare name | 999.00
public.orders | 80.00
demo_archive.orders | 999.00
(3 rows)
Với search_path = demo_archive, public, tên trần trỏ về demo_archive.orders nên dòng bare name là 999.00, giống dòng demo_archive.orders, còn dòng public.orders cho 80.00. Ở Bước 4, đổi đường dẫn thành public, demo_archive thì dòng bare name đổi sang 80.00.
Bài 1. Ba kết quả:
SET search_path = public, demo_archive;
SELECT count(*) AS a FROM orders;
SET search_path = demo_archive;
SELECT count(*) AS b FROM orders;
SET search_path = demo_archive;
SELECT count(*) AS c FROM public.orders;
SET
a
---
2
(1 row)
SET
b
---
1
(1 row)
SET
c
---
2
(1 row)
Câu a: 2, vì public đứng trước. Câu b: 1, vì chỉ có demo_archive trong đường dẫn. Câu c: 2, vì tên đủ tiêu chuẩn bỏ qua đường dẫn.
Bài 2.
CREATE SCHEMA demo_reports;
CREATE TABLE demo_reports.daily (day date, revenue numeric(10,2));
SET search_path = demo_reports;
CREATE VIEW totals AS SELECT sum(revenue) AS revenue FROM daily;
SELECT schemaname, viewname FROM pg_views WHERE viewname = 'totals';
SELECT schemaname, tablename FROM pg_tables WHERE tablename = 'daily';
CREATE SCHEMA
CREATE TABLE
SET
CREATE VIEW
schemaname | viewname
--------------+----------
demo_reports | totals
(1 row)
schemaname | tablename
--------------+-----------
demo_reports | daily
(1 row)
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
SET search_path = demo_reports; | Đặt demo_reports làm schema duy nhất trong đường dẫn | Mọi tên trần sau đó trỏ về demo_reports |
CREATE VIEW totals AS SELECT ... FROM daily | Tạo view ở schema hiện hành, đọc daily cũng theo đường dẫn | Cả view lẫn bảng cùng nằm ở demo_reports |
pg_views, pg_tables | Tra catalog xem đối tượng thuộc schema nào | Cột schemaname là bằng chứng |
Bài 3.
SELECT n.nspname AS schema, c.relname AS object, c.relkind
FROM pg_class AS c
JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE n.nspname IN ('public', 'demo_archive')
AND c.relkind = 'r'
ORDER BY n.nspname, c.relname;
schema | object | relkind
--------------+--------+---------
demo_archive | orders | r
public | orders | r
(2 rows)
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
JOIN pg_namespace AS n ON n.oid = c.relnamespace | Đổi mã schema thành tên schema | relnamespace chỉ là mã số oid |
WHERE n.nspname IN ('public', 'demo_archive') | Giới hạn hai schema của bài | Bỏ qua schema hệ thống |
c.relkind = 'r' | Chỉ lấy bảng thường | relkind cũng dùng cho view, sequence, chỉ mục |
Tự kiểm tra.
- Vì tên đối tượng chỉ cần không trùng trong cùng một schema;
publicvàdemo_archivelà hai không gian tên khác nhau. publiclà schema mặc định cho bảng của người dùng,pg_catalogchứa bảng hệ thống và hàm có sẵn,information_schemachứa các view chuẩn SQL mô tả cấu trúc database.- Tên trần đọc
demo_archive.orders(1 đơn cũ);public.ordersđọc bảng ởpublic(2 đơn). - Vì
USAGEchỉ cho vào schema, còn đọc bảng cần thêmSELECTtrên chính bảng. - Schema dùng chung một kết nối và
JOINđược giữa các schema; database tách biệt hơn, mỗi database cần kết nối riêng và không nốiJOINtrực tiếp với database khác.