SQL Server CTE 및 재귀 예제


109

재귀와 함께 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

이게 가능해...?

답변:


210

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

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

CTE제가 생각할 수 있는 재귀의 가장 간단한 예 는 다음과 같습니다.

;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 값이 초기화되었을 때만 가능 합니다.

A1:이 경우은 N변수가 아닙니다. N별칭입니다. 와 동등 SELECT 1 AS N합니다. 개인 취향의 구문입니다. A의 열을 앨리어싱의 2 가지 주요 방법이 있습니다 CTE에가 T-SQL. 나는 간단한의 아날로그 포함 시켰 CTE에서 Excel시도하고 무슨 일이 일어나고 있는지 좀 더 친숙한 방법으로 설명하기를.

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

Excel_CTE

Q 2) 이제 두 명의 관리자를 추가하고 두 번째 관리자 아래에 몇 명의 직원을 추가 한 다음 문제가 시작되는 순간 CTE 및 직원 관계의 재귀에 대해 설명합니다. 첫 번째 관리자 세부 정보를 표시하고 다음 행에는 해당 관리자의 부하 직원 만 해당 직원 세부 정보 만 표시됩니다.

A2:

이 코드가 질문에 대답합니까?

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

트리 구조가있는 또 다른 SQL

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

CTE 재귀 쿼리가 원하는 방식으로 결과를 반환하지 않습니다. 첫 번째 관리자 이름을 표시 한 다음 모든 부하 직원을 다시 표시하고 싶습니다. 두 번째 관리자 이름을 표시 한 다음 그의 부하 직원을 모두 표시합니다. 나는 출력이 이런 방식으로 있어야합니다. 가능하면 ur 쿼리를 업데이트하십시오. 감사
토마스

[Family] 열이 추가되었습니다. 지금 확인하십시오.
MarkD 2013 년

여기에서 결과를 표시하려는 방식으로 출력을 제공합니다. 확인하고 가능한지 알려주십시오 ... 그렇다면 ur SQL에서 필요한 수정을하십시오. 당신의 노력에 감사드립니다.
Thomas

왜 ';' WITH 문 앞에? "; 함께"감사합니다
Drewdin

2
@ SiKni8-링크가 죽은 것 같습니다
MarkD

11

이미 정답에 대한 간략한 의미론을 설명하고 싶습니다.

'단순한'용어로 재귀 CTE는 의미 론적으로 다음 부분으로 정의 될 수 있습니다.

1 : CTE 쿼리입니다. ANCHOR라고도합니다.

2 : UNION ALL (또는 UNION, EXCEPT 또는 INTERSECT)이있는 (1)의 CTE에 대한 재귀 CTE 쿼리이므로 그에 따라 최종 결과가 반환됩니다.

3 : 코너 / 종료 조건. 재귀 쿼리에서 반환 된 행 / 튜플이 더 이상 없을 때 기본적으로 사용됩니다.

그림을 명확하게하는 간단한 예 :

;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

설명 : 첫 번째 CTE 쿼리는 다른 공급자에게 직접 공급하지 않는 기본 공급자 (예 : 잎사귀)를 반환합니다 (-1).

첫 번째 반복의 재귀 쿼리는 ANCHOR에서 반환 한 공급 업체에 공급하는 모든 공급 업체를 가져옵니다. 이 프로세스는 조건이 튜플을 반환 할 때까지 계속됩니다.

UNION ALL은 총 재귀 호출에 대한 모든 튜플을 반환합니다.

또 다른 좋은 예는 여기 에서 찾을 수 있습니다 .

추신 : 재귀 적 CTE가 작동하려면 관계에 작업 할 계층 적 (재귀 적) 조건이 있어야합니다. 예 : elementId = elementParentId .. 당신은 요점을 얻습니다.


9

실행 프로세스는 재귀 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입니다.

-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
이것은 재귀 CTE 가 없기 때문에 질문에 답하지 않는 코드 전용 답변입니다 .
Dragomok 2017-06-11
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.