पुनरावर्ती स्व-जुड़ने का सबसे सरल तरीका?


101

SQL सर्वर में पुनरावर्ती स्व-जुड़ने का सबसे सरल तरीका क्या है? मेरे पास इस तरह की एक तालिका है:

PersonID | Initials | ParentID
1          CJ         NULL
2          EB         1
3          MB         1
4          SW         2
5          YT         NULL
6          IS         5

और मैं केवल एक विशिष्ट व्यक्ति के साथ शुरू होने वाले पदानुक्रम से संबंधित रिकॉर्ड प्राप्त करने में सक्षम होना चाहता हूं। इसलिए यदि मैंने CID के व्यक्ति द्वारा CJ के पदानुक्रम का अनुरोध किया = 1 मुझे मिलेगा:

PersonID | Initials | ParentID
1          CJ         NULL
2          EB         1
3          MB         1
4          SW         2

और ईबी के लिए मुझे मिलेगा:

PersonID | Initials | ParentID
2          EB         1
4          SW         2

मैं इस पर थोड़ा अटका हुआ हूं सोच भी नहीं सकता कि यह एक निश्चित गहराई से प्रतिक्रिया के अलावा कैसे जुड़ सकता है। ऐसा होता है क्योंकि ऐसा होता है क्योंकि हमारे पास कई स्तर नहीं होंगे लेकिन मैं इसे ठीक से करना चाहूंगा।

धन्यवाद! क्रिस।


2
SQL सर्वर का कौन सा संस्करण आप उपयोग कर रहे हैं? यानी Sql 2000, 2005, 2008?
boydc7

2
पुनरावर्ती प्रश्नों के बारे में SO प्रश्न: stackoverflow.com/search?q=sql-server+recursive
OMG Ponies

जवाबों:


113
WITH    q AS 
        (
        SELECT  *
        FROM    mytable
        WHERE   ParentID IS NULL -- this condition defines the ultimate ancestors in your chain, change it as appropriate
        UNION ALL
        SELECT  m.*
        FROM    mytable m
        JOIN    q
        ON      m.parentID = q.PersonID
        )
SELECT  *
FROM    q

आदेश देने की स्थिति जोड़कर, आप ट्री ऑर्डर को संरक्षित कर सकते हैं:

WITH    q AS 
        (
        SELECT  m.*, CAST(ROW_NUMBER() OVER (ORDER BY m.PersonId) AS VARCHAR(MAX)) COLLATE Latin1_General_BIN AS bc
        FROM    mytable m
        WHERE   ParentID IS NULL
        UNION ALL
        SELECT  m.*,  q.bc + '.' + CAST(ROW_NUMBER() OVER (PARTITION BY m.ParentID ORDER BY m.PersonID) AS VARCHAR(MAX)) COLLATE Latin1_General_BIN
        FROM    mytable m
        JOIN    q
        ON      m.parentID = q.PersonID
        )
SELECT  *
FROM    q
ORDER BY
        bc

ORDER BYशर्त बदलकर आप भाई-बहनों का क्रम बदल सकते हैं।


7
+1, सिवाय इसके कि क्रिस के PersonID = theIdYouAreLookingForबजाय की आवश्यकता होगी ParentID IS NULL
हेंजज़ी


@ एरोनिनस: मूल नोड को WITHक्लॉज में सबसे ऊपरी (एंकर) क्वेरी द्वारा परिभाषित किया गया है । अगर आपको बारीकियों की जरूरत है, तो कृपया sqlfiddle.com पर एक फिडेल बनाएं और यहां लिंक पोस्ट करें।
क्वासोनि

25

सीटीई का उपयोग करके आप इसे इस तरह से कर सकते हैं

DECLARE @Table TABLE(
        PersonID INT,
        Initials VARCHAR(20),
        ParentID INT
)

INSERT INTO @Table SELECT     1,'CJ',NULL
INSERT INTO @Table SELECT     2,'EB',1
INSERT INTO @Table SELECT     3,'MB',1
INSERT INTO @Table SELECT     4,'SW',2
INSERT INTO @Table SELECT     5,'YT',NULL
INSERT INTO @Table SELECT     6,'IS',5

DECLARE @PersonID INT

SELECT @PersonID = 1

;WITH Selects AS (
        SELECT *
        FROM    @Table
        WHERE   PersonID = @PersonID
        UNION ALL
        SELECT  t.*
        FROM    @Table t INNER JOIN
                Selects s ON t.ParentID = s.PersonID
)
SELECT  *
FROm    Selects

2
महत्वपूर्ण पूरा जवाब जहां WHID के लिए अच्छा है = @PersonID
Oli B

5

बड़े टेबल के बदलाव के साथ क्वासोई क्वेरी। अधिक बच्चों वाले माता-पिता तब 10: स्ट्रैट के रूप में (5) पंक्ति_नंबर ()

क्ष एएस के साथ 
        (
        M का चयन करें। *, CAST (str (ROW_NUMBER)) OVER (ORDER BY m.ordernum), 5) AS VARCHAR (MAX)) COLLATE Latin1_General_BIN AS bc
        #Tm से
        कहाँ पितृद्या = ०
        यूनिअन ऑल
        M का चयन करें। *, q.bc + '।' + str (ROW_NUMBER () OVER (पार्टिशन बाय m.ParentID ORDER BY m.ordernum), 5) COLLATE लैटिन 1_General_BIN
        #Tm से
        जॉय क्यू
        M.parentID = q.DBID पर
        )
चुनते हैं *
क्यू से
द्वारा आदेश
        बीसी


2

SQL 2005 या बाद में, CTE दिखाए गए उदाहरणों के अनुसार जाने का मानक तरीका है।

SQL 2000, आप इसे UDFs का उपयोग करके कर सकते हैं -

CREATE FUNCTION udfPersonAndChildren
(
    @PersonID int
)
RETURNS @t TABLE (personid int, initials nchar(10), parentid int null)
AS
begin
    insert into @t 
    select * from people p      
    where personID=@PersonID

    while @@rowcount > 0
    begin
      insert into @t 
      select p.*
      from people p
        inner join @t o on p.parentid=o.personid
        left join @t o2 on p.personid=o2.personid
      where o2.personid is null
    end

    return
end

(जो 2005 में काम करेगा, यह सिर्फ इसे करने का मानक तरीका नहीं है। उन्होंने कहा, यदि आपको लगता है कि काम करने का आसान तरीका है, तो इसे चलाएं)

यदि आपको वास्तव में SQL7 में ऐसा करने की आवश्यकता है, तो आप मोटे तौर पर ऊपर एक स्पोक में कर सकते हैं, लेकिन इसमें से चयन नहीं कर सकते - SQL7 UDFs का समर्थन नहीं करता है।


2

सीटीई रिकर्सन की अवधारणा को समझने में मदद के लिए निम्नलिखित की जाँच करें

DECLARE
@startDate DATETIME,
@endDate DATETIME

SET @startDate = '11/10/2011'
SET @endDate = '03/25/2012'

; WITH CTE AS (
    SELECT
        YEAR(@startDate) AS 'yr',
        MONTH(@startDate) AS 'mm',
        DATENAME(mm, @startDate) AS 'mon',
        DATEPART(d,@startDate) AS 'dd',
        @startDate 'new_date'
    UNION ALL
    SELECT
        YEAR(new_date) AS 'yr',
        MONTH(new_date) AS 'mm',
        DATENAME(mm, new_date) AS 'mon',
        DATEPART(d,@startDate) AS 'dd',
        DATEADD(d,1,new_date) 'new_date'
    FROM CTE
    WHERE new_date < @endDate
    )
SELECT yr AS 'Year', mon AS 'Month', count(dd) AS 'Days'
FROM CTE
GROUP BY mon, yr, mm
ORDER BY yr, mm
OPTION (MAXRECURSION 1000)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.