Logo of Vovisoft

Data Integrity and Advanced Query Technique 

Ðể đọc và hiểu bài viết này bạn nên đọc qua Bài 4- Backup and Restore SQL Server Database    

Nói đến Data Integrity là ta nói đến tính toàn vẹn của một database hay nói một cách khác là data chứa trong database phải chính xác và đáng tin cậy. Nếu data chứa trong database không chính xác ta nói database mất tính toàn vẹn (lost data integrity). Trong bài này chúng ta sẽ bàn qua các phương pháp để giữ cho database được toàn vẹn.

Các Phương Pháp Ðảm Bảo Data Integrity

SQL Server dùng một số cách để đảm bảo Data Integrity. Một số cách như Triggers hay Index sẽ được bàn đến trong các bài sau tuy nhiên trong phạm vi bài này chúng ta cũng nói sơ qua các cách trên.

Constraints

Constraints là những thuộc tính (property) mà ta áp đặt lên một table hay một cột để tránh việc lưu dữ liệu không chính xác vào database (invalid data). Thật ra NOT NULL hay DEFAULT cũng được xem là một dạng constraint nhưng chúng ta không bao gồm hai loại này ở đây mà chỉ trình bày 4 loại constraints là Primary Key Constraint, Unique Constraint, Foreign Key Constraint và Check Constraint.

Primary Key Constraint:

Một table thường có một hay nhiều cột có giá trị mang tính duy nhất để xác định một hàng bất kỳ trong table. Ta thường gọi là Primary Key và được tạo ra khi ta Create hay Alter một table với Primary Key Constraint.

Một table chỉ có thể có một Primary Key constraint. Có thể có nhiều cột tham gia vào việc tạo nên một Primary Key, các cột này không thể chứa Null và giá trị trong các cột thành viên có thể trùng nhau nhưng giá trị của tất cả các cột tạo nên Primary Key phải mang tính duy nhất.

Khi một Primary Key được tạo ra một Unique Index sẽ được tự động tạo ra để duy trì tính duy nhất. Nếu trong table đó chưa có Clustered Index thì một Unique + Clustered Index sẽ được tạo ra.

Có thể tạo ra Primary Key Constraints như sau:

	CREATE TABLE Table1
		(Col1 INT PRIMARY KEY,
	 	 Col2 VARCHAR(30)
		)

    hay

	CREATE TABLE Table1
		(Col1 INT,
	 	 Col2 VARCHAR(30),
	 	 CONSTRAINT table_pk PRIMARY KEY (Col1)
		)

Unique Constraint

Bạn có thể tạo Unique Constraint để đảm bảo giá trị của một cột nào đó không bị trùng lập. Tuy Unique Constraint và Primary Key Constraint đều đảm bảo tính duy nhất nhưng bạn nên dùng Unique Constraint trong những trường hợp sau:

Cách tạo ra Unique Constraint cũng tương tự như Primary Key Constraint chỉ việc thay chữ Primary Key thành Unique. SQL Server sẽ tự động tạo ra một non-clustered unique index khi ta tạo một Unique Constraint.

Foreign Key Constraint

Foreign Key là một cột hay một sự kết hợp của nhiều cột được sử dụng để áp đặt mối liên kết data giữa hai table. Foreign key của một table sẽ giữ giá trị của Primary key của một table khác và chúng ta có thể tạo ra nhiều Foreign key trong một table.

Foreign key có thể reference (tham chiếu) vào Primary Key hay cột có Unique Constraints. Foreign key có thể chứa Null. Mặc dù mục đích chính của Foreign Key Constraint là để kiểm soát data chứa trong table có Foreign key (tức table con) nhưng thực chất nó cũng kiểm soát luôn cả data trong table chứa Primary key (tức table cha). Ví dụ nếu ta delete data trong table cha thì data trong table con trở nên "mồ côi" (orphan) vì không thể reference ngược về table cha. Do đó Foreign Key constraint sẽ đảm bảo điều đó không xảy ra. Nếu bạn muốn delete data trong table cha thì trước hết bạn phải drop hay disable Foreign key trong table con trước.

Có thể tạo ra Foreign Key Constraints như sau:

	CREATE TABLE Table1
		(Col1 INT PRIMARY KEY,
	 	 Col2 INT REFERENCES Employees(EmployeeID)
		 )

    hay

	CREATE TABLE Table1
		(Col1 INT PRIMARY KEY,
	 	 Col2 INT,
	 	 CONSTRAINT col2_fk FOREIGN KEY (Col2)
	 	 REFERENCES Employees (EmployeeID)
		 )

Ðôi khi chúng ta cũng cần Disable Foreign Key Constraint trong trường hợp:

Check Constraint

Check Constraint dùng để giới hạn hay kiểm soát giá trị được phép insert vào một cột. Check Constraint giống Foreign Key Constraint ở chỗ nó kiểm soát giá trị đưa vào một cột nhưng khác ở chỗ Foreign Key Constraint dựa trên giá trị ở table cha để cho phép một giá trị được chấp nhận hay không trong khi Check Constraint dựa trên một biểu thức logic (logic expression) để kiểm tra xem một giá trị có hợp lệ không. Ví dụ ta có thể áp đặt một Check Constraint lên cột salary để chỉ chấp nhận tiền lương từ $15000 đến $100000/năm.

Ta có thể tạo ra nhiều Check Constraint trên một cột. Ngoài ra ta có thể tạo một Check Constraint trên nhiều cột bằng cách tạo ra Check Constraint ở mức table (table level).

Có thể tạo ra Check Constraint như sau:

	CREATE TABLE Table1
		(Col1 INT PRIMARY KEY,
	 	 Col2 INT
	 	 CONSTRAINT limit_amount CHECK (Col2 BETWEEN 0 AND 1000),
	 	 Col3 VARCHAR(30)
		 )

Trong ví dụ này ta giới hạn giá trị chấp nhận được của cột Col2 từ 0 đến 1000. Ví dụ sau sẽ tạo ra một Check Constraint giống như trên nhưng ở table level:

	CREATE TABLE Table1
		(Col1 INT PRIMARY KEY,
	 	 Col2 INT,
	 	 Col3 VARCHAR(30),
 	 	 CONSTRAINT limit_amount CHECK (Col2 BETWEEN 0 AND 1000)
		 )

Tương tự như Foreign Key Constraint đôi khi ta cũng cần disable Check Constraint trong trường hợp Insert hay Update mà việc kiểm soát tính hợp lệ của data không áp dụng cho data hiện tại. Trường hợp thứ hai là replication.

Muốn xem hay tạo ra Constraint bằng Enterprise Manager thì làm như sau:

Click lên trên một table nào đó và chọn Design Table-> Click vào icon bên phải "Manage Constraints..."

Advanced Query Techniques

Trong phần này chúng ta sẽ đào sâu một số câu lệnh nâng cao như SELECT, INSERT...

Có thể nói hầu như ai cũng biết qua câu lệnh căn bản kiểu như "SELECT * FROM TABLENAME WHERE..." nhưng có thể có nhiều người không biết đến những tính chất nâng cao của nó.

Cú pháp đầy đủ của một câu lệnh SELECT rất phức tạp tuy nhiên ở đây chỉ trình bày những nét chính của lệnh này mà thôi:

SELECT select_list
[ INTO new_table ]
FROM table_source [ WHERE search_condition ]
[ GROUP BY group_by_expression ]
[ HAVING search_condition ]
[ ORDER BY order_expression [ ASC | DESC ] ]

Chúng ta sẽ lần lượt nghiên cứu từng clause (mệnh đề) trong câu lệnh này.

SELECT Clause

Sau keyword (từ khóa) SELECT ta sẽ có một danh sách các cột mà ta muốn select được cách nhau bằng dấu ",". Có 3 Keywords cần nhấn mạnh trong phần SELECT.

	SELECT DISTINCT ShipCity, ShipRegion
	FROM Orders
	ORDER BY ShipCity
	SELECT DISTINCT TOP 10 ShipCity, ShipRegion
	FROM Orders
	ORDER BY ShipCity
	USE pubs
	SELECT p.pub_id, p.pub_name AS PubName
	FROM publishers AS p

Ngoài ra trong Select list ta có thể select dưới dạng một expression như sau:

	SELECT FirstName + ' ' + LastName AS "Employee Name", 
		IDENTITYCOL AS "Employee ID",
		HomePhone,
		Region
	FROM Northwind.dbo.Employees
	ORDER BY LastName, FirstName ASC

Trong ví dụ trên ta select cột "Employee Name" là sản phẩm ghép lại của cột FirstName và LastName được cách nhau bằng một khoảng trắng. Một  giá trị thuộc loại identity để làm cột "Employee ID". Kết quả sẽ được sắp theo thứ tự từ nhỏ tới lớn (ASC) (còn DESC là từ lớn tới nhỏ) trong đó cột LastName được sắp trước rồi mới tới cột FirstName.

The INTO Clause

INTO Clause cho phép ta select data từ một hay nhiều table sau đó kết quả sẽ được insert vào một table mới. Table này được tạo ra do kết quả của câu lệnh SELECT INTO. Ví dụ:

	SELECT FirstName, LastName
	INTO EmployeeNames
	FROM Employers

Câu lệnh tên sẽ tạo ra một table mới có tên là EmployeeNames với 2 cột là FirstName và LastName sau đó kết quả select được từ table Employers sẽ được insert vào table mới này. Nếu table EmployeeNames tồn tại SQL Server sẽ báo lỗi. Câu lệnh này thường hay được sử dụng để select một lượng data lớn từ nhiều table khác nhau vào một table mới (thường dùng cho mục đích tạm thời (temporary table)) mà khỏi phải thực thi câu lệnh Insert nhiều lần.

Một cách khác cũng select data từ một hay nhiều table và insert vào một table khác là dùng "Insert Into...Select...". Nhưng câu lệnh này không tạo ra một table mới. Nghĩa là ta table đó phải tồn tại trước. Ví dụ:

	INSERT INTO EmployeeNames
	SELECT FirstName, LastName
	FROM Employers

Chú ý là không có chữ "Value" trong câu Insert này.

The GROUP BY and HAVING Clauses

GROUP BY dùng để tạo ra các giá trị tổng (aggregate values) cho từng hàng trong kết quả select được. Chỉ có một hàng cho từng giá trị riêng biệt (distinct) của từng cột. Các cột được select đều phải nằm trong GROUP BY Clause. Hãy xem ví dụ phức tạp sau:

	SELECT OrdD1.OrderID AS OrderID,
		SUM(OrdD1.Quantity) AS "Units Sold",
		SUM(OrdD1.UnitPrice * OrdD1.Quantity) AS Revenue
	FROM [Order Details] AS OrdD1
	WHERE OrdD1.OrderID in (SELECT DISTINCT OrdD2.OrderID
				FROM [Order Details] AS OrdD2
				WHERE OrdD2.UnitPrice > $100)
	GROUP BY OrdD1.OrderID
	HAVING SUM(OrdD1.Quantity) > 100

Trong ví dụ trên đầu tiên ta select những order riêng biệt (distinct) từ Order Details table với giá > 100. Sau đó tiếp tục select OrderID, "Units Sold", Revenue từ kết quả trên trong đó "Units Sold" và Revenue là những aggregate columns (cho giá trị tổng một cột của những hàng có cùng OrderID). HAVING Clause đóng vai trò như một filter dùng để lọc lại các giá trị cần select mà thôi. HAVING Clause thường đi chung với GROUP BY mặc dù có thể xuất hiện riêng lẻ.

UNION

Uninon keyword có nhiệm vụ ghép nối kết quả của 2 hay nhiều queries lại thành một kết quả.

Ví dụ:

Giả sử có table1(ColumnA varchar(10), ColumnB  int) và table2(ColumnC varchar(10), ColumnD  int). Ta muốn select data từ table1 và ghép với data từ table2 để tạo thành một kết quả duy nhất ta làm như sau:

	SELECT * FROM Table1
	UNION ALL
	SELECT * FROM Table2

Nếu không có keyword ALL thì những hàng giống nhau từ 2 table sẽ chỉ xuất hiện một lần trong kết quả. Còn khi dùng ALL thì các hàng trong 2 table đều có trong kết quả bất chấp việc lập lại.

Khi Dùng Union phải chú ý hai chuyện: số cột select ở 2 queries phải bằng nhau và data type của các cột tương ứng phải compatible (tương thích).

Using JOINS

Trong phần này chúng ta sẽ tìm hiểu về các loại Join trong SQL Server. Bằng cách sử dụng JOIN bạn có thể select data từ nhiều table dựa trên mối quan hệ logic giữa các table (logical relationships). Có thể tóm tắt các loại Join thông dụng bằng các hình sau:

Thứ tự từ trái sang phải: Inner Join, Left Outer Join, Right Outer Join, Full Outer Join

Inner Joins

Dùng Inner Join để select data từ 2 hay nhiều tables trong đó giá trị của các cột được join phải xuất hiện ở  cả 2 tables tức là phần gạch chéo trên hình. Ví dụ:

	SELECT t.Title, p.Pub_name
	FROM Publishers AS p INNER JOIN Titles AS t
	ON p.Pub_id = t.Pub_id
	ORDER BY Title ASC

Left Outer Joins

Dùng Left Outer Join để select data từ 2 hay nhiều tables trong đó tất cả cột bên table thứ nhất và không tồn tại bên table thứ hai sẽ được select cộng với các giá trị của các cột được inner join. Số cột select được sẽ bằng với số cột của table thứ nhất. Tức là phần tô màu đỏ trên hình.  Ví dụ:

	USE Pubs
	SELECT a.Au_fname, a.Au_lname, p.Pub_name
	FROM Authors a LEFT OUTER JOIN Publishers p
	ON a.City = p.City
	ORDER BY p.Pub_name ASC, a.Au_lname ASC, a.Au_fname ASC

Right Outer Joins

Dùng Right Outer Join để select data từ 2 hay nhiều tables trong đó tất cả cột bên table thứ hai và không tồn tại bên table thứ nhất sẽ được select cộng với các giá trị của các cột được inner join. Số cột select được sẽ bằng với số cột của table thứ hai. Tức là phần tô màu đỏ trên hình.  Ví dụ:

	USE Pubs
	SELECT a.Au_fname, a.Au_lname, p.Pub_name
	FROM Authors a RIGHT OUTER JOIN Publishers p
	ON a.City = p.City
	ORDER BY p.Pub_name ASC, a.Au_lname ASC, a.Au_fname ASC

Full Outer Joins

Dùng Full Outer Join để select data từ 2 hay nhiều tables trong đó tất cả cột bên table thứ nhất và thứ hai đều được chọn các giá trị bên hai table bằng nhau thì chỉ lấy một lần. Tức là phần tô màu đỏ trên hình.  Ví dụ:

	USE Pubs
	SELECT a.Au_fname, a.Au_lname, p.Pub_name
	FROM Authors a FULL OUTER JOIN Publishers p
	ON a.City = p.City
	ORDER BY p.Pub_name ASC, a.Au_lname ASC, a.Au_fname ASC

Cross Joins

Dùng Cross Join ghép data từ hai table trong đó số hàng thu được bằng với số hàng của table thứ nhất nhân với số hàng của table thứ hai.  Ví dụ:

	USE pubs
	SELECT au_fname, au_lname, pub_name
	FROM authors CROSS JOIN publishers 
	WHERE authors.city = publishers.city
	ORDER BY au_lname DESC

Ðể ý là trong câu lệnh này không có keyword "On".

Muốn hiểu rõ hơn về các loại join bạn cho chạy thử trên SQL Server và làm phần bài tập số 1.

Tóm lại trong bài này chúng ta đã tìm hiểu data integrity trong SQL Server bằng cách dùng các loại Constraint. Ngoài ra Chúng ta cũng biết qua về một số kỹ thuật query nâng cao. Sau bài học này các bạn cần làm bài tập số 1 để hệ thống hóa lại kiến thức đã học từ bài 1 đến bài 5 trước khi bạn học tiếp bài số 6. Khi làm bài tập nhớ phải làm theo thứ tự và tuân thủ theo các yêu cầu của bài tập đặt ra. Không nên bỏ qua bước nào.

Bài 6 Stored Procedure and Advanced T-SQL

  Học SQL Server 2000