Keys & Constraints trong SQL Server
1. Tổng quan về Keys & Constraints
Keys và Constraints là các công cụ để:
- Đảm bảo Data Integrity (toàn vẹn dữ liệu)
- Thiết lập Relationships (quan hệ giữa tables)
- Enforce Business Rules (quy tắc nghiệp vụ)
Keys & Constraints Types
Keys & Constraints
Keys
Primary Key
Foreign Key
Unique Key
Candidate Key
Alternate Key
Constraints
NOT NULL
CHECK
DEFAULT
2. Primary Key (PK)
Đặc điểm Primary Key
| Property | Description |
|---|---|
| Unique | Giá trị không trùng lặp |
| NOT NULL | Không được NULL |
| One per table | Mỗi table chỉ có 1 PK |
| Immutable | Không nên thay đổi |
2.1 Single Column Primary Key
SQL
1-- Method 1: Column-level constraint2CREATE TABLE Customer (3 CustomerID INT PRIMARY KEY,4 CustomerName NVARCHAR(100),5 Email NVARCHAR(100)6);78-- Method 2: Table-level constraint (đặt tên)9CREATE TABLE Customer (10 CustomerID INT,11 CustomerName NVARCHAR(100),12 Email NVARCHAR(100),13 CONSTRAINT PK_Customer PRIMARY KEY (CustomerID)14);1516-- Method 3: Với IDENTITY (auto-increment)17CREATE TABLE Customer (18 CustomerID INT IDENTITY(1,1),19 CustomerName NVARCHAR(100),20 Email NVARCHAR(100),21 CONSTRAINT PK_Customer PRIMARY KEY (CustomerID)22);2.2 Composite Primary Key
SQL
1-- Composite PK: nhiều columns2CREATE TABLE OrderDetail (3 OrderID INT,4 ProductID INT,5 Quantity INT,6 UnitPrice DECIMAL(10,2),7 CONSTRAINT PK_OrderDetail PRIMARY KEY (OrderID, ProductID)8);910-- Trong AdventureWorks11-- Sales.SalesOrderDetail có composite PK12SELECT 13 COLUMN_NAME14FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE15WHERE TABLE_SCHEMA = 'Sales' 16AND TABLE_NAME = 'SalesOrderDetail'17AND CONSTRAINT_NAME LIKE 'PK%';18-- Result: SalesOrderID, SalesOrderDetailID2.3 Natural Key vs Surrogate Key
| Type | Description | Example |
|---|---|---|
| Natural Key | Business-meaningful | Email, SSN, ISBN |
| Surrogate Key | System-generated | CustomerID (IDENTITY) |
SQL
1-- Natural Key (có thể thay đổi, dài)2CREATE TABLE Book (3 ISBN VARCHAR(13) PRIMARY KEY, -- Natural4 Title NVARCHAR(200)5);67-- Surrogate Key (recommended)8CREATE TABLE Book (9 BookID INT IDENTITY(1,1) PRIMARY KEY, -- Surrogate10 ISBN VARCHAR(13) UNIQUE, -- Natural as UNIQUE11 Title NVARCHAR(200)12);💡 Best Practice: Dùng Surrogate Key (INT IDENTITY) làm PK, Natural Key làm UNIQUE constraint.
2.4 Primary Key trong AdventureWorks
SQL
1-- Xem tất cả Primary Keys2SELECT 3 t.name AS TableName,4 i.name AS PKName,5 COL_NAME(ic.object_id, ic.column_id) AS ColumnName6FROM sys.tables t7JOIN sys.indexes i ON t.object_id = i.object_id8JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id9WHERE i.is_primary_key = 110AND SCHEMA_NAME(t.schema_id) = 'Sales'11ORDER BY t.name;3. Foreign Key (FK)
Đặc điểm Foreign Key
| Property | Description |
|---|---|
| References PK/UK | Tham chiếu đến PK hoặc UNIQUE |
| Can be NULL | Có thể NULL (optional relationship) |
| Multiple per table | Table có thể có nhiều FK |
| Enforces RI | Đảm bảo Referential Integrity |
3.1 Tạo Foreign Key
SQL
1-- Table cha2CREATE TABLE Category (3 CategoryID INT IDENTITY(1,1) PRIMARY KEY,4 CategoryName NVARCHAR(50)5);67-- Table con với FK8CREATE TABLE Product (9 ProductID INT IDENTITY(1,1) PRIMARY KEY,10 ProductName NVARCHAR(100),11 CategoryID INT,12 -- Method 1: Column-level13 -- FOREIGN KEY (CategoryID) REFERENCES Category(CategoryID)14 15 -- Method 2: Table-level (recommended - đặt tên)16 CONSTRAINT FK_Product_Category 17 FOREIGN KEY (CategoryID) REFERENCES Category(CategoryID)18);3.2 Referential Actions
SQL
1-- ON DELETE và ON UPDATE options2CREATE TABLE Product (3 ProductID INT IDENTITY(1,1) PRIMARY KEY,4 ProductName NVARCHAR(100),5 CategoryID INT,6 CONSTRAINT FK_Product_Category 7 FOREIGN KEY (CategoryID) REFERENCES Category(CategoryID)8 ON DELETE SET NULL -- Khi Category bị xóa, Product.CategoryID = NULL9 ON UPDATE CASCADE -- Khi CategoryID thay đổi, auto update10);| Action | ON DELETE | ON UPDATE |
|---|---|---|
| NO ACTION | Error nếu có child records | Error nếu có child records |
| CASCADE | Xóa child records theo | Update child records theo |
| SET NULL | Set FK = NULL | Set FK = NULL |
| SET DEFAULT | Set FK = DEFAULT value | Set FK = DEFAULT value |
3.3 Self-Referencing Foreign Key
SQL
1-- Employee với Manager (cùng table)2CREATE TABLE Employee (3 EmployeeID INT IDENTITY(1,1) PRIMARY KEY,4 EmployeeName NVARCHAR(100),5 ManagerID INT,6 CONSTRAINT FK_Employee_Manager 7 FOREIGN KEY (ManagerID) REFERENCES Employee(EmployeeID)8);910-- Insert data11INSERT INTO Employee (EmployeeName, ManagerID) VALUES ('CEO', NULL);12INSERT INTO Employee (EmployeeName, ManagerID) VALUES ('Director', 1);13INSERT INTO Employee (EmployeeName, ManagerID) VALUES ('Manager', 2);3.4 AdventureWorks Foreign Keys
SQL
1-- Xem Foreign Keys của Sales.SalesOrderHeader2SELECT 3 fk.name AS FKName,4 OBJECT_NAME(fk.parent_object_id) AS ChildTable,5 COL_NAME(fkc.parent_object_id, fkc.parent_column_id) AS ChildColumn,6 OBJECT_NAME(fk.referenced_object_id) AS ParentTable,7 COL_NAME(fkc.referenced_object_id, fkc.referenced_column_id) AS ParentColumn,8 fk.delete_referential_action_desc AS OnDelete,9 fk.update_referential_action_desc AS OnUpdate10FROM sys.foreign_keys fk11JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id12WHERE OBJECT_NAME(fk.parent_object_id) = 'SalesOrderHeader';1314-- Query với FK relationships15SELECT 16 soh.SalesOrderID,17 soh.OrderDate,18 c.CustomerID,19 p.FirstName + ' ' + p.LastName AS CustomerName,20 sp.Name AS SalesPerson21FROM Sales.SalesOrderHeader soh22JOIN Sales.Customer c ON soh.CustomerID = c.CustomerID23LEFT JOIN Person.Person p ON c.PersonID = p.BusinessEntityID24LEFT JOIN Sales.SalesPerson sp2 ON soh.SalesPersonID = sp2.BusinessEntityID25LEFT JOIN Person.Person sp ON sp2.BusinessEntityID = sp.BusinessEntityID26WHERE soh.SalesOrderID = 43659;4. UNIQUE Constraint
Đặc điểm UNIQUE
| Property | Description |
|---|---|
| Unique values | Không trùng lặp |
| Allows NULL | Cho phép 1 NULL (SQL Server) |
| Multiple per table | Có thể có nhiều UNIQUE |
| Creates index | Tự động tạo index |
4.1 Tạo UNIQUE Constraint
SQL
1-- Single column UNIQUE2CREATE TABLE Customer (3 CustomerID INT IDENTITY(1,1) PRIMARY KEY,4 Email NVARCHAR(100) UNIQUE,5 Phone NVARCHAR(20)6);78-- Named UNIQUE constraint9CREATE TABLE Customer (10 CustomerID INT IDENTITY(1,1) PRIMARY KEY,11 Email NVARCHAR(100),12 Phone NVARCHAR(20),13 CONSTRAINT UQ_Customer_Email UNIQUE (Email),14 CONSTRAINT UQ_Customer_Phone UNIQUE (Phone)15);1617-- Composite UNIQUE (combination must be unique)18CREATE TABLE ProductVariant (19 VariantID INT IDENTITY(1,1) PRIMARY KEY,20 ProductID INT,21 Color NVARCHAR(20),22 Size NVARCHAR(10),23 CONSTRAINT UQ_Product_Color_Size UNIQUE (ProductID, Color, Size)24);4.2 UNIQUE vs PRIMARY KEY
| Feature | PRIMARY KEY | UNIQUE |
|---|---|---|
| NULL values | ❌ Not allowed | ✅ Allowed (1) |
| Per table | 1 only | Multiple |
| Clustered index | Yes (default) | No (non-clustered) |
5. CHECK Constraint
5.1 Tạo CHECK Constraint
SQL
1-- Simple CHECK2CREATE TABLE Product (3 ProductID INT IDENTITY(1,1) PRIMARY KEY,4 ProductName NVARCHAR(100),5 Price DECIMAL(10,2) CHECK (Price > 0),6 Quantity INT CHECK (Quantity >= 0)7);89-- Named CHECK constraint10CREATE TABLE Employee (11 EmployeeID INT IDENTITY(1,1) PRIMARY KEY,12 Salary DECIMAL(12,2),13 HireDate DATE,14 BirthDate DATE,15 CONSTRAINT CK_Employee_Salary CHECK (Salary > 0),16 CONSTRAINT CK_Employee_BirthDate CHECK (BirthDate < HireDate)17);1819-- CHECK với multiple conditions20CREATE TABLE Order (21 OrderID INT IDENTITY(1,1) PRIMARY KEY,22 OrderDate DATE,23 ShipDate DATE,24 Status VARCHAR(20),25 CONSTRAINT CK_Order_Dates CHECK (ShipDate >= OrderDate),26 CONSTRAINT CK_Order_Status CHECK (Status IN ('Pending', 'Shipped', 'Delivered', 'Cancelled'))27);5.2 AdventureWorks CHECK Examples
SQL
1-- Xem CHECK constraints2SELECT 3 cc.name AS ConstraintName,4 OBJECT_NAME(cc.parent_object_id) AS TableName,5 cc.definition AS CheckDefinition6FROM sys.check_constraints cc7WHERE SCHEMA_NAME(OBJECTPROPERTY(cc.parent_object_id, 'SchemaId')) = 'Sales';89-- Ví dụ: SalesOrderHeader có CHECK cho Status10-- CK_SalesOrderHeader_Status: Status >= 0 AND Status <= 86. DEFAULT Constraint
6.1 Tạo DEFAULT Constraint
SQL
1-- DEFAULT value2CREATE TABLE Order (3 OrderID INT IDENTITY(1,1) PRIMARY KEY,4 OrderDate DATE DEFAULT GETDATE(),5 Status VARCHAR(20) DEFAULT 'Pending',6 Quantity INT DEFAULT 1,7 IsActive BIT DEFAULT 18);910-- Named DEFAULT constraint11CREATE TABLE Product (12 ProductID INT IDENTITY(1,1) PRIMARY KEY,13 CreatedDate DATETIME,14 ModifiedDate DATETIME,15 CONSTRAINT DF_Product_CreatedDate DEFAULT GETDATE() FOR CreatedDate,16 CONSTRAINT DF_Product_ModifiedDate DEFAULT GETDATE() FOR ModifiedDate17);1819-- Insert với DEFAULT20INSERT INTO Order (Status) VALUES (DEFAULT); -- Dùng DEFAULT21INSERT INTO Order DEFAULT VALUES; -- Tất cả DEFAULT6.2 AdventureWorks DEFAULT Examples
SQL
1-- Xem DEFAULT constraints2SELECT 3 dc.name AS ConstraintName,4 OBJECT_NAME(dc.parent_object_id) AS TableName,5 COL_NAME(dc.parent_object_id, dc.parent_column_id) AS ColumnName,6 dc.definition AS DefaultValue7FROM sys.default_constraints dc8WHERE SCHEMA_NAME(OBJECTPROPERTY(dc.parent_object_id, 'SchemaId')) = 'Production'9AND OBJECT_NAME(dc.parent_object_id) = 'Product';1011-- Product có nhiều DEFAULT constraints12-- ModifiedDate = GETDATE()13-- rowguid = NEWID()7. NOT NULL Constraint
7.1 Tạo NOT NULL
SQL
1-- NOT NULL trong CREATE TABLE2CREATE TABLE Customer (3 CustomerID INT IDENTITY(1,1) PRIMARY KEY,4 FirstName NVARCHAR(50) NOT NULL,5 LastName NVARCHAR(50) NOT NULL,6 Email NVARCHAR(100), -- NULLable by default7 Phone NVARCHAR(20) -- NULLable8);910-- Combine NOT NULL với DEFAULT11CREATE TABLE Order (12 OrderID INT IDENTITY(1,1) PRIMARY KEY,13 OrderDate DATE NOT NULL DEFAULT GETDATE(),14 Status VARCHAR(20) NOT NULL DEFAULT 'Pending'15);8. Quản lý Constraints
8.1 Add Constraint sau khi tạo Table
SQL
1-- Add PRIMARY KEY2ALTER TABLE Customer3ADD CONSTRAINT PK_Customer PRIMARY KEY (CustomerID);45-- Add FOREIGN KEY6ALTER TABLE Order7ADD CONSTRAINT FK_Order_Customer 8 FOREIGN KEY (CustomerID) REFERENCES Customer(CustomerID);910-- Add UNIQUE11ALTER TABLE Customer12ADD CONSTRAINT UQ_Customer_Email UNIQUE (Email);1314-- Add CHECK15ALTER TABLE Product16ADD CONSTRAINT CK_Product_Price CHECK (Price > 0);1718-- Add DEFAULT19ALTER TABLE Order20ADD CONSTRAINT DF_Order_Status DEFAULT 'Pending' FOR Status;8.2 Drop Constraint
SQL
1-- Drop constraint by name2ALTER TABLE Customer3DROP CONSTRAINT UQ_Customer_Email;45ALTER TABLE Order6DROP CONSTRAINT FK_Order_Customer;78ALTER TABLE Product9DROP CONSTRAINT CK_Product_Price;8.3 Disable/Enable Constraint
SQL
1-- Disable FK check (useful for bulk insert)2ALTER TABLE Order3NOCHECK CONSTRAINT FK_Order_Customer;45-- Enable FK check6ALTER TABLE Order7CHECK CONSTRAINT FK_Order_Customer;89-- Disable all FK in table10ALTER TABLE Order11NOCHECK CONSTRAINT ALL;1213-- Enable all FK14ALTER TABLE Order15CHECK CONSTRAINT ALL;8.4 Xem tất cả Constraints
SQL
1-- All constraints in database2SELECT 3 tc.CONSTRAINT_NAME,4 tc.CONSTRAINT_TYPE,5 tc.TABLE_SCHEMA,6 tc.TABLE_NAME7FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc8WHERE tc.TABLE_SCHEMA = 'Sales'9ORDER BY tc.TABLE_NAME, tc.CONSTRAINT_TYPE;1011-- Chi tiết column constraints12SELECT 13 c.TABLE_NAME,14 c.COLUMN_NAME,15 c.IS_NULLABLE,16 c.COLUMN_DEFAULT,17 tc.CONSTRAINT_TYPE18FROM INFORMATION_SCHEMA.COLUMNS c19LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu 20 ON c.TABLE_NAME = kcu.TABLE_NAME AND c.COLUMN_NAME = kcu.COLUMN_NAME21LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc 22 ON kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME23WHERE c.TABLE_SCHEMA = 'Sales' AND c.TABLE_NAME = 'SalesOrderHeader'24ORDER BY c.ORDINAL_POSITION;9. Hands-on Exercise
Exercise: Design với Constraints
Thiết kế database cho Online Store với đầy đủ constraints:
SQL
1-- 1. Category table2CREATE TABLE Category (3 CategoryID INT IDENTITY(1,1),4 CategoryName NVARCHAR(50) NOT NULL,5 Description NVARCHAR(500),6 IsActive BIT NOT NULL DEFAULT 1,7 CreatedDate DATETIME NOT NULL DEFAULT GETDATE(),8 9 CONSTRAINT PK_Category PRIMARY KEY (CategoryID),10 CONSTRAINT UQ_Category_Name UNIQUE (CategoryName)11);1213-- 2. Product table14CREATE TABLE Product (15 ProductID INT IDENTITY(1,1),16 ProductName NVARCHAR(100) NOT NULL,17 SKU VARCHAR(20) NOT NULL,18 CategoryID INT NOT NULL,19 Price DECIMAL(10,2) NOT NULL,20 StockQuantity INT NOT NULL DEFAULT 0,21 IsActive BIT NOT NULL DEFAULT 1,22 CreatedDate DATETIME NOT NULL DEFAULT GETDATE(),23 24 CONSTRAINT PK_Product PRIMARY KEY (ProductID),25 CONSTRAINT UQ_Product_SKU UNIQUE (SKU),26 CONSTRAINT FK_Product_Category FOREIGN KEY (CategoryID) 27 REFERENCES Category(CategoryID),28 CONSTRAINT CK_Product_Price CHECK (Price > 0),29 CONSTRAINT CK_Product_Stock CHECK (StockQuantity >= 0)30);3132-- 3. Customer table33CREATE TABLE Customer (34 CustomerID INT IDENTITY(1,1),35 Email NVARCHAR(100) NOT NULL,36 FirstName NVARCHAR(50) NOT NULL,37 LastName NVARCHAR(50) NOT NULL,38 Phone NVARCHAR(20),39 CreatedDate DATETIME NOT NULL DEFAULT GETDATE(),40 41 CONSTRAINT PK_Customer PRIMARY KEY (CustomerID),42 CONSTRAINT UQ_Customer_Email UNIQUE (Email),43 CONSTRAINT CK_Customer_Email CHECK (Email LIKE '%@%.%')44);4546-- 4. Order table47CREATE TABLE [Order] (48 OrderID INT IDENTITY(1,1),49 CustomerID INT NOT NULL,50 OrderDate DATETIME NOT NULL DEFAULT GETDATE(),51 Status VARCHAR(20) NOT NULL DEFAULT 'Pending',52 TotalAmount DECIMAL(12,2) NOT NULL DEFAULT 0,53 54 CONSTRAINT PK_Order PRIMARY KEY (OrderID),55 CONSTRAINT FK_Order_Customer FOREIGN KEY (CustomerID) 56 REFERENCES Customer(CustomerID),57 CONSTRAINT CK_Order_Status CHECK (Status IN ('Pending', 'Processing', 'Shipped', 'Delivered', 'Cancelled')),58 CONSTRAINT CK_Order_TotalAmount CHECK (TotalAmount >= 0)59);6061-- 5. OrderDetail table62CREATE TABLE OrderDetail (63 OrderID INT NOT NULL,64 ProductID INT NOT NULL,65 Quantity INT NOT NULL,66 UnitPrice DECIMAL(10,2) NOT NULL,67 LineTotal AS (Quantity * UnitPrice) PERSISTED, -- Computed column68 69 CONSTRAINT PK_OrderDetail PRIMARY KEY (OrderID, ProductID),70 CONSTRAINT FK_OrderDetail_Order FOREIGN KEY (OrderID) 71 REFERENCES [Order](OrderID) ON DELETE CASCADE,72 CONSTRAINT FK_OrderDetail_Product FOREIGN KEY (ProductID) 73 REFERENCES Product(ProductID),74 CONSTRAINT CK_OrderDetail_Quantity CHECK (Quantity > 0),75 CONSTRAINT CK_OrderDetail_UnitPrice CHECK (UnitPrice > 0)76);10. Summary - Best Practices
markdown
1## Constraint Best Practices2 3### Naming Convention4- PK_TableName5- FK_ChildTable_ParentTable6- UQ_TableName_ColumnName7- CK_TableName_Description8- DF_TableName_ColumnName9 10### Design Guidelines11- ✅ Always use PRIMARY KEY12- ✅ Define FOREIGN KEYs for all relationships13- ✅ Use UNIQUE for natural keys (Email, SKU)14- ✅ Use CHECK for business rules15- ✅ Use DEFAULT for common values16- ✅ Use NOT NULL where appropriate17 18### Performance Considerations19- PK creates clustered index by default20- FK columns should have index21- Too many CHECK constraints can slow INSERTsTiếp theo
Bài tiếp theo: Data Types trong SQL Server - học cách chọn data types phù hợp cho từng loại dữ liệu!
