code

SQL Server CTE 및 재귀 예제

codestyles 2020. 8. 23. 09:15
반응형

SQL Server CTE 및 재귀 예제


재귀와 함께 CTE를 사용하지 않습니다. 나는 그것에 관한 기사를 읽고 있었다. 이 문서에서는 SQL 서버 CTE 및 재귀를 사용하여 직원 정보를 보여줍니다. 기본적으로 직원과 관리자 정보를 표시합니다. 이 쿼리가 어떻게 작동하는지 이해할 수 없습니다. 다음은 쿼리입니다.

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

여기에 출력이 어떻게 표시되는지 게시하고 있습니다. 여기에 이미지 설명 입력

관리자를 먼저 표시 한 다음 그의 부하를 루프로 표시하는 방법을 알아야합니다. 첫 번째 SQL 문은 한 번만 실행되고 모든 직원 ID를 반환한다고 생각합니다.

그리고 두 번째 쿼리는 반복적으로 실행되어 현재 관리자 ID로 직원이 존재하는 데이터베이스를 쿼리합니다.

SQL 문이 내부 루프에서 어떻게 실행되는지 설명하고 SQL 실행 순서도 알려주십시오. 감사.

나의 2 단계 질문

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

Q 1) N의 값은 어떻게 증가합니까? 값이 매번 N에 할당되면 N 값이 증가 할 수 있지만 처음 N 값이 초기화되었을 때만 가능합니다.

Q 2) CTE 및 직원 관계의 재귀 :

두 명의 관리자를 추가하고 두 번째 관리자 아래에 몇 명의 직원을 추가하는 순간 문제가 시작됩니다.

첫 번째 관리자 세부 정보를 표시하고 다음 행에는 해당 관리자의 부하 직원과 관련된 직원 세부 정보 만 표시하려고합니다.

가정

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

CTE 식으로 결과를 표시하고 싶습니다. 관리자-직원 관계를 가져 오기 위해 여기에 준 내 SQL에서 수정해야 할 내용을 알려주십시오. 감사.

출력이 다음과 같기를 바랍니다.

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

이게 가능해...?


나는 당신의 코드를 테스트하지 않았고, 그것이 주석에서 어떻게 작동하는지 이해하도록 돕기 위해 노력했습니다.

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

The simplest example of a recursive CTE I can think of to illustrate its operation is;

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

Q 1) how value of N is getting incremented. if value is assign to N every time then N value can be incremented but only first time N value was initialize.

A1: In this case, N is not a variable. N is an alias. It is the equivalent of SELECT 1 AS N. It is a syntax of personal preference. There are 2 main methods of aliasing columns in a CTE in T-SQL. I've included the analog of a simple CTE in Excel to try and illustrate in a more familiar way what is happening.

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

Excel_CTE

Q 2) now here about CTE and recursion of employee relation the moment i add two manager and add few more employee under second manager then problem start. i want to display first manager detail and in the next rows only those employee details will come those who are subordinate of that manager

A2:

Does this code answer your question?

--------------------------------------------
-- 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

Another one sql with tree structure

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

Would like to outline a brief semantic parallel to an already correct answer.

In 'simple' terms, a recursive CTE can be semantically defined as the following parts:

1: The CTE query. Also known as ANCHOR.

2: The recursive CTE query on the CTE in (1) with UNION ALL (or UNION or EXCEPT or INTERSECT) so the ultimate result is accordingly returned.

3: The corner/termination condition. Which is by default when there are no more rows/tuples returned by the recursive query.

A short example that will make the picture clear:

;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

Explanation: The first CTE query returns the base suppliers (like leaves) who do not supply to any other supplier directly (-1)

The recursive query in the first iteration gets all the suppliers who supply to the suppliers returned by the ANCHOR. This process continues till the condition returns tuples.

UNION ALL returns all the tuples over the total recursive calls.

Another good example can be found here.

PS: For a recursive CTE to work, the relations must have a hierarchical (recursive) condition to work on. Ex: elementId = elementParentId.. you get the point.


실행 프로세스는 재귀 CTE와 정말 혼동 스럽습니다. https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx 에서 가장 좋은 답을 찾았고 CTE 실행 프로세스의 개요를 찾았습니다. 다음과 같습니다.

재귀 실행의 의미는 다음과 같습니다.

  1. CTE 식을 앵커 및 재귀 멤버로 분할합니다.
  2. 첫 번째 호출 또는 기본 결과 세트 (T0)를 작성하는 앵커 멤버를 실행하십시오.
  3. Ti를 입력으로, Ti + 1을 출력으로 사용하여 재귀 멤버를 실행합니다.
  4. 빈 세트가 반환 될 때까지 3 단계를 반복합니다.
  5. 결과 집합을 반환합니다. 이것은 T0에서 Tn까지의 UNION ALL입니다.

    --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

참고 URL : https://stackoverflow.com/questions/14274942/sql-server-cte-and-recursion-example

반응형