ALTER TABLE trong PostgreSQL
Xem thửTình huống mở đầu
Tạp Hóa Cô Tấm đã có bảng products với 5 mặt hàng và quầy thu ngân đang chạy cả ngày. Hôm nay có ba việc cần sửa cấu trúc: cột note cần giá trị mặc định, cột price cần thêm một chữ số thập phân, và cô muốn cột id đổi sang kiểu số lớn hơn. Cô Tấm hỏi: "Em sửa lúc quầy đang bán có sao không?" Bạn nhớ trên MySQL có người từng làm treo cả shop vì một lệnh sửa bảng.
Hãy đoán trước: trong ba việc trên, việc nào đụng đến dữ liệu thật, và việc nào chỉ sửa "bảng mô tả"?
Ôn nhanh bài trước: sau khi ROLLBACK, số mà nextval đã phát có quay lại sequence không?
Đáp án: không. Bài này nói về một thứ ngược lại: lệnh sửa bảng ALTER TABLE thì hoàn tác được bằng ROLLBACK.
Mục tiêu bài học
Sau bài này bạn có thể:
- Viết
ALTER TABLEgộp nhiều hành động trong một lệnh và đọc lại bằng\d. - Nêu khóa mà
ALTER TABLEgiữ và chứng minh bằngpg_locks. - Phân biệt hành động chỉ sửa catalog với hành động viết lại cả bảng, và kiểm tra bằng
relfilenode. - Dùng
USING,SET NOT NULL,NOT VALIDvàVALIDATE CONSTRAINTđúng chỗ.
Bức tranh lớn
Xuyên suốt bài, ALTER TABLE là việc sửa cái kệ đang đầy hàng: có sửa chỉ là dán lại nhãn, có sửa phải bê hết hàng sang kệ mới. Trong lúc sửa, cô Tấm treo biển "Đóng cửa kệ" cho mọi khách. Dự án xuyên suốt là bảng products của database shop.
Hình dưới trả lời một câu hỏi: một lệnh ALTER TABLE đi qua những chặng nào, và chặng nào làm nó chậm?
Đang tải sơ đồ…
Cách đọc hình: đọc từ trên xuống. Ô cyan là lệnh. Ô đỏ là khóa mạnh, mọi lệnh ALTER TABLE đều phải qua. Ô cam là câu hỏi quyết định tốc độ. Nhánh xanh lá chỉ sửa catalog (nhanh), nhánh tím viết lại bảng (chậm theo kích thước). Cả hai kết thúc ở COMMIT.
Chỗ ẩn dụ không còn đúng: cô Tấm có thể treo biển cho từng kệ riêng. PostgreSQL treo biển cho cả bảng: ALTER TABLE chặn mọi SELECT và INSERT trên bảng đó, không riêng gì cột bạn đang sửa.
Khái niệm cốt lõi
Bài này có ba khái niệm.
1. Một lệnh có thể làm nhiều việc
ALTER TABLE tên hành_động, hành_động, ... gộp nhiều hành động bằng dấu phẩy: ADD COLUMN, DROP COLUMN, ALTER COLUMN ... TYPE, SET DEFAULT, SET NOT NULL, ADD CONSTRAINT, RENAME. Gộp lại thì lệnh chỉ lấy khóa một lần và, theo tài liệu, các lần quét hoặc viết lại bảng được dồn vào một lượt.
2. Khóa mạnh nhất
Hầu hết dạng ALTER TABLE giữ ACCESS EXCLUSIVE, khóa duy nhất xung đột với cả SELECT. Khóa này giữ đến hết transaction, nên bảng càng bận thì sửa cấu trúc càng cần canh giờ.
3. Sửa catalog và viết lại bảng
Một số hành động chỉ đổi mô tả cột trong catalog (SET DEFAULT, nới numeric(10,2) thành numeric(12,2)). Số khác phải viết lại toàn bộ bảng sang tệp mới (đổi int thành bigint, đổi số chữ số thập phân). Cách biết chắc: so relfilenode của bảng trước và sau.
Ví dụ đầu tiên
Ví dụ nhỏ nhất: gộp ba hành động vào một lệnh. Cột note có mặc định none, name không được rỗng, và thêm cột sku:
ALTER TABLE products
ALTER COLUMN note SET DEFAULT 'none', -- mặc định cho dòng mới
ADD CONSTRAINT products_name_check CHECK (length(name) > 0), -- chặn tên rỗng
ADD COLUMN sku text; -- thêm cột, ba hành động một lệnh
\d products
Dự đoán trước khi xem kết quả: \d products sẽ thấy cột nào mới và ràng buộc nào mới, và note của các dòng cũ là gì?
Kết quả [Đã chạy: PostgreSQL 16.15]:
ALTER TABLE
Table "public.products"
Column | Type | Collation | Nullable | Default
----------+---------------+-----------+----------+------------------------------
id | integer | | not null | generated always as identity
name | text | | not null |
category | text | | not null |
price | numeric(10,2) | | not null |
note | text | | | 'none'::text
sku | text | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (id)
Check constraints:
"products_name_check" CHECK (length(name) > 0)
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
ALTER TABLE products | Mở một lệnh sửa trên bảng products, lấy khóa một lần | Các hành động sau cùng dùng chung khóa này |
ALTER COLUMN note SET DEFAULT 'none' | Đổi giá trị mặc định của cột note trong catalog | Chỉ ảnh hưởng dòng thêm sau này, dòng cũ giữ nguyên |
ADD CONSTRAINT products_name_check CHECK (length(name) > 0) | Thêm ràng buộc và kiểm tra các dòng đang có | Đặt tên để sau này DROP CONSTRAINT dễ gọi |
ADD COLUMN sku text | Thêm cột nullable cuối bảng | Cột nullable không cần điền giá trị cho dòng cũ |
\d products | Đọc lại cấu trúc bảng | Kiểm tra xem cả ba hành động đã vào chưa |
Phần Default của cột note giờ là 'none'::text, cột sku mới xuất hiện, và Check constraints có thêm products_name_check. Còn dữ liệu cũ? Thêm một món mới rồi nhìn cột note:
INSERT INTO products (name, category, price) VALUES ('Gel Pen', 'pen', 8.00);
SELECT id, name, note FROM products ORDER BY id;
INSERT 0 1
id | name | note
----+--------------+------
1 | Pro Notebook |
2 | Pro Pen |
3 | Pencil |
4 | Desk Lamp |
5 | Sticky Notes |
6 | Gel Pen | none
(6 rows)
Năm dòng cũ vẫn có note là rỗng (NULL), chỉ dòng mới nhận none.
Tiếp theo là câu hỏi chính của bài: lệnh giữ khóa nào? Hỏi pg_locks ngay trong transaction, so với một SELECT thường:
BEGIN;
SELECT count(*) AS rows_seen FROM products;
SELECT mode FROM pg_locks WHERE relation = 'products'::regclass;
ROLLBACK;
BEGIN;
ALTER TABLE products ALTER COLUMN note SET DEFAULT 'none';
SELECT mode FROM pg_locks WHERE relation = 'products'::regclass;
ROLLBACK;
BEGIN
rows_seen
-----------
5
(1 row)
mode
-----------------
AccessShareLock
(1 row)
ROLLBACK
BEGIN
ALTER TABLE
mode
---------------------
AccessExclusiveLock
(1 row)
ROLLBACK
SELECT chỉ cần AccessShareLock. ALTER TABLE cần AccessExclusiveLock, khóa xung đột với mọi khóa khác [Suy luận: theo bảng xung đột khóa trong tài liệu, vì bài chưa mở hai phiên song song].
Xem từng bước
Animation dưới đây cho thấy vì sao khóa này đáng quan tâm, và khi nào lệnh phải viết lại bảng. 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]. Mỗi hành động được thử độc lập trên bảng products mới; cột rewritten bằng t khi tệp dữ liệu đổi sang tệp khác:
CREATE FUNCTION file_of(t regclass) RETURNS oid
LANGUAGE sql AS 'SELECT relfilenode FROM pg_class WHERE oid = t';
SELECT file_of('products') AS f \gset
\echo -- SET DEFAULT
ALTER TABLE products ALTER COLUMN note SET DEFAULT 'none';
SELECT file_of('products') <> :f AS rewritten;
SELECT file_of('products') AS f \gset
\echo -- price numeric(10,2) thành numeric(12,2)
ALTER TABLE products ALTER COLUMN price TYPE numeric(12,2);
SELECT file_of('products') <> :f AS rewritten;
SELECT file_of('products') AS f \gset
\echo -- price numeric(12,2) thành numeric(12,3)
ALTER TABLE products ALTER COLUMN price TYPE numeric(12,3);
SELECT file_of('products') <> :f AS rewritten;
SELECT file_of('products') AS f \gset
\echo -- id int thành bigint
ALTER TABLE products ALTER COLUMN id TYPE bigint;
SELECT file_of('products') <> :f AS rewritten;
CREATE FUNCTION
-- SET DEFAULT
ALTER TABLE
rewritten
-----------
f
(1 row)
-- price numeric(10,2) thành numeric(12,2)
ALTER TABLE
rewritten
-----------
f
(1 row)
-- price numeric(12,2) thành numeric(12,3)
ALTER TABLE
rewritten
-----------
t
(1 row)
-- id int thành bigint
ALTER TABLE
rewritten
-----------
t
(1 row)
| Hành động | Viết lại bảng | Giải thích |
|---|---|---|
SET DEFAULT | Không | Chỉ sửa catalog |
numeric(10,2) thành numeric(12,2) | Không | Nới độ rộng, giữ nguyên số chữ số thập phân |
numeric(12,2) thành numeric(12,3) | Có | Số chữ số thập phân đổi nên giá trị phải ghi lại |
int thành bigint | Có | Số 4 byte thành 8 byte, mọi dòng đổi kích thước |
Bạn vừa làm được: nhìn một lệnh ALTER TABLE và đoán nó nhẹ hay nặng, rồi chứng minh bằng relfilenode.
Concept Check
Dùng bảng products ở trên.
Câu 1. Sau ALTER COLUMN note SET DEFAULT 'none', 5 dòng cũ có note là gì?
- A. Giữ nguyên giá trị cũ, chỉ dòng thêm sau mới nhận
none. - B. Tất cả đổi thành
nonevì mặc định áp dụng cho cả bảng. - C. Chỉ dòng đầu tiên đổi thành
none, các dòng còn lại giữ nguyên. - D. Tất cả bị xóa giá trị vì mặc định mới thay thế hoàn toàn.
Câu 2. Phiên A đang mở transaction chứa ALTER TABLE products ... chưa commit. Phiên B chạy SELECT * FROM products. Điều gì xảy ra?
- A. Phiên B đọc ngay dữ liệu cũ vì
SELECTkhông cần khóa bảng. - B. Phiên B báo lỗi ngay vì bảng đang được sửa không cho đọc.
- C. Phiên B phải chờ đến khi phiên A kết thúc transaction.
- D. Phiên B đọc được cấu trúc mới của phiên A trước khi commit.
Câu 3. Hành động nào sau đây phải viết lại cả bảng?
- A.
ALTER COLUMN note SET DEFAULT ''. - B.
ALTER COLUMN price TYPE numeric(12,2)khi cột đang lànumeric(10,2). - C.
ALTER COLUMN id TYPE bigintkhi cột đang làint. - D.
ADD COLUMN sku textkhông cóDEFAULT.
Đáp án và giải thích:
- Câu 1: A.
DEFAULTchỉ có tác dụng khiINSERTbỏ qua cột. B, C, D đều coi nó như lệnhUPDATE. Muốn đổi dòng cũ phảiUPDATEriêng. - Câu 2: C.
AccessExclusiveLockchặn cảAccessShareLockcủaSELECT, nên phiên B phải chờ [Suy luận: theo bảng xung đột khóa]. A và D sai vì transaction chưa commit chưa hiện ra với người khác. - Câu 3: C. Bài đã chạy:
intthànhbigintđổirelfilenode. A, B, D chỉ sửa catalog.
Làm cùng mình
Cô Tấm muốn mọi món mới mà quên nhập loại hàng thì tự vào nhóm misc, và quên nhập giá thì tự bằng 0. Bạn gộp cả hai vào một lệnh.
Bước 1. Vẽ ra giấy
Vẽ bảng ba cột: tên cột, mặc định cũ, mặc định mới. Ghi dự đoán: một dòng INSERT INTO products (name) VALUES ('Mystery Item') sẽ nhận category và price nào?
Bước 2. Viết lệnh
Điền chỗ trống:
ALTER TABLE products
-- TODO 1: đặt mặc định cho cột category là 'misc'
-- TODO 2: đặt mặc định cho cột price là 0
;
INSERT INTO products (name) VALUES ('Mystery Item');
SELECT id, name, category, price FROM products ORDER BY id;
Gợi ý cho chỗ trống thứ nhất: ALTER COLUMN category SET DEFAULT 'misc',. Chú ý dấu phẩy giữa hai hành động.
Bước 3. So kết quả
Đối chiếu với mục Lời giải và đáp án. Nếu lệnh báo lỗi cú pháp, kiểm tra dấu phẩy.
Thử phá nó đi
Bốn tình huống hay gặp [Đã chạy: PostgreSQL 16.15]. Lệnh gây lỗi được chạy trong một lần riêng, nên thông báo nằm cuối kết quả.
Tình huống 1: đổi kiểu cột chữ sang số mà không nói cách đổi.
ALTER TABLE products ALTER COLUMN name TYPE int;
ERROR: column "name" cannot be cast automatically to type integer
HINT: You might need to specify "USING name::integer".
Cách sửa là thêm USING để chỉ cách chuyển. Ở đây ví dụ đổi cột note (đã điền tên hàng) thành độ dài tên:
ALTER TABLE products ALTER COLUMN category TYPE varchar(20);
ALTER TABLE products ALTER COLUMN note TYPE int USING length(note);
\d products
ALTER TABLE
ALTER TABLE
Table "public.products"
Column | Type | Collation | Nullable | Default
----------+-----------------------+-----------+----------+------------------------------
id | integer | | not null | generated always as identity
name | text | | not null |
category | character varying(20) | | not null |
price | numeric(10,2) | | not null |
note | integer | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (id)
Tình huống 2: đặt NOT NULL khi cột còn NULL.
ALTER TABLE products ALTER COLUMN note SET NOT NULL;
ERROR: column "note" of relation "products" contains null values
Sửa bằng cách điền dữ liệu trước rồi mới đặt ràng buộc:
UPDATE products SET note = '' WHERE note IS NULL;
ALTER TABLE products ALTER COLUMN note SET NOT NULL;
\d products
UPDATE 5
ALTER TABLE
Table "public.products"
Column | Type | Collation | Nullable | Default
----------+---------------+-----------+----------+------------------------------
id | integer | | not null | generated always as identity
name | text | | not null |
category | text | | not null |
price | numeric(10,2) | | not null |
note | text | | not null |
Indexes:
"products_pkey" PRIMARY KEY, btree (id)
Tình huống 3: thêm CHECK khi có dòng không thỏa.
ALTER TABLE products ADD CONSTRAINT price_cap CHECK (price < 20);
ERROR: check constraint "price_cap" of relation "products" is violated by some row
| Tình huống | Nguyên nhân | Cách sửa |
|---|---|---|
| Không ép kiểu tự động | PostgreSQL không đoán cách đổi chữ thành số | Viết USING biểu_thức |
contains null values | Còn dòng NULL | UPDATE điền giá trị rồi mới SET NOT NULL |
violated by some row | Dữ liệu cũ vi phạm ràng buộc | Sửa dữ liệu, hoặc thêm với NOT VALID rồi xử lý sau |
Hình dưới đối chiếu hiểu lầm lớn nhất của bài.
Đang tải sơ đồ…
Cách đọc hình: hai chuỗi độc lập. Chuỗi đỏ coi SET DEFAULT như một lệnh sửa dữ liệu. Chuỗi xanh là mô hình đúng, đã chạy ở ví dụ Gel Pen phía trên.
Còn ALTER TABLE trong transaction thì sao? Khác với MySQL, cấu trúc cũng quay lui được:
SELECT count(*) AS columns_before FROM information_schema.columns WHERE table_name = 'products';
BEGIN;
ALTER TABLE products DROP COLUMN note;
SELECT count(*) AS columns_inside FROM information_schema.columns WHERE table_name = 'products';
ROLLBACK;
SELECT count(*) AS columns_after FROM information_schema.columns WHERE table_name = 'products';
columns_before
----------------
5
(1 row)
BEGIN
ALTER TABLE
columns_inside
----------------
4
(1 row)
ROLLBACK
columns_after
---------------
5
(1 row)
Đây là điểm mạnh của PostgreSQL khi làm migration: gói nhiều ALTER TABLE trong BEGIN ... COMMIT, có lỗi thì ROLLBACK và bảng không bị sửa dở.
Tự tay làm
Dùng chung bảng products của bài.
Cơ bản
Bài 1. Đoán nhẹ hay nặng. Với từng lệnh, ghi "nhẹ" (chỉ sửa catalog) hoặc "nặng" (viết lại bảng): SET DEFAULT, int thành bigint, numeric(10,2) thành numeric(12,2). Tiêu chí đạt: đúng cả ba, tương ứng nhẹ, nặng, nhẹ.
Vận dụng
Bài 2. Mặc định gộp. Làm theo mục Làm cùng mình, rồi trả lời: dòng Mystery Item có category và price là gì, và các dòng cũ có đổi không? Tiêu chí đạt: nêu đúng misc, 0.00, và dòng cũ không đổi.
Thử thách (tùy chọn)
Bài 3. Thêm cột bắt buộc theo từng bước. Đầu vào: bảng products 5 dòng. Thêm cột stock int kèm CHECK (stock >= 0), điền 10 cho mọi dòng, rồi đặt NOT NULL. Tiêu chí đạt: \d products thấy stock là not null và có ràng buộc products_stock_check. Gợi ý: thêm nullable trước, điền dữ liệu, rồi mới siết NOT NULL.
Tự kiểm tra
Trả lời bằng lời của bạn, không nhìn lại bài.
- (Mục tiêu 2, nhớ lại) Khóa nào mà hầu hết dạng
ALTER TABLEgiữ, và nó chặn những gì? - (Mục tiêu 3, dự đoán)
ALTER COLUMN id TYPE bigintcó đổirelfilenodekhông? - (Mục tiêu 1, dự đoán)
SET DEFAULTcó làm thay đổinotecủa dòng cũ không? - (Mục tiêu 4, giải thích)
NOT VALIDlàm gì vàVALIDATE CONSTRAINTlàm gì tiếp theo? - (Mục tiêu 4, tìm lỗi)
ALTER COLUMN name TYPE intbáocannot be cast automatically. Sửa thế nào?
Điều cần nhớ
ALTER TABLEgộp nhiều hành động bằng dấu phẩy, nên lấy khóa và quét bảng ít lần hơn.- Hầu hết dạng
ALTER TABLEgiữ khóaACCESS EXCLUSIVEđến hết transaction. - Có hành động chỉ sửa catalog (nhẹ), có hành động viết lại cả bảng (nặng); kiểm tra bằng
relfilenode. - DDL của PostgreSQL hoàn tác được trong transaction.
Cheat sheet:
| Muốn | Viết | Ghi chú |
|---|---|---|
| Gộp nhiều việc | ALTER TABLE t a1, a2, a3 | Ngăn cách bằng dấu phẩy |
| Đặt mặc định | ALTER COLUMN c SET DEFAULT v | Chỉ cho dòng mới, nhẹ |
| Đổi kiểu | ALTER COLUMN c TYPE new USING expr | Có thể viết lại bảng |
| Bắt buộc có giá trị | ALTER COLUMN c SET NOT NULL | Lỗi nếu còn NULL |
| Thêm ràng buộc nhẹ tay | ADD CONSTRAINT n CHECK (...) NOT VALID | Dòng cũ chưa kiểm tra |
| Kiểm tra dòng cũ | VALIDATE CONSTRAINT n | Khóa nhẹ hơn khi thêm trực tiếp |
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: thêm cột vào bảng 2 triệu dòng có phải viết lại bảng không, và DEFAULT đóng vai trò gì?
Đào sâu
Phần này dành cho bạn tò mò.
- Cơ chế bên trong. Với bảng lớn, siết ràng buộc theo hai bước giúp giảm thời gian giữ khóa mạnh.
NOT VALIDthêm ràng buộc mà bỏ qua dòng cũ, chỉ kiểm tra dòng mới:
INSERT INTO products (name, category, price) VALUES ('Free Gift', 'misc', -1);
ALTER TABLE products ADD CONSTRAINT price_positive CHECK (price > 0) NOT VALID;
SELECT conname, convalidated FROM pg_constraint WHERE conname = 'price_positive';
INSERT 0 1
ALTER TABLE
conname | convalidated
----------------+--------------
price_positive | f
(1 row)
Khi VALIDATE CONSTRAINT gặp dòng vi phạm thì báo lỗi:
ALTER TABLE products VALIDATE CONSTRAINT price_positive;
ERROR: check constraint "price_positive" of relation "products" is violated by some row
Sửa dữ liệu rồi validate lại là xong:
UPDATE products SET price = 1.00 WHERE price <= 0;
ALTER TABLE products VALIDATE CONSTRAINT price_positive;
SELECT conname, convalidated FROM pg_constraint WHERE conname = 'price_positive';
UPDATE 1
ALTER TABLE
conname | convalidated
----------------+--------------
price_positive | t
(1 row)
- So sánh với lựa chọn khác.
ADD CONSTRAINT ... CHECKkhông cóNOT VALIDquét toàn bảng ngay. VớiPRIMARY KEYvàUNIQUEthì không cóNOT VALID, nên cần dựng index trước rồi gắn vào [Suy luận: theo tài liệu, chưa chạy ở bài này]. - Hiểu lầm nâng cao.
VALIDATE CONSTRAINTgiữ khóa nhẹ hơn khi thêm trực tiếp, nhưng không phải không khóa: theo tài liệu nó lấySHARE UPDATE EXCLUSIVE, vẫn cho đọc và ghi nhưng chặn lệnh sửa cấu trúc khác [Suy luận: dựa trên tài liệu, chưa chạy ở bài này]. - Giới hạn của bài. Bài chưa nói đến
lock_timeoutđể lệnh sửa không chờ khóa vô hạn,ALTER TABLE ... SET SCHEMA, bảng phân vùng hay tablespace. - Đọc thêm. PostgreSQL 16 Documentation, trang
ALTER TABLE: https://www.postgresql.org/docs/16/sql-altertable.html và mục Explicit Locking: https://www.postgresql.org/docs/16/explicit-locking.html
Bài tiếp theo
Bài sau là ADD COLUMN: thêm cột vào bảng đang có dữ liệu, và vì sao từ PostgreSQL 11 thêm cột có DEFAULT hằng số lại gần như tức thì. Mini-dự án nối tiếp: thêm cột in_stock cho cả bảng products của Tạp Hóa Cô Tấm.
Lời giải và đáp án
Làm cùng mình. Hai chỗ trống:
ALTER COLUMN category SET DEFAULT 'misc',
ALTER COLUMN price SET DEFAULT 0
ALTER TABLE products
ALTER COLUMN category SET DEFAULT 'misc',
ALTER COLUMN price SET DEFAULT 0;
INSERT INTO products (name) VALUES ('Mystery Item');
SELECT id, name, category, price FROM products ORDER BY id;
ALTER TABLE
INSERT 0 1
id | name | category | price
----+--------------+----------+-------
1 | Pro Notebook | paper | 45.00
2 | Pro Pen | pen | 12.50
3 | Pencil | pen | 3.00
4 | Desk Lamp | office | 30.00
5 | Sticky Notes | paper | 3.00
6 | Mystery Item | misc | 0.00
(6 rows)
Dòng Mystery Item nhận category là misc và price là 0.00. Năm dòng cũ không đổi.
Bài 1. SET DEFAULT: nhẹ. int thành bigint: nặng. numeric(10,2) thành numeric(12,2): nhẹ (đã chạy ở bảng trace).
Bài 2. Giống mục Làm cùng mình: misc, 0.00, dòng cũ không đổi.
Bài 3.
ALTER TABLE products
ADD COLUMN stock int,
ADD CONSTRAINT products_stock_check CHECK (stock >= 0);
UPDATE products SET stock = 10;
ALTER TABLE products ALTER COLUMN stock SET NOT NULL;
\d products
ALTER TABLE
UPDATE 5
ALTER TABLE
Table "public.products"
Column | Type | Collation | Nullable | Default
----------+---------------+-----------+----------+------------------------------
id | integer | | not null | generated always as identity
name | text | | not null |
category | text | | not null |
price | numeric(10,2) | | not null |
note | text | | |
stock | integer | | not null |
Indexes:
"products_pkey" PRIMARY KEY, btree (id)
Check constraints:
"products_stock_check" CHECK (stock >= 0)
| Dòng code | Máy làm gì | Vì sao viết như vậy |
|---|---|---|
ADD COLUMN stock int | Thêm cột nullable | Dòng cũ có thể tạm NULL |
ADD CONSTRAINT products_stock_check CHECK (stock >= 0) | Thêm ràng buộc, NULL vẫn qua được | CHECK cho phép NULL nên bước này không lỗi |
UPDATE products SET stock = 10 | Điền giá trị cho mọi dòng | Phải điền trước khi siết NOT NULL |
SET NOT NULL | Cấm NULL từ đây | Chỉ thành công khi không còn dòng NULL |
Tự kiểm tra.
ACCESS EXCLUSIVE; nó xung đột với mọi khóa khác, kể cả khóa củaSELECT.- Có,
relfilenodeđổi vì mỗi dòng đổi từ 4 byte lên 8 byte. - Không,
DEFAULTchỉ áp dụng cho dòng thêm sau. NOT VALIDthêm ràng buộc mà không kiểm tra dòng cũ;VALIDATE CONSTRAINTkiểm tra dòng cũ sau đó, với khóa nhẹ hơn.- Thêm
USING, ví dụUSING length(name), tùy ý nghĩa muốn chuyển.