Ví dụ về CTE và đệ quy của SQL Server


109

Tôi không bao giờ sử dụng CTE với đệ quy. Tôi chỉ đang đọc một bài báo về nó. Bài viết này hiển thị thông tin nhân viên với sự trợ giúp của CTE máy chủ Sql và đệ quy. Về cơ bản, nó hiển thị thông tin nhân viên và người quản lý của họ. Tôi không thể hiểu cách hoạt động của truy vấn này. Đây là truy vấn:

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
ORDER BY EmpLevel, MgrID

Ở đây tôi đăng về cách hiển thị đầu ra: nhập mô tả hình ảnh ở đây

Tôi chỉ cần biết cách nó hiển thị với người quản lý đầu tiên và sau đó là cấp dưới của anh ta trong một vòng lặp. Tôi đoán câu lệnh sql đầu tiên chỉ kích hoạt một lần và điều đó trả về tất cả id nhân viên.

Và truy vấn thứ hai liên tục kích hoạt, truy vấn cơ sở dữ liệu mà nhân viên tồn tại với id người quản lý hiện tại.

Vui lòng giải thích cách câu lệnh sql thực thi trong vòng lặp nội bộ và cũng cho tôi biết thứ tự thực hiện lệnh sql. Cảm ơn.

Giai đoạn câu hỏi thứ 2 của TÔI

;WITH Numbers AS
(
    SELECT n = 1
    UNION ALL
    SELECT n + 1
    FROM Numbers
    WHERE n+1 <= 10
)
SELECT n
FROM Numbers

Câu hỏi 1) Giá trị của N tăng lên như thế nào? nếu giá trị được gán cho N mọi lúc thì giá trị N có thể được tăng lên nhưng chỉ giá trị N lần đầu tiên được khởi tạo.

Q 2) CTE và đệ quy của quan hệ nhân viên:

Thời điểm tôi thêm hai người quản lý và thêm một vài nhân viên nữa dưới người quản lý thứ hai là lúc vấn đề bắt đầu.

Tôi muốn hiển thị chi tiết người quản lý đầu tiên và trong các hàng tiếp theo chỉ những thông tin chi tiết về nhân viên có liên quan đến cấp dưới của người quản lý đó.

Giả sử

ID     Name      MgrID    Level
---    ----      ------   -----
1      Keith      NULL     1
2      Josh       1        2
3      Robin      1        2
4      Raja       2        3
5      Tridip     NULL     1
6      Arijit     5        2
7      Amit       5        2
8      Dev        6        3

Tôi muốn hiển thị kết quả theo cách như vậy với các biểu thức CTE. Vui lòng cho tôi biết những gì cần sửa đổi trong sql của tôi mà tôi đã đưa ra ở đây để kéo mối quan hệ giữa người quản lý và nhân viên. Cảm ơn.

Tôi muốn đầu ra như thế này:

ID          Name   MgrID       nLevel      Family
----------- ------ ----------- ----------- --------------------
1           Keith  NULL        1           1
3           Robin  1           2           1
2           Josh   1           2           1
4           Raja   2           3           1
5           Tridip NULL        1           2
7           Amit   5           2           2
6           Arijit 5           2           2
8           Dev    6           3           2

Điều này có thể ...?

Câu trả lời:


210

Tôi chưa kiểm tra mã của bạn, chỉ cố gắng giúp bạn hiểu cách nó hoạt động trong nhận xét;

WITH
  cteReports (EmpID, FirstName, LastName, MgrID, EmpLevel)
  AS
  (
-->>>>>>>>>>Block 1>>>>>>>>>>>>>>>>>
-- In a rCTE, this block is called an [Anchor]
-- The query finds all root nodes as described by WHERE ManagerID IS NULL
    SELECT EmployeeID, FirstName, LastName, ManagerID, 1
    FROM Employees
    WHERE ManagerID IS NULL
-->>>>>>>>>>Block 1>>>>>>>>>>>>>>>>>
    UNION ALL
-->>>>>>>>>>Block 2>>>>>>>>>>>>>>>>>    
-- This is the recursive expression of the rCTE
-- On the first "execution" it will query data in [Employees],
-- relative to the [Anchor] above.
-- This will produce a resultset, we will call it R{1} and it is JOINed to [Employees]
-- as defined by the hierarchy
-- Subsequent "executions" of this block will reference R{n-1}
    SELECT e.EmployeeID, e.FirstName, e.LastName, e.ManagerID,
      r.EmpLevel + 1
    FROM Employees e
      INNER JOIN cteReports r
        ON e.ManagerID = r.EmpID
-->>>>>>>>>>Block 2>>>>>>>>>>>>>>>>>
  )
SELECT
  FirstName + ' ' + LastName AS FullName,
  EmpLevel,
  (SELECT FirstName + ' ' + LastName FROM Employees
    WHERE EmployeeID = cteReports.MgrID) AS Manager
FROM cteReports
ORDER BY EmpLevel, MgrID

Ví dụ đơn giản nhất về đệ quy CTEmà tôi có thể nghĩ ra để minh họa hoạt động của nó là;

;WITH Numbers AS
(
    SELECT n = 1
    UNION ALL
    SELECT n + 1
    FROM Numbers
    WHERE n+1 <= 10
)
SELECT n
FROM Numbers

Q 1) giá trị của N đang tăng lên như thế nào. nếu giá trị được gán cho N mọi lúc thì giá trị N có thể được tăng lên nhưng chỉ giá trị N lần đầu tiên được khởi tạo .

A1:Trong trường hợp này, Nkhông phải là một biến. Nlà một bí danh. Nó tương đương với SELECT 1 AS N. Nó là một cú pháp của sở thích cá nhân. Có 2 phương pháp chính của các cột răng cưa trong a CTEin T-SQL. Tôi đã bao gồm tương tự của mộtCTE trong Excelđể thử và minh họa một cách quen thuộc hơn những gì đang xảy ra.

--  Outside
;WITH CTE (MyColName) AS
(
    SELECT 1
)
-- Inside
;WITH CTE AS
(
    SELECT 1 AS MyColName
    -- Or
    SELECT MyColName = 1  
    -- Etc...
)

Excel_CTE

Câu hỏi 2) bây giờ ở đây về CTE và đệ quy của mối quan hệ nhân viên tại thời điểm tôi thêm hai người quản lý và thêm một vài nhân viên nữa dưới người quản lý thứ hai thì vấn đề bắt đầu. tôi muốn hiển thị thông tin chi tiết về người quản lý đầu tiên và trong các hàng tiếp theo chỉ những thông tin chi tiết về nhân viên đó sẽ đến với những người cấp dưới của người quản lý đó

A2:

Mã này có trả lời câu hỏi của bạn không?

--------------------------------------------
-- Synthesise table with non-recursive CTE
--------------------------------------------
;WITH Employee (ID, Name, MgrID) AS 
(
    SELECT 1,      'Keith',      NULL   UNION ALL
    SELECT 2,      'Josh',       1      UNION ALL
    SELECT 3,      'Robin',      1      UNION ALL
    SELECT 4,      'Raja',       2      UNION ALL
    SELECT 5,      'Tridip',     NULL   UNION ALL
    SELECT 6,      'Arijit',     5      UNION ALL
    SELECT 7,      'Amit',       5      UNION ALL
    SELECT 8,      'Dev',        6   
)
--------------------------------------------
-- Recursive CTE - Chained to the above CTE
--------------------------------------------
,Hierarchy AS
(
    --  Anchor
    SELECT   ID
            ,Name
            ,MgrID
            ,nLevel = 1
            ,Family = ROW_NUMBER() OVER (ORDER BY Name)
    FROM Employee
    WHERE MgrID IS NULL

    UNION ALL
    --  Recursive query
    SELECT   E.ID
            ,E.Name
            ,E.MgrID
            ,H.nLevel+1
            ,Family
    FROM Employee   E
    JOIN Hierarchy  H ON E.MgrID = H.ID
)
SELECT *
FROM Hierarchy
ORDER BY Family, nLevel

Một sql khác với cấu trúc cây

SELECT ID,space(nLevel+
                    (CASE WHEN nLevel > 1 THEN nLevel ELSE 0 END)
                )+Name
FROM Hierarchy
ORDER BY Family, nLevel

truy vấn đệ quy CTE không trả về kết quả theo cách tôi muốn. tôi muốn hiển thị tên người quản lý đầu tiên và sau đó hiển thị tất cả cấp dưới của anh ấy một lần nữa hiển thị tên người quản lý thứ hai và sau đó hiển thị tất cả cấp dưới của anh ấy. tôi muốn đầu ra phải theo kiểu này. nếu có thể xin vui lòng cập nhật truy vấn của bạn. cảm ơn
Thomas

Đã thêm cột [Gia đình]. Kiểm tra ngay.
MarkD

ở đây tôi đưa ra kết quả theo cách tôi muốn hiển thị kết quả. xin vui lòng kiểm tra và cho tôi biết là nó có thể ... nếu có sau đó thực hiện các sửa đổi cần thiết trong sql ur. cảm ơn cho nỗ lực của bạn.
Thomas

Tại sao vậy ';' trước câu lệnh WITH? "; VỚI" Cảm ơn
Drewdin

2
@ SiKni8 - liên kết dường như đã chết
MarkD

11

Muốn phác thảo một ngữ nghĩa ngắn gọn song song với một câu trả lời đã đúng.

Theo thuật ngữ 'đơn giản', một CTE đệ quy có thể được định nghĩa theo ngữ nghĩa như các phần sau:

1: Truy vấn CTE. Còn được gọi là ANCHOR.

2: Truy vấn CTE đệ quy trên CTE trong (1) với UNION ALL (hoặc UNION hoặc EXCEPT hoặc INTERSECT) để kết quả cuối cùng được trả về tương ứng.

3: Điều kiện góc / kết thúc. Đó là theo mặc định khi không còn hàng / bộ giá trị nào được trả về bởi truy vấn đệ quy.

Một ví dụ ngắn sẽ làm cho bức tranh rõ ràng:

;WITH SupplierChain_CTE(supplier_id, supplier_name, supplies_to, level)
AS
(
SELECT S.supplier_id, S.supplier_name, S.supplies_to, 0 as level
FROM Supplier S
WHERE supplies_to = -1    -- Return the roots where a supplier supplies to no other supplier directly

UNION ALL

-- The recursive CTE query on the SupplierChain_CTE
SELECT S.supplier_id, S.supplier_name, S.supplies_to, level + 1
FROM Supplier S
INNER JOIN SupplierChain_CTE SC
ON S.supplies_to = SC.supplier_id
)
-- Use the CTE to get all suppliers in a supply chain with levels
SELECT * FROM SupplierChain_CTE

Giải thích: Truy vấn CTE đầu tiên trả về các nhà cung cấp cơ sở (như lá) không cung cấp trực tiếp cho bất kỳ nhà cung cấp nào khác (-1)

Truy vấn đệ quy trong lần lặp đầu tiên lấy tất cả các nhà cung cấp cung cấp cho các nhà cung cấp được trả về bởi ANCHOR. Quá trình này tiếp tục cho đến khi điều kiện trả về bộ giá trị.

UNION ALL trả về tất cả các bộ giá trị trên tổng số cuộc gọi đệ quy.

Một ví dụ điển hình khác có thể được tìm thấy ở đây .

Tái bút: Để một CTE đệ quy hoạt động, các quan hệ phải có điều kiện phân cấp (đệ quy) để hoạt động. Ví dụ: elementId = elementParentId .. bạn sẽ có được điểm.


9

Quá trình thực thi thực sự khó hiểu với CTE đệ quy, tôi đã tìm thấy câu trả lời tốt nhất tại https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx và phần tóm tắt của quá trình thực thi CTE như bên dưới.

Ngữ nghĩa của việc thực thi đệ quy như sau:

  1. Chia biểu thức CTE thành các thành viên neo và đệ quy.
  2. Chạy (các) thành viên neo tạo lệnh gọi đầu tiên hoặc tập kết quả cơ sở (T0).
  3. Chạy (các) thành viên đệ quy với Ti làm đầu vào và Ti + 1 làm đầu ra.
  4. Lặp lại bước 3 cho đến khi một tập hợp trống được trả lại.
  5. Trả lại tập hợp kết quả. Đây là một ĐOÀN KẾT TẤT CẢ từ T0 đến Tn.

-4
    --DROP TABLE #Employee
    CREATE TABLE #Employee(EmpId BIGINT IDENTITY,EmpName VARCHAR(25),Designation VARCHAR(25),ManagerID BIGINT)

    INSERT INTO #Employee VALUES('M11M','Manager',NULL)
    INSERT INTO #Employee VALUES('P11P','Manager',NULL)

    INSERT INTO #Employee VALUES('AA','Clerk',1)
    INSERT INTO #Employee VALUES('AB','Assistant',1)
    INSERT INTO #Employee VALUES('ZC','Supervisor',2)
    INSERT INTO #Employee VALUES('ZD','Security',2)


    SELECT * FROM #Employee (NOLOCK)

    ;
    WITH Emp_CTE 
    AS
    (
        SELECT EmpId,EmpName,Designation, ManagerID
              ,CASE WHEN ManagerID IS NULL THEN EmpId ELSE ManagerID END ManagerID_N
        FROM #Employee  
    )
    select EmpId,EmpName,Designation, ManagerID
    FROM Emp_CTE
    order BY ManagerID_N, EmpId

1
Đây là một câu trả lời chỉ có mã, thậm chí không trả lời câu hỏi, vì không có CTE đệ quy trong đó.
Dragomok
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.