Transact-SQL hoặc T-SQL, là một phương ngữ của ngôn ngữ SQL tiêu chuẩn ANSI được sử dụng bởi các sản phẩm và dịch vụ Microsoft SQL. Nó tương tự như SQL tiêu chuẩn. Phần lớn trọng tâm của chúng ta sẽ là câu lệnh SELECT, câu lệnh này có nhiều tùy chọn và biến thể nhất của bất kỳ câu lệnh DML nào.
Hãy bắt đầu bằng cách xem cách một câu lệnh SELECT được xử lý. Thứ tự mà câu lệnh SELECT được viết không phải là thứ tự mà nó được đánh giá và xử lý bởi công cụ cơ sở dữ liệu SQL Server.
Các nội dung chính
Câu lệnh SELECT
Hãy xem ví dụ truy vấn sau:
SELECT OrderDate, COUNT(OrderID) AS Orders
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
ORDER BY OrderDate DESC;
Truy vấn bao gồm một câu lệnh SELECT, bao gồm nhiều mệnh đề, mỗi mệnh đề xác định một hoạt động cụ thể phải được áp dụng cho dữ liệu đang được truy xuất. Trước khi xem xét thứ tự thời gian chạy của các hoạt động, chúng ta hãy xem xét sơ qua những gì truy vấn này thực hiện, mặc dù chi tiết của các mệnh đề khác nhau sẽ không được đề cập trong module này.
Mệnh đề SELECT trả về cột OrderDate và số lượng giá trị OrderID, được gán tên (hoặc bí danh) Đơn hàng:
SELECT OrderDate, COUNT(OrderID) AS Orders
Mệnh đề FROM xác định bảng nào là nguồn của các hàng cho truy vấn; trong trường hợp này, đó là bảng Sales.SalesOrder:
FROM Sales.SalesOrder
Mệnh đề WHERE lọc các hàng ra khỏi kết quả, chỉ giữ lại những hàng thỏa coden điều kiện đã chỉ định; trong trường hợp này, các đơn đặt hàng có trạng thái “đã giao hàng”:
WHERE Status = 'Shipped'
Mệnh đề GROUP BY lấy các hàng đáp ứng điều kiện bộ lọc và nhóm chúng theo Ngày đặt hàng, để tất cả các hàng có cùng Ngày đặt hàng được coi là một nhóm duy nhất và một hàng sẽ được trả lại cho mỗi nhóm:
GROUP BY OrderDate
Sau khi các nhóm được thành lập, mệnh đề HAVING lọc các nhóm dựa trên vị từ của chính nó. Chỉ những ngày có nhiều thứ tự mới được đưa vào kết quả:
HAVING COUNT(OrderID) > 1
Với mục đích xem trước truy vấn này, mệnh đề cuối cùng là ORDER BY, sắp xếp đầu ra theo thứ tự giảm dần của OrderDate:
ORDER BY OrderDate DESC;
Chúng ta sẽ xem kỹ hơn các mệnh đề trong câu lệnh SQL được xử lý theo thứ tự như thế nào:
- Mệnh đề
FROM
được đánh giá đầu tiên, để cung cấp các hàng nguồn cho phần còn lại của câu lệnh. Một bảng ảo được tạo và chuyển sang bước tiếp theo. - Mệnh đề
WHERE
tiếp theo sẽ được đánh giá, lọc các hàng đó khỏi bảng nguồn khớp với một vị từ. Bảng ảo đã lọc được chuyển sang bước tiếp theo. - Tiếp theo là
GROUP BY
, sắp xếp các hàng trong bảng ảo theo các giá trị duy nhất được tìm thấy trong danh sáchGROUP BY.
Một bảng ảo mới được tạo, chứa danh sách các nhóm và được chuyển sang bước tiếp theo. Từ thời điểm này trong luồng hoạt động, chỉ các cột trong danh sáchGROUP BY
hoặc các hàm tổng hợp mới có thể được tham chiếu bởi các phần tử khác. - Mệnh đề
HAVING
được đánh giá tiếp theo, lọc ra toàn bộ nhóm dựa trên vị từ của nó. Bảng ảo được tạo ở bước 3 được lọc và chuyển sang bước tiếp theo. - Mệnh đề
SELECT
cuối cùng được thực thi, xác định cột nào sẽ xuất hiện trong kết quả truy vấn. Vì mệnh đề SELECT được đánh giá sau các bước khác, bất kỳ bí danh cột nào (column alias) (trong ví dụ là Orders) được tạo ở đó không thể được sử dụng trong mệnh đềGROUP BY
hoặcHAVING.
- Mệnh đề
ORDER BY
là mệnh đề cuối cùng được thực thi, sắp xếp các hàng được xác định bởi danh sách cột của nó.
Để áp dụng cách hiểu này cho truy vấn mẫu, đây là thứ tự logic thời điểm chạy của câu lệnh SELECT ở trên:
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
SELECT OrderDate, COUNT(OrderID) AS Orders
ORDER BY OrderDate DESC;
Không phải tất cả các mệnh đề khả thi đều được yêu cầu trong mọi câu lệnh SELECT mà bạn viết. Mệnh đề bắt buộc duy nhất là mệnh đề SELECT, có thể được sử dụng riêng trong một số trường hợp. Thông thường, một mệnh đề FROM cũng được bao gồm để xác định bảng đang được truy vấn. Ngoài ra, Transact-SQL có các mệnh đề khác có thể được thêm vào.
Như bạn đã thấy, bạn không viết các truy vấn T-SQL theo cùng một thứ tự mà chúng được đánh giá một cách logic. Thứ tự thời gian chạy của đánh giá xác định dữ liệu nào có sẵn cho các mệnh đề nào, vì một mệnh đề chỉ có quyền truy cập vào thông tin đã có sẵn từ một mệnh đề đã được xử lý. Vì lý do này, điều quan trọng là phải hiểu thứ tự xử lý logic thực sự khi viết truy vấn.
Ví dụ về truy vấn SELECT cơ bản
Mệnh đề SELECT thường được gọi là danh sách SELECT, vì nó liệt kê các giá trị được trả về trong kết quả của truy vấn.
Chọn tất cả các cột
Dạng đơn giản nhất của mệnh đề SELECT là sử dụng ký tự dấu hoa thị (*) để trả về tất cả các cột. Khi được sử dụng trong các truy vấn T-SQL, nó được gọi là dấu sao. Mặc dù SELECT * phù hợp để kiểm tra nhanh, bạn nên tránh sử dụng nó vì những lý do sau:
- Các thay đổi đối với bảng thêm hoặc sắp xếp lại các cột sẽ được phản ánh trong kết quả truy vấn, điều này có thể dẫn đến kết quả không mong muốn cho các ứng dụng hoặc báo cáo sử dụng truy vấn.
- Việc trả lại dữ liệu không cần thiết có thể làm chậm các truy vấn của bạn và gây ra các vấn đề về hiệu suất nếu bảng nguồn chứa một số lượng lớn các hàng.
Ví dụ, ví dụ sau đây truy xuất tất cả các cột từ bảng Production.Product.
SELECT * FROM Production.Product;
Kết quả từ truy vấn này là một tập hợp hàng chứa tất cả các cột cho tất cả các hàng của bảng, có thể trông giống như sau:
ProductID | Name | ProductNum | Color | StandardCost | ListPrice | Size | Weight | ProductCatID |
680 | HL Road Frame – Black, 58 | FR-R92B-58 | Black | 1059.31 | 1431.5 | 58 | 1016.04 | 18 |
706 | HL Road Frame – Red, 58 | FR-R92R-58 | Red | 1059.31 | 1431.5 | 58 | 1016.04 | 18 |
707 | Sport-100 Helmet, Red | HL-U509-R | Red | 13.0863 | 34.99 | 35 | ||
708 | Sport-100 Helmet, Black | HL-U509 | Black | 13.0863 | 34.99 | 35 | ||
… | … | … | … | … | … | … | … | … |
Chọn một cột cụ thể
Danh sách cột rõ ràng cho phép bạn kiểm soát chính xác cột nào được trả về và theo thứ tự nào. Mỗi cột trong kết quả sẽ có tên của cột làm tiêu đề.
Ví dụ, hãy xem xét truy vấn sau đây; mà lại sử dụng bảng Production.Product giả định.
SELECT ProductID, Name, ListPrice, StandardCost FROM Production.Product;
Lần này, kết quả chỉ bao gồm các cột được chỉ định:
ProductID | Name | ListPrice | StandardCost |
680 | HL Road Frame – Black, 58 | 1431.5 | 1059.31 |
706 | HL Road Frame – Red, 58 | 1431.5 | 1059.31 |
707 | Sport-100 Helmet, Red | 34.99 | 13.0863 |
708 | Sport-100 Helmet, Black | 34.99 | 13.0863 |
… | … | … | … |
Chọn hàm
Ngoài việc truy xuất các cột được lưu trữ trong bảng được chỉ định, mệnh đề SELECT có thể thực hiện các phép tính và thao tác, sử dụng toán tử để kết hợp các cột và giá trị hoặc nhiều cột. Kết quả của phép tính hoặc thao tác phải là kết quả có giá trị đơn (vô hướng) sẽ xuất hiện trong kết quả dưới dạng một cột riêng biệt.
Ví dụ: truy vấn sau bao gồm hai biểu thức:
SELECT ProductID,
Name + '(' + ProductNumber + ')',
ListPrice - StandardCost
FROM Production.Product;
Kết quả:
ProductID | ||
680 | HL Road Frame – Black, 58(FR-R92B-58) | 372.19 |
706 | HL Road Frame – Red, 58(FR-R92R-58) | 372.19 |
707 | Sport-100 Helmet, Red(HL-U509-R) | 21.9037 |
708 | Sport-100 Helmet, Black(HL-U509) | 21.9037 |
… | … | … |
Có một số điều thú vị cần lưu ý về những kết quả này:
- Các cột được trả về bởi hai biểu thức không có tên cột. Tùy thuộc vào công cụ bạn đang sử dụng để gửi truy vấn của mình, tên cột bị thiếu có thể được biểu thị bằng tiêu đề cột trống, chỉ báo “không có tên cột” theo nghĩa đen hoặc tên mặc định như column1. Chúng ta sẽ xem cách chỉ định bí danh cho tên cột trong truy vấn ở phần sau.
- Biểu thức đầu tiên sử dụng toán tử + để nối các giá trị chuỗi (dựa trên ký tự), trong khi biểu thức thứ hai sử dụng toán tử – để trừ một giá trị số cho một giá trị khác. Khi được sử dụng với các giá trị số, toán tử + thực hiện phép cộng. Vì vậy, rõ ràng, điều quan trọng là phải hiểu các kiểu dữ liệu của các cột mà bạn đưa vào biểu thức. Chúng ta sẽ thảo luận về các loại dữ liệu trong phần tiếp theo.
Chỉ định bí danh cột (alias)
Bạn có thể chỉ định một bí danh cho mỗi cột được trả về bởi truy vấn SELECT, như một sự thay thế cho tên cột nguồn hoặc để gán một tên cho đầu ra của một biểu thức.
Ví dụ: đây là cùng một truy vấn như trước đây, nhưng với các bí danh được chỉ định cho từng cột:
SELECT ProductID AS ID,
Name + '(' + ProductNumber + ')' AS ProductName,
ListPrice - StandardCost AS Markup
FROM Production.Product;
Kết quả từ truy vấn này bao gồm các tên cột được chỉ định:
ID | ProductName | Markup |
680 | HL Road Frame – Black, 58(FR-R92B-58) | 372.19 |
706 | HL Road Frame – Red, 58(FR-R92R-58) | 372.19 |
707 | Sport-100 Helmet, Red(HL-U509-R) | 21.9037 |
708 | Sport-100 Helmet, Black(HL-U509) | 21.9037 |
… | … | … |
Lưu ý: Từ khóa AS là tùy chọn khi chỉ định alias, nhưng bạn nên đưa nó vào để làm rõ.
Định dạng truy vấn (Formating queries)
Bạn có thể lưu ý từ các ví dụ trong phần này rằng bạn có thể linh hoạt về cách bạn định dạng code truy vấn của mình. Ví dụ: bạn có thể viết mỗi mệnh đề (hoặc toàn bộ truy vấn) trên một dòng hoặc ngắt nó thành nhiều dòng. Trong hầu hết các hệ thống cơ sở dữ liệu, code không phân biệt chữ hoa chữ thường và một số phần tử của ngôn ngữ T-SQL là tùy chọn (bao gồm từ khóa AS như đã đề cập trước đây và thậm chí cả dấu chấm phẩy ở cuối câu lệnh).
Có một số nguyên tắc viết code T-SQL để làm cho code T-SQL của bạn dễ đọc (và do đó dễ hiểu và dễ sửa lỗi hơn):
- Viết hoa các từ khóa T-SQL, như SELECT, FROM, AS, v.v. Viết hoa từ khóa là một quy ước thường được sử dụng để giúp tìm kiếm từng mệnh đề của một câu lệnh phức tạp dễ dàng hơn.
- Bắt đầu một dòng mới cho mỗi mệnh đề chính của một câu lệnh.
- Nếu danh sách SELECT chứa nhiều cột, biểu thức hoặc bí danh, hãy xem xét liệt kê từng cột trên dòng riêng của nó.
- Nếu danh sách SELECT chứa nhiều hơn một vài cột, biểu thức hoặc bí danh, hãy xem xét liệt kê từng cột trên dòng riêng của nó.
- Thụt lề dòng có chứa các tiêu đề phụ hoặc cột để làm rõ code nào thuộc về mỗi mệnh đề chính.
Như vậy, bạn đã hiểu được câu lệnh SELECT được thực thi như thế nào và nguyên tắc của nó là gì. Bạn có thể đọc tài liệu của Microsoft về câu lệnh SELECT nhé!