Sql server CTE и пример рекурсии
Я никогда не использую CTE с рекурсией. Я как раз читал статью об этом. В этой статье показаны сведения о сотрудниках с помощью SQL server 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 оператор срабатывает только один раз, и это возвращает все идентификаторы сотрудников.
и второй запрос повторно срабатывает, запрашивая базу данных, на которой существует сотрудник с текущим идентификатором менеджера.
пожалуйста, объясните, как оператор 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
это возможно...?
4 ответа:
я не проверял ваш код, просто пытался помочь вам понять, как он работает в комментариях;
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
. Это синтаксис личных предпочтений. Есть 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... )
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 может быть семантически определен как следующие части:
1: запрос CTE. Также известный как якорь.
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)
рекурсивный запрос в первой итерации получает все поставщики, которые поставляют поставщикам, возвращенным привязкой. Этот процесс продолжается до тех пор, пока условие не вернет кортежи.
ОБЪЕДИНЕНИЕ ВСЕХ возвращает все Кортежи по общему количеству рекурсивных вызовов.
еще один хороший пример можно найти здесь.
PS: Для работы рекурсивного CTE отношения должны иметь иерархическое (рекурсивное) условие для работы. Пример: elementId = elementParentId.. вы понимаете, в чем дело.
процесс выполнения действительно запутан с рекурсивным CTE, я нашел лучший ответ на https://technet.microsoft.com/en-us/library/ms186243 (v=sql.105).aspx и аннотация процесса выполнения CTE, как показано ниже.
семантика рекурсивного выполнения выглядит следующим образом:
- разделить выражение CTE на якорные и рекурсивные члены.
- запустите якорные элементы, создающие первый вызов или базовый результирующий набор (T0).
- запустите рекурсивный элемент(ы) с Ti в качестве входа и Ti+1 в качестве выхода.
- повторяйте Шаг 3, пока не будет возвращен пустой набор.
- возвращает результирующий набор. Это объединение всего от T0 до Tn.
--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