Truy vấn đệ quy mysql

CTE có thể được xem như một bảng chứa dữ liệu tạm thời tương tự như một bảng dẫn xuất (bảng dẫn xuất) tại chỗ nó không được lưu trữ dưới dạng một đối tượng và chỉ kéo dài trong suốt thời gian của câu truy vấn. Không giống như bảng dẫn xuất, CTE có thể tự tham chiếu tới bản thân của nó và có thể tham chiếu nhiều lần trong một câu truy vấn

Việc lưu trữ dữ liệu một cách tạm thời sẽ giúp chúng ta chia nhỏ một lệnh truy vấn SQL phức tạp thành những phần nhỏ hơn để giải quyết. Sau đó chúng ta sẽ viết lại những phần này để giải quyết một vấn đề tổng thể có thể tốt hơn

Cứ hình dung như khi bạn giải các phương trình toán học phức tạp, chúng ta thường sẽ chia nhỏ các bài toán thành nhiều phần nhỏ khác nhau để giải quyết. Và kết quả của mỗi phần nhỏ này sẽ được lưu vào một biến và biến này có thể được sử dụng lại ở các phần sau
CTE cũng hoạt động với nguyên tắc tương tự và nó sẽ giúp chúng tôi lưu kết quả từ truy vấn lệnh của mình và được sử dụng lại sau đó

Nếu các bạn từng học qua lập trình, việc gọi tên bảng CTE cũng khá giống với việc gọi một hàm (hàm) được khai báo sẵn. Khác nhau ở chỗ là bảng CTE sẽ chỉ thực hiện một lần và lưu kết quả vào bộ nhớ tạm thời, còn chức năng trong các ngôn ngữ khác nhau sẽ thực hiện mỗi lần bạn sử dụng

Mục đích của CTE

CTE được sinh ra để giúp giải quyết các vấn đề

  • Tạo truy vấn đệ quy (recursive query)
  • Thay thế Xem trong một số trường hợp
  • Cho phép nhóm một cột từ truy vấn con
  • Tham chiếu tới kết quả bảng nhiều lần trong cùng một lệnh

Ưu điểm của CTE

CTE có nhiều ưu điểm như khả năng đọc dữ liệu được cải thiện một cách thiện chí và dễ dàng bảo trì các truy vấn phức tạp. Các truy vấn có thể được phân chia thành các khối nhỏ, đơn giản. Những khối này được sử dụng để xây dựng các CTE phức tạp hơn cho đến khi tập hợp kết quả cuối cùng được tạo ra
CTE could not be default in function, store procedure, view, trigger

Những lợi ích của lệnh VỚI CTE

Việc mình ra lệnh VỚI là một trong những mệnh lệnh quan trọng nhất dựa trên những lợi ích mà nó mang lại cho chúng ta. Chúng ta sẽ đi phân tích từng lợi ích một

Tối ưu hóa bộ nhớ và tốc độ

Như mình nói về phía trên, các bảng CTE có thể được sử dụng lại nhiều lần và ở nhiều nơi khác nhau. Chính đặc tính này sẽ giúp chúng ta quản lý tốt hơn bộ nhớ và tốc độ xử lý

Để hiểu rõ hơn vấn đề chúng ta cùng xem đoạn mã sau

select a.*,b.total_sales
	from superstore.orders as a 
		left join (select Order_ID, sum(Sales) as total_sales 
			from superstore.orders group by  Order_ID) as b
		-- lệnh lồng ghép
		on a.Order_ID = b.order_id
	Limit 100;

Chúng ta có thể thấy rằng mình hoàn toàn có thể đưa ra kết quả tương tự nhưng không cần đến CTE. Nhưng nếu nhìn kỹ hơn chúng ta sẽ thấy rằng ở câu lệnh này hệ thống sẽ kết nối với máy chủ 2 lần để lấy dữ liệu. Một lần ở dòng thứ 2 và một lần còn lại ở dòng thứ 4

Việc tạo nhiều kết nối đến cùng một bảng dữ liệu sẽ tiêu tốn khá nhiều tài nguyên. Nếu chúng ta có một bảng dữ liệu lên đến hàng Terabyte (TB) và truy xuất chúng nhiều lần thì tổng lượng tài nguyên tiêu thụ sẽ rất lớn. Hơn nữa, việc kết nối với máy chủ nhiều lần cũng tiêu tốn khá nhiều thời gian

Trong trường hợp này, việc sử dụng bảng CTE sẽ giúp lưu trữ toàn bộ dữ liệu cần thiết trong bộ nhớ tạm thời và sử dụng lại bất cứ lúc nào. Chúng giúp giảm thiểu rất nhiều tài nguyên và thời gian thực thi lệnh truy vấn

Giảm độ phức tạp của truy vấn

Nhìn lại câu lệnh số 2. Ở phần LEFT JOIN chúng ta sử dụng một Sub-query để lấy dữ liệu. Trong các lệnh đơn giản, việc sử dụng truy vấn phụ sẽ khá thuận tiện. Nhưng trong những trường hợp phức tạp, chúng ta khó có thể kiểm soát được những tiểu truy vấn này
Hãy thử cấu hình dung trong trường hợp chúng ta cần lấy dữ liệu từ 5 hoặc 10 bảng dữ liệu khác nhau. Và toàn bộ bộ đồng bộ cần được xử lý trước khi gộp lại thành một bảng. Trong trường hợp này, việc sử dụng sub-query sẽ dễ phát sinh lỗi vì chúng ta khó kiểm soát được code khi bỏ chung lại với nhau

Nhưng vấn đề sẽ dễ dàng hơn nhiều nếu chúng ta xử lý 10 bảng dữ liệu này và lưu và 10 bảng CTE khác nhau. Việc xử lý các bảng dữ liệu riêng có thể sẽ luôn dễ dàng hơn là chia lại một nơi. Đến cuối cùng chúng ta chỉ cần tham gia các bảng dữ liệu đã qua xử lý. Nếu có phát sinh lỗi, chúng ta cũng sẽ dễ dàng kiểm tra và chỉnh sửa hơn

Dễ đọc và dễ hiểu

Việc tách bài toán lớn thành những phần nhỏ sẽ giúp mình hay người khác dễ đọc và hiểu mục đích mình muốn làm. Trên thực tế, sẽ không ít lần bạn được yêu cầu đọc mã của người khác hoặc mã đọc của người khác của bạn

Sử dụng CTE sẽ giúp chúng ta dễ dàng hơn trong việc truyền tải mục đích của mình cũng như logic của cả câu lệnh. Thông qua việc đặt tên bảng và ghi chú cho từng bảng, chúng ta sẽ giúp người khác hiểu ý nghĩa của nó dễ dàng hơn. Ngoài ra chỉ là để chính bạn đọc mã của mình sau 2-3 năm

Tăng tính linh hoạt mà truy vấn phụ không có

Không phải tất cả các trường hợp truy vấn phụ đều có thể thay thế bằng. Sẽ có nhiều trường hợp mà bạn buộc phải sử dụng with để xử lý vì truy vấn phụ không hỗ trợ điều này. Một trong những trường hợp này là khi bạn muốn xử lý dữ liệu trùng lặp (duplicate) phát sinh do quá trình tham gia các bảng

Trong trường hợp hợp lệ trên, chúng ta có thể cần đến hàm row_number() trong MySQL để chọn ra giá trị muốn giữ lại. Những công việc này gần như rất khó hoặc bất khả thi nếu chỉ sử dụng sub_query. Mình sẽ hướng dẫn sử dụng hàm Row_number() và các ví dụ sau của bài viết
Trên đây là những lợi ích chính mà Common Table Expression sẽ mang lại cho bạn trong quá trình sử dụng MySQL. Bây giờ chúng ta sẽ tìm hiểu xem một vài trường hợp mà chúng ta có thể sử dụng với

Khả năng lồng ghép câu lệnh dưới dạng Truy vấn phụ

Có một điều khá thú vị ở CTE là các bạn có thể lồng ghép chúng với nhau theo nhiều cấp độ khác nhau. Mặc dù rằng mình không khuyến khích việc làm này trên thực tế vì nó sẽ làm tăng mức độ phức tạp của câu lệnh. Các bạn hoàn toàn có thể sử dụng nếu thấy phù hợp

CTE start up

Chúng ta có thể định nghĩa một CTE với mệnh đề VỚI

[WITH  [, ...]]  
 
::=
cte_name [(column_name [, ...])]
AS (cte_query) 

Ví dụ

USE AdventureWorks2008R2;
GO
-- Define the CTE expression name and column list.
WITH Sales_CTE (SalesPersonID, SalesOrderID, SalesYear)
AS
-- Define the CTE query.
(
    SELECT SalesPersonID, SalesOrderID, YEAR(OrderDate) AS SalesYear
    FROM Sales.SalesOrderHeader
    WHERE SalesPersonID IS NOT NULL
)
-- Define the outer query referencing the CTE name.
SELECT SalesPersonID, COUNT(SalesOrderID) AS TotalSales, SalesYear
FROM Sales_CTE
GROUP BY SalesYear, SalesPersonID
ORDER BY SalesPersonID, SalesYear;
GO

Tạo biểu thức bảng chung đệ quy

Một CTE đệ quy là một CTE tự tham chiếu đến chính nó. CTE đệ quy hữu ích khi làm việc với dữ liệu phân cấp vì CTE tiếp tục thực thi cho đến khi truy vấn trả về toàn bộ phân cấp

Ví dụ tạo một bảng Nhân viên

CREATE TABLE Employees
(
  EmployeeID int NOT NULL PRIMARY KEY,
  FirstName varchar(50) NOT NULL,
  LastName varchar(50) NOT NULL,
  ManagerID int NULL
)

INSERT INTO Employees VALUES (1, 'Ken', 'Thompson', NULL)
INSERT INTO Employees VALUES (2, 'Terri', 'Ryan', 1)
INSERT INTO Employees VALUES (3, 'Robert', 'Durello', 1)
INSERT INTO Employees VALUES (4, 'Rob', 'Bailey', 2)
INSERT INTO Employees VALUES (5, 'Kent', 'Erickson', 2)
INSERT INTO Employees VALUES (6, 'Bill', 'Goldberg', 3)
INSERT INTO Employees VALUES (7, 'Ryan', 'Miller', 3)
INSERT INTO Employees VALUES (8, 'Dane', 'Mark', 5)
INSERT INTO Employees VALUES (9, 'Charles', 'Matthew', 6)
INSERT INTO Employees VALUES (10, 'Michael', 'Jhonson', 6) 

Sau khi tạo bảng Nhân viên, sau câu lệnh CHỌN, đứng trước mệnh đề VỚI bao gồm một CTE có tên cteReports được tạo

WITH
  cteReports (EmpID, FirstName, LastName, MgrID, EmpLevel)
  AS
  (
    SELECT EmployeeID, FirstName, LastName, ManagerID, 1
    FROM Employees
    WHERE ManagerID IS NULL
    UNION ALL
    SELECT e.EmployeeID, e.FirstName, e.LastName, e.ManagerID, 
      r.EmpLevel + 1
    FROM Employees e
      INNER JOIN cteReports r
        ON e.ManagerID = r.EmpID
  )
SELECT
  FirstName + ' ' + LastName AS FullName, 
  EmpLevel,
  (SELECT FirstName + ' ' + LastName FROM Employees 
    WHERE EmployeeID = cteReports.MgrID) AS Manager
FROM cteReports 

Vì vậy, CTE có thể là một công cụ hữu ích khi bạn cần tạo các tệp kết quả tạm thời có thể truy cập được bằng lệnh SELECT, INSERT, UPDATE, DELETE hoặc MERGE