Приклад CTE SQL Server та рекурсія


109

Я ніколи не використовую CTE при рекурсії. Я просто читав статтю про це. У цій статті відображається інформація про працівників за допомогою CTE сервера Sql та рекурсії. Це, в основному, інформація про працівників та їх керівника. Я не в змозі зрозуміти, як працює цей запит. Ось запит:

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 запускається лише один раз, і це повертає всі ідентифікатори службовців.

А другий запит повторно спрацьовує, запитуючи базу даних, в якій існує співробітник, з поточним ідентифікатором менеджера.

Будь ласка, поясніть, як оператор 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. Це синтаксис особистих уподобань. Є два основні способи псевдонімування стовпців у 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 не повертає результат так, як мені хочеться. я хочу відобразити перше ім’я менеджера, а потім відобразити все його підлеглого знову відобразити ім'я другого менеджера, а потім відобразити всіх його підлеглого. Я хочу, щоб результат був таким чином. по можливості оновіть запит на ур. спасибі
Томас

Додано стовпець [Сім'я]. Перевірь зараз.
MarkD

тут я даю висновку так, як я хочу відобразити результат. будь ласка, перевірте і скажіть, чи це можливо ... якщо так, то зробіть необхідні зміни в ur sql. дякую за зусилля ур.
Thomas

чому ';' перед заявою WITH? "; З" Дякую
Дрюдін

2
@ SiKni8 - посилання здається мертвим
MarkD

11

Хочеться окреслити коротку смислову паралель вже правильної відповіді.

«Простими» словами, рекурсивний CTE може бути семантично визначений у вигляді наступних частин:

1: Запит на CTE Також відомий як ANCHOR.

2: Рекурсивний запит CTE на CTE в (1) з UNION ALL (або UNION, EXCEPT або INTERSECT), тому кінцевий результат відповідно повертається.

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)

Рекурсивний запит під час першої ітерації отримує всіх постачальників, які постачають постачальників, повернених ЯКОР. Цей процес триває, поки умова не поверне кортежі.

UNION ALL повертає всі кортежі за загальну кількість рекурсивних дзвінків.

Ще один хороший приклад можна знайти тут .

PS: Щоб рекурсивний 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.

-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.
Драгомок
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.