SQL Server CTE dan contoh rekursi

109

Saya tidak pernah menggunakan CTE dengan rekursi. Saya baru saja membaca artikel tentang itu. Artikel ini menunjukkan info karyawan dengan bantuan CTE dan rekursi server Sql. Ini pada dasarnya menunjukkan karyawan dan info manajer mereka. Saya tidak dapat memahami cara kerja kueri ini. Berikut pertanyaannya:

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

Di sini saya memposting tentang bagaimana output ditampilkan: masukkan deskripsi gambar di sini

Saya hanya perlu tahu bagaimana hal itu menunjukkan manajer terlebih dahulu dan kemudian bawahannya dalam satu lingkaran. Saya kira pernyataan sql pertama hanya menyala sekali dan itu mengembalikan semua id karyawan.

Dan kueri kedua berulang kali diaktifkan, menanyakan database tempat karyawan ada dengan id manajer saat ini.

Tolong jelaskan bagaimana pernyataan sql dieksekusi dalam loop internal dan beri tahu saya urutan eksekusi sql. Terima kasih.

Pertanyaan tahap kedua SAYA

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

Q 1) Bagaimana nilai N bertambah? jika nilai diberikan ke N setiap kali maka nilai N dapat bertambah tetapi hanya pertama kali nilai N diinisialisasi.

Q 2) CTE dan rekursi hubungan karyawan:

Saat saya menambahkan dua manajer dan menambahkan beberapa karyawan lagi di bawah manajer kedua adalah tempat masalah dimulai.

Saya ingin menampilkan detail manajer pertama dan di baris berikutnya hanya detail karyawan yang terkait dengan bawahan manajer tersebut.

Seharusnya

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

Saya ingin menampilkan hasil sedemikian rupa dengan ekspresi CTE. Tolong beri tahu saya apa yang harus diubah di sql saya yang saya berikan di sini untuk menarik hubungan manajer-karyawan. Terima kasih.

Saya ingin hasilnya seperti ini:

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

Apakah ini mungkin ...?

Thomas
sumber

Jawaban:

210

Saya belum menguji kode Anda, hanya mencoba membantu Anda memahami cara kerjanya dalam komentar;

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

Contoh paling sederhana dari rekursif yang CTEdapat saya pikirkan untuk menggambarkan operasinya adalah;

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

Q 1) Bagaimana nilai N bertambah. Jika nilai diberikan ke N setiap kali maka nilai N dapat bertambah tetapi hanya pertama kali nilai N diinisialisasi .

A1:Dalam hal ini, Nbukan merupakan variabel. Nadalah sebuah alias. Ini setara dengan SELECT 1 AS N. Ini adalah sintaks dari preferensi pribadi. Ada 2 metode utama kolom aliasing di a CTEin T-SQL. Saya telah menyertakan analog yang sederhana CTEdi Exceluntuk mencoba dan menggambarkan dengan cara yang lebih familiar apa yang terjadi.

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

Excel_CTE

Pertanyaan 2) Sekarang di sini tentang CTE dan rekursi hubungan karyawan saat saya menambahkan dua manajer dan menambahkan beberapa karyawan lagi di bawah manajer kedua kemudian masalah dimulai. Saya ingin menampilkan detail manajer pertama dan di baris berikutnya hanya detail karyawan yang akan muncul di bawah manajer tersebut

A2:

Apakah kode ini menjawab pertanyaan Anda?

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

Satu lagi persegi dengan struktur pohon

SELECT ID,space(nLevel+
                    (CASE WHEN nLevel > 1 THEN nLevel ELSE 0 END)
                )+Name
FROM Hierarchy
ORDER BY Family, nLevel
MarkD
sumber
kueri rekursif CTE tidak mengembalikan hasil seperti yang saya inginkan. saya ingin menampilkan nama manajer pertama dan kemudian menampilkan semua bawahannya lagi menampilkan nama manajer kedua dan kemudian menampilkan semua bawahannya. Saya ingin hasilnya harus seperti ini. jika memungkinkan harap perbarui kueri Anda. terima kasih
Thomas
Kolom [Keluarga] ditambahkan. Cek sekarang.
MarkD
di sini saya memberikan output seperti yang saya inginkan untuk menampilkan hasil. silakan periksa dan beri tahu saya apakah mungkin ... jika ya maka lakukan modifikasi yang diperlukan di ur sql. terima kasih atas usahamu.
Thomas
mengapa ';' sebelum pernyataan WITH? "; DENGAN" Terima kasih
Drewdin
2
@ SiKni8 - tautan tampaknya sudah mati
MarkD
11

Ingin menguraikan paralel semantik singkat dengan jawaban yang sudah benar.

Dalam istilah 'sederhana', CTE rekursif dapat didefinisikan secara semantik sebagai bagian berikut:

1: Kueri CTE. Juga dikenal sebagai ANCHOR.

2: Kueri CTE rekursif pada CTE di (1) dengan UNION ALL (atau UNION atau EXCEPT atau INTERSECT) sehingga hasil akhir dikembalikan sesuai keinginan.

3: Kondisi pojok / penghentian. Yang secara default ketika tidak ada lagi baris / tupel yang dikembalikan oleh kueri rekursif.

Contoh singkat yang akan memperjelas gambar:

;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

Penjelasan: Kueri CTE pertama mengembalikan pemasok dasar (seperti daun) yang tidak memasok ke pemasok lain secara langsung (-1)

Kueri rekursif pada iterasi pertama mendapatkan semua pemasok yang memasok ke pemasok dikembalikan oleh ANCHOR. Proses ini berlanjut sampai kondisi mengembalikan tupel.

UNION ALL mengembalikan semua tupel selama total panggilan rekursif.

Contoh bagus lainnya dapat ditemukan di sini .

PS: Agar CTE rekursif berfungsi, relasi harus memiliki kondisi hierarkis (rekursif) untuk bekerja. Contoh: elementId = elementParentId .. Anda mendapatkan intinya.

Vaibhav
sumber
9

Proses eksekusi benar-benar membingungkan dengan CTE rekursif, saya menemukan jawaban terbaik di https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx dan abstrak dari proses eksekusi CTE adalah seperti di bawah ini.

Semantik dari eksekusi rekursif adalah sebagai berikut:

  1. Pisahkan ekspresi CTE menjadi anggota jangkar dan rekursif.
  2. Jalankan anggota jangkar yang membuat pemanggilan pertama atau kumpulan hasil dasar (T0).
  3. Jalankan anggota rekursif dengan Ti sebagai masukan dan Ti + 1 sebagai keluaran.
  4. Ulangi langkah 3 hingga set kosong dikembalikan.
  5. Kembalikan set hasil. Ini adalah UNION ALL dari T0 hingga Tn.
Pavan
sumber
-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
Vishal Motwani
sumber
1
Ini adalah jawaban hanya kode yang bahkan tidak menjawab pertanyaan, karena tidak ada CTE rekursif di dalamnya.
Dragomok