Định nghĩa : Primary key là một ràng buộc (constraint) đặt trên một hoặc nhiều cột ở trong một bảng dùng để đảm bảo mỗi hàng (row) trong bảng đó là duy nhất
Một số quy tắc cần lưu ý khi tạo một PRIMARY KEY :
1) Một primary key có thể được tạo ra từ một hoặc nhiều cột của một bảng, nhưng một bảng thì chỉ có thể có một primary key
2) Các cột tạo nên một primary key không được phép chứa giá trị NULL
3) Khi tạo primary key cho bảng, thì lúc đó một index của bảng đó cũng được tạo ra, loại index có thể được định nghĩa bởi người dùng (clustered index hoặc nonclustered index), nếu index không được định nghĩa bởi người dùng thì clustered index sẽ tự động được tạo nếu bảng trước đó chưa có clustered index, hoặc nonclustered index sẽ được tạo nếu bảng trước đó đã có clustered index
Trước khi giải thích chi tiết ba quy tắc này ta cùng làm quen với một số cách thức tạo và chỉnh sửa primary key trên một bảng
Tạo Primary key
Primary key có thể được tạo trong lúc tạo bảng, hoặc được thêm vào sau khi bảng đã được tạo, ví dụ sau đây là cách tạo primary key trong lúc tạo bảng
/*Tạo database AZSqlserver để phục vụ cho quá trình demo của bài viết này*/
CREATE DATABASE AZSqlserver
GO
USE AZSqlserver
GO
/*Kiểm tra nếu bảng Products đã tồn tại thì xóa*/
DROP TABLE IF EXISTS dbo.Products
GO
/*tạo bảng Products cùng với primary key đặt trên cột ProductCode */
CREATE TABLE dbo.Products
(
ProductCode INT PRIMARY KEY
,ProductName VARCHAR(20)
,ShopName VARCHAR(50)
)
GO
Sau khi chạy câu lệnh trên ta ấn vào SQL Server Management Studio → View → Object Explorer → Databases → AZSqlserver → Tables → mở 2 thư mục là Columns và Keys sẽ được kết quả như hình sau
Hình 1
Ở thư mục columns có biểu tượng khóa ở cột ProductCode và bên trong dấu ngoặc có mô tả thêm PK, INT, NOT NULL, trong đó PK cho ta biết primary key được đặt trên cột ProductCode, INT là kiểu dữ liệu của ProductCode và NOT NULL là một ràng buộc được thêm vào để đảm bảo các cột thuộc primary key không được phép NULL
Tiếp theo ta nhìn vào thư mục keys, thư mục này cho ta biết tên của primary key là PK__Products__2F4E024EA1326F73, tên này được tạo tự động nên nó rất dài và khá khó nhớ, để khắc phục trường hợp này ta tạo primary key với một tên gọi cụ thể như sau :
USE AZSqlserver
GO
/*Kiểm tra nếu bảng Products đã tồn tại thì xóa*/
DROP TABLE IF EXISTS dbo.Products
GO
/*Tạo Primary key với tên cụ thể là PK_ProductCode */
CREATE TABLE dbo.Products
(
ProductCode INT CONSTRAINT PK_ProductCode PRIMARY KEY
,ProductName VARCHAR(20)
,ShopName VARCHAR(50)
)
GO
Kết quả :
Hình 2
Tên của Primary key lúc này là PK_ProductCode, một cái tên rất dễ sử dụng cho quá trình chỉnh sửa primary key sau này nếu cần
Bên trên là ví dụ về việc tạo primary key trên một cột, trong trường hợp ta muốn tạo primary key với nhiều cột thì làm như sau :
USE AZSqlserver
GO
/*Kiểm tra nếu bảng Products đã tồn tại thì xóa*/
DROP TABLE IF EXISTS dbo.Products
GO
/*Tạo Primary key trên 2 cột là ProductCode và ProductName*/
CREATE TABLE dbo.Products
(
ProductCode INT
,ProductName VARCHAR(20)
,ShopName VARCHAR(50)
,CONSTRAINT PK_Products PRIMARY KEY (ProductCode, ProductName)
)
GO
Kết quả :
Hình 3
Lúc này ta thấy
- Ở thư mục Columns, có 2 ký hiệu khóa ở cột ProductCode và ProductName chứng tỏ primary key nằm trên 2 cột này
- Ở thư mục Keys cho ta biết Primary key của bảng dbo.Products có tên là PK_Products
Sửa đổi Primary key đã tồn tại trên một bảng
Nếu một bảng đã thực sự tồn tại primary key trên nó, để chỉnh sửa primary key việc đầu tiên bạn phải làm là gỡ primary key cũ và thêm primary key mới
để minh họa quá trình này mình sẽ tiếp tục sử dụng bảng dbo.Products và điều chỉnh PK_Products trên 2 cột là ProductCode, ProductName thành đặt primary key cùng tên và chỉ đặt trên cột ProductCode
USE AZSqlserver
GO
/*gỡ primary key cũ*/
ALTER TABLE Products DROP CONSTRAINT PK_Products
GO
/*tạo primary key mới với cùng tên PK_Products nhưng chỉ đặt trên cột ProductCode*/
ALTER TABLE Products ADD CONSTRAINT PK_Products PRIMARY KEY(ProductCode )
GO
Kết quả :
Hình 4
từ hình 4 ta thấy primary key : PK_Products bây giờ chỉ đặt trên một cột là ProductCode
Các quy tắc khi tạo một Primary key
Sau khi đi qua các ví dụ trên về việc chỉnh sửa và tạo primary key, chúng ta cùng quay lại một số quy tắc đã được nêu ở trên
Một số quy tắc cần lưu ý khi tạo một PRIMARY KEY :
1) Một primary key có thể được tạo ra từ một hoặc nhiều cột của một bảng, nhưng một bảng thì chỉ có thể có một primary key
2) Các cột tạo nên một primary key không được phép chứa giá trị NULL
3) Khi tạo primary key cho bảng, thì lúc đó một index của bảng đó cũng được tạo ra, loại index có thể được định nghĩa bởi người dùng (clustered index hoặc nonclustered index), nếu index không được định nghĩa bởi người dùng thì clustered index sẽ tự động được tạo nếu bảng trước đó chưa có clustered index, hoặc nonclustered index sẽ được tạo nếu bảng trước đó đã có clustered index
Quy tắc số 1 : Một primary key có thể được tạo ra từ một hoặc nhiều cột của một bảng, nhưng một bảng thì chỉ có thể có một primary key
USE AZSqlserver
GO
/*Kiểm tra nếu bảng Products đã tồn tại thì xóa*/
DROP TABLE IF EXISTS dbo.Products
GO
/*Tạo Primary key cùng với bảng dbo.Products*/
CREATE TABLE dbo.Products
(
ProductCode INT
,ProductName VARCHAR(20)
,CONSTRAINT PK_Products PRIMARY KEY (ProductCode)
)
GO
chạy xong câu lệnh trên ta sẽ có một primary key đặt trên cột ProductCode của bảng Products
Tiếp đến ta sẽ tạo thêm một primary key nữa cũng trên bảng dbo.Products xem điều gì sẽ xảy ra
ALTER TABLE Products
ADD CONSTRAINT PK_Products_ProductName PRIMARY KEY(ProductName)
GO
một lỗi xuất hiện :
Msg 1779, Level 16, State 0, Line 12
Table ‘Products’ already has a primary key defined on it.
Msg 1750, Level 16, State 0, Line 12
Could not create constraint or index. See previous errors.
lỗi này báo cho ta biết bảng Products thực sự đã có một primary key (là PK_Products) nên không thể tạo thêm một primary key nữa
Quy tắc số 2 : các cột tạo nên một primary key không được phép chứa giá trị NULL
đầu tiên chúng ta tạo bảng Products và insert một số dữ liệu có giá trị là NULL
USE AZSqlserver
GO
/*Kiểm tra nếu bảng Products đã tồn tại thì xóa*/
DROP TABLE IF EXISTS dbo.Products
GO
CREATE TABLE dbo.Products
(
ProductCode INT
,ProductName VARCHAR(20)
)
GO
INSERT INTO dbo.Products (ProductCode, ProductName)
VALUES (1, 'Product 1'), (NULL, 'Product NULL')
Câu lệnh trên sẽ tạo ra bảng Products với dữ liệu như sau :
Hình 5
giờ ta cùng thêm primary key cho bảng Products trên cột ProductCode xem điều gì sẽ xảy ra
ALTER TABLE Products
ADD CONSTRAINT PK_Products PRIMARY KEY(ProductCode)
GO
Msg 8111, Level 16, State 1, Line 16
Cannot define PRIMARY KEY constraint on nullable column in table ‘Products’.
Msg 1750, Level 16, State 0, Line 16
Could not create constraint or index. See previous errors.
lỗi trên báo cho ta biết không thể tạo PRIMARY KEY trên cột có thể NULL ở bảng Products, để rõ hơn ta hãy nhìn vào cấu trúc bảng này
Hình 6
Ở phần mô tả cho cột ProductCode, ta thấy có INT, NULL trong đó INT là kiểu dữ liệu của cột ProductCode, còn NULL cho ta biết cột này chấp nhận cả những giá trị NULL và không NULL vì thế trong câu lệnh insert phía trên
INSERT INTO dbo.Products (ProductCode, ProductName)
VALUES (1, 'Product 1'), (NULL, 'Product NULL')
giá trị (NULL, ‘Product NULL’) vẫn được chấp nhận
để khắc phục lỗi trên ta cần có một ràng buộc ở cột ProductCode để cho biết cột này không nhận giá trị NULL, mình tạm thời gọi đây là ràng buộc NOT NULL, mà để thêm được ràng buộc NOT NULL này thì dữ liệu hiện tại đang chứa ở cột ProductCode phải không là NULL vì vậy ta phải thực hiện xóa dữ liệu có ProductCode là NULL và thêm ràng buộc NOT NULL cho cột ProductCode, ta dùng câu lệnh sau đây
USE AZSqlserver
GO
/*Xóa dữ liệu không hợp lệ*/
DELETE dbo.Products WHERE ProductCode IS NULL
GO
/*Thêm ràng buộc NOT NULL*/
ALTER TABLE Products
ALTER COLUMN ProductCode INT NOT NULL
GO
sau khi chỉnh sửa xong ta kiểm tra lại cấu trúc bảng
Hình 7
Ở phần mô tả ProductCode có INT, NOT NULL, trong đó INT là kiểu dữ liệu của cột ProductCode , còn NOT NULL cho ta biết cột ProductCode sẽ không chấp nhận giá trị là NULL vì vậy đã đủ điều kiện để ta thêm primary key, giờ ta thêm primary key lại lần nữa
ALTER TABLE Products
ADD CONSTRAINT PK_Products PRIMARY KEY(ProductCode)
GO
Hình 8
ta thành công thêm primary key
Quy tắc số 3 : khi tạo primary key cho bảng, thì lúc đó một index của bảng đó cũng được tạo ra, loại index có thể được định nghĩa bởi người dùng (clustered index hoặc nonclustered index), nếu index không được định nghĩa bởi người dùng thì clustered index sẽ tự động được tạo nếu bảng trước đó chưa có clustered index, hoặc nonclustered index sẽ được tạo nếu bảng trước đó đã có clustered index
chúng ta mở thư mục Indexes trên SQL Server Management Studio
Hình 9
Khi tạo primary key : PK_Products, sẽ có một indexes được tạo cùng, nó có tên giống tên của primary key và có thêm một ghi chú Clustered cho ta biết loại index này là clustered index, bởi vì trước đó bảng Products không có index nên index tạo ra sẽ thuộc loại clustered index như được nêu trong quy tắc số 3
Ngược lại với trường hợp đã tồn tại clustered index trong bảng dbo.Products thì index do primary key sinh ra sẽ là noclustered index, ta cùng đi qua ví dụ sau để rõ hơn
USE AZSqlserver
GO
DROP TABLE IF EXISTS dbo.Products
GO
CREATE TABLE dbo.Products
(
ProductCode INT NOT NULL
,ProductName VARCHAR(20)
)
GO
/*tạo clustered index CIX_ProductCode cho bảng Products*/
CREATE CLUSTERED INDEX CIX_ProductCode ON Products(ProductCode)
GO
kết quả :
Hình 10
tiếp theo ta tạo một primary key trên cột ProductCode bằng câu lệnh sau :
ALTER TABLE Products
ADD CONSTRAINT PK_Products PRIMARY KEY(ProductCode)
GO
Kết quả :
Hình 11
lúc này một index mới đã được tạo ra có tên là PK_Products và có thêm một ghi chú Non-Clustered cho ta biết index này thuộc loại non clustered index
Trong các trường hợp trên index được tạo ra đồng thời cùng với quá trình tạo primary key và không được định nghĩa loại của index cần tạo, trong một số trường hợp bạn muốn kiểm soát loại index được tạo ra thì có thể dùng các câu lệnh sau :
/*tạo clustered index cùng primary key*/
DROP TABLE IF EXISTS dbo.Products1
GO
CREATE TABLE dbo.Products1
(
ProductCode INT NOT NULL PRIMARY KEY CLUSTERED
,ProductName VARCHAR(20)
)
GO
/*tạo non clustered index cùng primary key*/
DROP TABLE IF EXISTS dbo.Products2
GO
CREATE TABLE dbo.Products2
(
ProductCode INT NOT NULL PRIMARY KEY NONCLUSTERED
,ProductName VARCHAR(20)
)
GO
Cách thức hoạt động của primary key
Primary key dùng để chặn không cho trường hợp bị trùng data xảy ra trong bảng dữ liệu, cùng đi qua ví dụ sau để thấy rõ hơn
DROP TABLE IF EXISTS dbo.Products
GO
CREATE TABLE dbo.Products
(
ProductCode INT NOT NULL PRIMARY KEY
,ProductName VARCHAR(20)
)
GO
/*ta tiến hành insert một dòng mới *
INSERT INTO dbo.Products (ProductCode, ProductName)
VALUES (1, 'Product1')
insert lúc này đã thành công, tiếp theo ta insert thêm một dòng có ProductCode = 1
INSERT INTO dbo.Products (ProductCode, ProductName)
VALUES (1, 'Product2')
Msg 2627, Level 14, State 1, Line 20
Violation of PRIMARY KEY constraint ‘PK__Products__2F4E024EBFA7099E’. Cannot insert duplicate key in object ‘dbo.Products’. The duplicate key value is (1).
xuất hiện thông báo lỗi, thông báo cho ta biết đã vi phạm ràng buộc primary key, tên primary key là PK__Products__2F4E024EBFA7099E, đồng thời thông báo trên cũng cho ta biết key value = 1 bị trùng lặp hay nói cách khác ProductCode = 1 đã trùng với ProductCode = 1 của lần insert trước đó