SQL Server CTE ve özyineleme örneği


109

CTE'yi asla özyineleme ile kullanmıyorum. Onunla ilgili bir makale okuyordum. Bu makale, Sql sunucusu CTE ve özyineleme yardımıyla çalışan bilgilerini gösterir. Temelde çalışanları ve yönetici bilgilerini gösteriyor. Bu sorgunun nasıl çalıştığını anlayamıyorum. İşte sorgu:

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

Burada çıktının nasıl gösterildiğini yazıyorum: görüntü açıklamasını buraya girin

Sadece önce menajeri ve sonra astını bir döngüde nasıl gösterdiğini bilmem gerekiyor. Sanırım ilk sql ifadesi yalnızca bir kez ateşleniyor ve bu tüm çalışan kimliklerini döndürüyor.

Ve ikinci sorgu, çalışanın mevcut yönetici kimliğiyle üzerinde bulunduğu veritabanını sorgulayarak tekrar tekrar tetiklenir.

Lütfen sql ifadesinin bir iç döngüde nasıl çalıştığını açıklayın ve ayrıca bana sql yürütme sırasını söyleyin. Teşekkürler.

2. soru aşaması

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

S 1) N'nin değeri nasıl artırılıyor? değer her seferinde N'ye atanırsa, o zaman N değeri artırılabilir, ancak yalnızca ilk kez N değeri başlatılır.

S 2) CTE ve çalışan ilişkilerinin yinelemesi:

İki yönetici eklediğim ve ikinci yöneticinin altına birkaç çalışan daha eklediğim an, sorunun başladığı yerdir.

İlk yönetici ayrıntısını ve sonraki satırlarda yalnızca o yöneticinin astıyla ilgili çalışan ayrıntılarını görüntülemek istiyorum.

varsaymak

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

Sonuçları CTE ifadeleri ile bu şekilde görüntülemek istiyorum. Yönetici-çalışan ilişkilerini çekebilmek için burada verdiğim sql'de neleri değiştireceğimi söyleyin lütfen. Teşekkürler.

Çıktının şöyle olmasını istiyorum:

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

Mümkün mü...?

Yanıtlar:


210

Kodunuzu test etmedim, sadece yorumda nasıl çalıştığını anlamanıza yardımcı olmaya çalıştım;

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

Bir özyinelemenin CTEişleyişini açıklamak için aklıma gelen en basit örneği ;

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

S 1) N'nin değerinin nasıl arttığı. değer her seferinde N'ye atanırsa, o zaman N değeri artırılabilir, ancak yalnızca ilk kez N değeri başlatılır .

A1:Bu durumda Nbir değişken değildir. Nbir takma addır. Eşdeğeri SELECT 1 AS N. Kişisel tercihlerin bir sözdizimidir. Bir CTEin T-SQL. Bir basit bir analog ekledik CTEiçinde Exceldenemek ve ne olup bittiğini bir daha tanıdık bir şekilde göstermek için.

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

Excel_CTE

S 2) Şimdi burada CTE ve çalışan ilişkisinin özyinelemesi hakkında iki yönetici eklediğim ve ikinci yöneticinin altına birkaç çalışan daha ekledikten sonra problem başlayacak. İlk yönetici ayrıntısını görüntülemek istiyorum ve sonraki satırlarda yalnızca bu çalışan ayrıntıları o yöneticinin astları olacak

A2:

Bu kod sorunuzu yanıtlıyor mu?

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

Ağaç yapısına sahip başka bir sql

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

CTE özyinelemeli sorgu istediğim şekilde sonuç vermiyor. İlk yönetici adını görüntülemek ve ardından tüm astını tekrar görüntülemek ve ikinci yönetici adını görüntülemek ve ardından tüm astını görüntülemek istiyorum. çıktının bu şekilde olmasını istiyorum. Mümkünse lütfen sorgunuzu güncelleyin. teşekkürler
Thomas

[Aile] sütunu eklendi. Şimdi kontrol et.
MarkD

burada çıktıyı sonucu görüntülemek istediğim şekilde veriyorum. lütfen kontrol edin ve mümkün olup olmadığını söyleyin ... eğer evet ise o zaman ur sql'de gerekli değişiklikleri yapın. çabalarınız için teşekkürler.
Thomas

neden ';' WITH ifadesinden önce? "; İLE" Teşekkürler
Drewdin

2
@ SiKni8 - bağlantı kesilmiş gibi görünüyor
MarkD

11

Zaten doğru olan bir cevaba kısa bir anlamsal paralellik göstermek ister.

'Basit' terimlerle, özyinelemeli bir CTE, anlamsal olarak aşağıdaki bölümler olarak tanımlanabilir:

1: CTE sorgusu. ANCHOR olarak da bilinir.

2: UNION ALL (veya UNION veya EXCEPT veya INTERSECT) ile (1) 'de CTE üzerindeki özyinelemeli CTE sorgusu, böylece nihai sonuç buna göre döndürülür.

3: Köşe / sonlandırma koşulu. Yinelemeli sorgu tarafından döndürülen daha fazla satır / tuple olmadığında varsayılan olarak budur.

Resmi netleştirecek kısa bir örnek:

;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

Açıklama: İlk CTE sorgusu, başka herhangi bir tedarikçiye doğrudan tedarik sağlamayan temel tedarikçileri (yapraklar gibi) döndürür (-1)

İlk yinelemedeki yinelemeli sorgu, ANCHOR tarafından iade edilen tedarikçilere tedarik yapan tüm tedarikçileri alır. Bu süreç, koşul tuple dönene kadar devam eder.

UNION ALL, toplam özyinelemeli çağrılar üzerinden tüm tupleları döndürür.

Başka bir güzel örnek burada bulunabilir .

Not: Özyinelemeli bir CTE'nin çalışması için, ilişkilerin üzerinde çalışacak hiyerarşik (özyinelemeli) bir koşulu olması gerekir. Ör: elementId = elementParentId .. puanı anladınız.


9

Yürütme süreci özyinelemeli CTE ile gerçekten kafa karıştırıcı, en iyi yanıtı https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx ve CTE yürütme sürecinin özetinde buldum aşağıdaki gibidir.

Özyinelemeli yürütmenin anlambilimi aşağıdaki gibidir:

  1. CTE ifadesini bağlantı ve yinelemeli üyelere bölün.
  2. İlk çağrıyı veya temel sonuç kümesini (T0) oluşturarak bağlantı elemanlarını çalıştırın.
  3. Özyinelemeli üye (ler) i giriş olarak Ti ve çıkış olarak Ti + 1 ile çalıştırın.
  4. Boş bir küme dönene kadar 3. adımı tekrarlayın.
  5. Sonuç kümesini döndür. Bu T0'dan Tn'ye TÜMÜNDEN BİR BİRLİKTİR.

-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
Bu, içinde özyinelemeli CTE olmadığı için soruyu yanıtlamayan yalnızca kodlu bir cevaptır .
Dragomok
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.