परिमित सहयोग दूरी वाली पंक्तियों के लिए अद्वितीय मान निर्दिष्ट करने का समाधान


9

मेरे पास एक तालिका है जिसे निम्न कोड के साथ बनाया और आबाद किया जा सकता है:

CREATE TABLE dbo.Example(GroupKey int NOT NULL, RecordKey varchar(12) NOT NULL);
ALTER TABLE dbo.Example
    ADD CONSTRAINT iExample PRIMARY KEY CLUSTERED(GroupKey ASC, RecordKey ASC);
INSERT INTO dbo.Example(GroupKey, RecordKey)
VALUES (1, 'Archimedes'), (1, 'Newton'), (1, 'Euler'), (2, 'Euler'), (2, 'Gauss'),
       (3, 'Gauss'), (3, 'Poincaré'), (4, 'Ramanujan'), (5, 'Neumann'),
       (5, 'Grothendieck'), (6, 'Grothendieck'), (6, 'Tao');

उन सभी पंक्तियों के लिए जिनके पास एक अन्य पंक्ति के आधार पर एक सीमित सहयोग दूरी हैRecordKey , मैं एक अद्वितीय मान निर्दिष्ट करना चाहूंगा- मुझे इस बात की परवाह नहीं है कि अद्वितीय मान कैसे या किस प्रकार का डेटा है।

एक सही परिणाम सेट जो मिलता है जो मैं पूछ रहा हूं वह निम्नलिखित क्वेरी के साथ उत्पन्न हो सकता है:

SELECT 1 AS SupergroupKey, GroupKey, RecordKey
FROM dbo.Example
WHERE GroupKey IN(1, 2, 3)
UNION ALL
SELECT 2 AS SupergroupKey, GroupKey, RecordKey
FROM dbo.Example
WHERE GroupKey = 4
UNION ALL
SELECT 3 AS SupergroupKey, GroupKey, RecordKey
FROM dbo.Example
WHERE GroupKey IN(5, 6)
ORDER BY SupergroupKey ASC, GroupKey ASC, RecordKey ASC;

मैं जो पूछ रहा हूं, उसकी बेहतर सहायता के लिए, मैं बताऊंगा कि GroupKeys3 में समान क्यों है SupergroupKey:

  • GroupKey1 में RecordKeyयूलर शामिल है जो बदले में GroupKey2 में निहित है ; इस प्रकार GroupKeys 1 और 2 में समान होना चाहिए SupergroupKey
  • क्योंकि गॉस GroupKey2 और 3 दोनों में समाहित है , वे भी एक ही होना चाहिए SupergroupKey। इससे GroupKeyएस 1–3 समान होता है SupergroupKey
  • चूँकि GroupKey1–3 RecordKeyशेष s के साथ किसी भी s को साझा नहीं करता है GroupKey, वे केवल SupergroupKey1 का मान निर्दिष्ट करते हैं ।

मुझे यह जोड़ना चाहिए कि समाधान सामान्य होने की आवश्यकता है। उपरोक्त तालिका और परिणाम सेट केवल एक उदाहरण था।

परिशिष्ट

मैंने समाधान के लिए गैर-पुनरावृत्ति की आवश्यकता को हटा दिया। जबकि मैं इस तरह के समाधान को प्राथमिकता दूंगा, मेरा मानना ​​है कि यह एक अनुचित बाधा है। दुर्भाग्य से, मैं किसी भी CLR- आधारित समाधान का उपयोग करने में असमर्थ हूं; लेकिन अगर आप इस तरह के समाधान को शामिल करना चाहते हैं, तो बेझिझक करें। मैं संभवतः इसे उत्तर के रूप में स्वीकार नहीं करूंगा।

मेरी असली तालिका में पंक्तियों की संख्या 5 मिलियन जितनी है, लेकिन ऐसे दिन हैं जब पंक्तियों की संख्या "केवल" लगभग दस हजार होगी। औसतन 8 RecordKeyएस प्रति GroupKeyऔर 4 GroupKeyएस प्रति हैं RecordKey। मुझे लगता है कि एक समाधान में घातीय समय जटिलता होगी, लेकिन मैं फिर भी एक समाधान में दिलचस्पी रखता हूं।

जवाबों:


7

प्रदर्शन की तुलना के लिए यह एक पुनरावृत्त टी-एसक्यूएल समाधान है।

यह मानता है कि सुपर समूह कुंजी को संग्रहीत करने के लिए एक अतिरिक्त स्तंभ तालिका में जोड़ा जा सकता है, और अनुक्रमण को बदला जा सकता है:

सेट अप

DROP TABLE IF EXISTS 
    dbo.Example;

CREATE TABLE dbo.Example
(
    SupergroupKey integer NOT NULL
        DEFAULT 0, 
    GroupKey integer NOT NULL, 
    RecordKey varchar(12) NOT NULL,

    CONSTRAINT iExample 
    PRIMARY KEY CLUSTERED 
        (GroupKey ASC, RecordKey ASC),

    CONSTRAINT [IX dbo.Example RecordKey, GroupKey]
    UNIQUE NONCLUSTERED (RecordKey, GroupKey),

    INDEX [IX dbo.Example SupergroupKey, GroupKey]
        (SupergroupKey ASC, GroupKey ASC)
);

INSERT dbo.Example
    (GroupKey, RecordKey)
VALUES 
    (1, 'Archimedes'), 
    (1, 'Newton'),
    (1, 'Euler'),
    (2, 'Euler'),
    (2, 'Gauss'),
    (3, 'Gauss'),
    (3, 'Poincaré'),
    (4, 'Ramanujan'),
    (5, 'Neumann'),
    (5, 'Grothendieck'),
    (6, 'Grothendieck'),
    (6, 'Tao');

यदि आप वर्तमान प्राथमिक कुंजी के प्रमुख क्रम को उलटने में सक्षम हैं, तो अतिरिक्त अद्वितीय सूचकांक की आवश्यकता नहीं होगी।

रेखांकित करें

इस समाधान का दृष्टिकोण है:

  1. सुपर ग्रुप आईडी को 1 पर सेट करें
  2. सबसे कम संख्या वाली असंसाधित समूह कुंजी का पता लगाएं
  3. अगर कोई नहीं मिला, तो बाहर निकलें
  4. वर्तमान समूह कुंजी के साथ सभी पंक्तियों के लिए सुपर समूह सेट करें
  5. वर्तमान समूह में पंक्तियों से संबंधित सभी पंक्तियों के लिए सुपर समूह सेट करें
  6. चरण 5 को तब तक दोहराएं जब तक कि कोई पंक्तियां अपडेट न हों
  7. वर्तमान सुपर समूह आईडी बढ़ाएँ
  8. चरण 2 पर जाएं

कार्यान्वयन

टिप्पणियाँ इनलाइन:

-- No execution plans or rows affected messages
SET NOCOUNT ON;
SET STATISTICS XML OFF;

-- Reset all supergroups
UPDATE E
SET SupergroupKey = 0
FROM dbo.Example AS E
    WITH (TABLOCKX)
WHERE 
    SupergroupKey != 0;

DECLARE 
    @CurrentSupergroup integer = 0,
    @CurrentGroup integer = 0;

WHILE 1 = 1
BEGIN
    -- Next super group
    SET @CurrentSupergroup += 1;

    -- Find the lowest unprocessed group key
    SELECT 
        @CurrentGroup = MIN(E.GroupKey)
    FROM dbo.Example AS E
    WHERE 
        E.SupergroupKey = 0;

    -- Exit when no more unprocessed groups
    IF @CurrentGroup IS NULL BREAK;

    -- Set super group for all records in the current group
    UPDATE E
    SET E.SupergroupKey = @CurrentSupergroup
    FROM dbo.Example AS E 
    WHERE 
        E.GroupKey = @CurrentGroup;

    -- Iteratively find all groups for the super group
    WHILE 1 = 1
    BEGIN
        WITH 
            RecordKeys AS
            (
                SELECT DISTINCT
                    E.RecordKey
                FROM dbo.Example AS E
                WHERE
                    E.SupergroupKey = @CurrentSupergroup
            ),
            GroupKeys AS
            (
                SELECT DISTINCT
                    E.GroupKey
                FROM RecordKeys AS RK
                JOIN dbo.Example AS E
                    WITH (FORCESEEK)
                    ON E.RecordKey = RK.RecordKey
            )
        UPDATE E WITH (TABLOCKX)
        SET SupergroupKey = @CurrentSupergroup
        FROM GroupKeys AS GK
        JOIN dbo.Example AS E
            ON E.GroupKey = GK.GroupKey
        WHERE
            E.SupergroupKey = 0
        OPTION (RECOMPILE, QUERYTRACEON 9481); -- The original CE does better

        -- Break when no more related groups found
        IF @@ROWCOUNT = 0 BREAK;
    END;
END;

SELECT
    E.SupergroupKey,
    E.GroupKey,
    E.RecordKey
FROM dbo.Example AS E;

निष्पादन योजना

प्रमुख अद्यतन के लिए:

अद्यतन योजना

परिणाम

तालिका की अंतिम स्थिति है:

╔═══════════════╦══════════╦══════════════╗
║ SupergroupKey ║ GroupKey ║  RecordKey   ║
╠═══════════════╬══════════╬══════════════╣
║             1 ║        1 ║ Archimedes   ║
║             1 ║        1 ║ Euler        ║
║             1 ║        1 ║ Newton       ║
║             1 ║        2 ║ Euler        ║
║             1 ║        2 ║ Gauss        ║
║             1 ║        3 ║ Gauss        ║
║             1 ║        3 ║ Poincaré     ║
║             2 ║        4 ║ Ramanujan    ║
║             3 ║        5 ║ Grothendieck ║
║             3 ║        5 ║ Neumann      ║
║             3 ║        6 ║ Grothendieck ║
║             3 ║        6 ║ Tao          ║
╚═══════════════╩══════════╩══════════════╝

डेमो: db <> फिडल

प्रदर्शन जांच

माइकल ग्रीन के उत्तर में दिए गए विस्तारित परीक्षण डेटा सेट का उपयोग करते हुए , मेरे लैपटॉप पर समय * हैं:

╔═════════════╦════════╗
║ Record Keys ║  Time  ║
╠═════════════╬════════╣
║ 10k         ║ 2s     ║
║ 100k        ║ 12s    ║
║ 1M          ║ 2m 30s ║
╚═════════════╩════════╝

* Microsoft SQL सर्वर 2017 (RTM-CU13), डेवलपर संस्करण (64-बिट), विंडोज 10 प्रो, 16 जीबी रैम, एसएसडी, 4 कोर हाइपरथ्रेडेड i7, 2.4GHz नाममात्र।


यह एक कमाल का जवाब है। जैसा कि मेरे प्रश्न में बताया गया है, यह "बड़े दिनों" के लिए बहुत धीमा है; लेकिन यह मेरे छोटे दिनों के लिए बहुत अच्छा है। Of2.5 मिलियन पंक्तियों की मेरी तालिका को चलाने में लगभग 5 घंटे लगे।
बास्केटबॉलफैन

10

यह समस्या आइटमों के बीच के लिंक के बारे में है। यह इसे रेखांकन और ग्राफ प्रसंस्करण के दायरे में रखता है । विशेष रूप से, संपूर्ण डेटासेट एक ग्राफ बनाता है और हम उस ग्राफ के घटकों की तलाश करते हैं । यह प्रश्न से नमूना डेटा के एक भूखंड द्वारा चित्रित किया जा सकता है।

यहां छवि विवरण दर्ज करें

प्रश्न कहता है कि हम उस मूल्य को साझा करने वाली अन्य पंक्तियों को खोजने के लिए GroupKey या RecordKey का अनुसरण कर सकते हैं। तो हम दोनों को एक ग्राफ में वर्टेक्स के रूप में मान सकते हैं। सवाल यह बताने के लिए जाता है कि GroupKeys 1-3 के पास समान SupergroupKey कैसे है। इसे पतली रेखाओं से जुड़े बाईं ओर के क्लस्टर के रूप में देखा जा सकता है। चित्र मूल डेटा द्वारा गठित दो अन्य घटकों (SupergroupKey) को भी दर्शाता है।

SQL सर्वर में T-SQL में निर्मित कुछ ग्राफ प्रोसेसिंग क्षमता है। इस समय, यह काफी कम है, और इस समस्या से मददगार नहीं है। एसक्यूएल सर्वर में आर और पायथन को कॉल करने की क्षमता भी है, और उनके लिए उपलब्ध पैकेजों के समृद्ध और मजबूत सूट। ऐसा ही एक है इग्राफ । यह "बड़े रेखांकन के तेजी से निपटने, लाखों कोने और किनारों ( लिंक ) के साथ" के लिए लिखा गया है ।

R और igraph का उपयोग करके मैं स्थानीय परीक्षण 1 में 2 मिनट 22 सेकंड में एक मिलियन पंक्तियों को संसाधित करने में सक्षम था । यह इस तरह से वर्तमान सबसे अच्छा समाधान के खिलाफ तुलना करता है:

Record Keys     Paul White  R               
------------    ----------  --------
Per question    15ms        ~220ms
100             80ms        ~270ms
1,000           250ms       430ms
10,000          1.4s        1.7s
100,000         14s         14s
1M              2m29        2m22s
1M              n/a         1m40    process only, no display

The first column is the number of distinct RecordKey values. The number of rows
in the table will be 8 x this number.

1M पंक्तियों को संसाधित करते समय, ग्राफ़ को लोड करने और संसाधित करने और तालिका को अपडेट करने के लिए 1m40s का उपयोग किया गया था। आउटपुट के साथ SSMS परिणाम तालिका को पॉप्युलेट करने के लिए 42s आवश्यक थे।

टास्क मैनेजर का अवलोकन जबकि 1M पंक्तियों को संसाधित करने के बारे में सुझाव दिया गया था कि लगभग 3 जीबी की कार्यशील मेमोरी की आवश्यकता थी। यह इस सिस्टम पर पेजिंग के बिना उपलब्ध था।

मैं पुनरावर्ती CTE दृष्टिकोण के Ypercube के मूल्यांकन की पुष्टि कर सकता हूं। कुछ सौ रिकॉर्ड कीज़ के साथ इसने 100% सीपीयू और सभी उपलब्ध रैम का उपभोग किया। आखिरकार tempdb 80GB से अधिक हो गया और SPID क्रैश हो गया।

मैंने SupergroupKey कॉलम के साथ पॉल की तालिका का उपयोग किया ताकि समाधानों के बीच उचित तुलना हो।

किसी कारण से R ने Poincaré पर उच्चारण पर आपत्ति जताई। इसे एक सादे "ई" में बदलने से इसे चलाने की अनुमति मिली। मैंने जांच नहीं की क्योंकि यह हाथ में समस्या के लिए जर्मन नहीं है। मुझे यकीन है कि एक समाधान है।

यहाँ कोड है

-- This captures the output from R so the base table can be updated.
drop table if exists #Results;

create table #Results
(
    Component   int         not NULL,
    Vertex      varchar(12) not NULL primary key
);


truncate table #Results;    -- facilitates re-execution

declare @Start time = sysdatetimeoffset();  -- for a 'total elapsed' calculation.

insert #Results(Component, Vertex)
exec sp_execute_external_script   
    @language = N'R',
    @input_data_1 = N'select GroupKey, RecordKey from dbo.Example',
    @script = N'
library(igraph)
df.g <- graph.data.frame(d = InputDataSet, directed = FALSE)
cpts <- components(df.g, mode = c("weak"))
OutputDataSet <- data.frame(cpts$membership)
OutputDataSet$VertexName <- V(df.g)$name
';

-- Write SuperGroupKey to the base table, as other solutions do
update e
set
    SupergroupKey = r.Component
from dbo.Example as e
inner join #Results as r
    on r.Vertex = e.RecordKey;

-- Return all rows, as other solutions do
select
    e.SupergroupKey,
    e.GroupKey,
    e.RecordKey
from dbo.Example as e;

-- Calculate the elapsed
declare @End time = sysdatetimeoffset();
select Elapse_ms = DATEDIFF(MILLISECOND, @Start, @End);

यही आर कोड करता है

  • @input_data_1 SQL सर्वर किसी तालिका से R कोड में डेटा कैसे स्थानांतरित करता है और इसे InputDataSet नामक R डेटाफ़्रेम में अनुवाद करता है।

  • library(igraph) आर निष्पादन वातावरण में पुस्तकालय आयात करता है।

  • df.g <- graph.data.frame(d = InputDataSet, directed = FALSE)डेटा को एक igraph ऑब्जेक्ट में लोड करें। यह एक अप्रत्यक्ष ग्राफ़ है क्योंकि हम समूह से लिंक को रिकॉर्ड करने या समूह में रिकॉर्ड करने के लिए अनुसरण कर सकते हैं। InputDataSet R को भेजे गए डेटासेट के लिए SQL सर्वर का डिफ़ॉल्ट नाम है।

  • cpts <- components(df.g, mode = c("weak")) असतत उप-ग्राफ़ (घटकों) और अन्य उपायों को खोजने के लिए ग्राफ़ को संसाधित करें।

  • OutputDataSet <- data.frame(cpts$membership)SQL सर्वर को R से एक डेटा फ़्रेम वापस मिलने की उम्मीद है। इसका डिफ़ॉल्ट नाम OutputDataSet है। घटकों को "सदस्यता" नामक एक वेक्टर में संग्रहीत किया जाता है। यह कथन वेक्टर को डेटा फ्रेम में अनुवाद करता है।

  • OutputDataSet$VertexName <- V(df.g)$nameV () ग्राफ में वर्टीकल का एक वेक्टर है - GroupKeys और RecordKeys की एक सूची। यह उन्हें ouput डेटा फ़्रेम में कॉपी करता है, जो वर्टेक्नाम नाम का एक नया कॉलम बनाता है। यह SupergroupKey को अपडेट करने के लिए स्रोत तालिका से मिलान करने के लिए उपयोग की जाने वाली कुंजी है।

मैं कोई आर एक्सपर्ट नहीं हूं। संभवतः यह अनुकूलित किया जा सकता है।

परीक्षण डेटा

ओपी का डेटा सत्यापन के लिए उपयोग किया गया था। स्केल टेस्ट के लिए मैंने निम्नलिखित स्क्रिप्ट का उपयोग किया।

drop table if exists Records;
drop table if exists Groups;

create table Groups(GroupKey int NOT NULL primary key);
create table Records(RecordKey varchar(12) NOT NULL primary key);
go

set nocount on;

-- Set @RecordCount to the number of distinct RecordKey values desired.
-- The number of rows in dbo.Example will be 8 * @RecordCount.
declare @RecordCount    int             = 1000000;

-- @Multiplier was determined by experiment.
-- It gives the OP's "8 RecordKeys per GroupKey and 4 GroupKeys per RecordKey"
-- and allows for clashes of the chosen random values.
declare @Multiplier     numeric(4, 2)   = 2.7;

-- The number of groups required to reproduce the OP's distribution.
declare @GroupCount     int             = FLOOR(@RecordCount * @Multiplier);


-- This is a poor man's numbers table.
insert Groups(GroupKey)
select top(@GroupCount)
    ROW_NUMBER() over (order by (select NULL))
from sys.objects as a
cross join sys.objects as b
--cross join sys.objects as c  -- include if needed


declare @c int = 0
while @c < @RecordCount
begin
    -- Can't use a set-based method since RAND() gives the same value for all rows.
    -- There are better ways to do this, but it works well enough.
    -- RecordKeys will be 10 letters, a-z.
    insert Records(RecordKey)
    select
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND())) +
        CHAR(97 + (26*RAND()));

    set @c += 1;
end


-- Process each RecordKey in alphabetical order.
-- For each choose 8 GroupKeys to pair with it.
declare @RecordKey varchar(12) = '';
declare @Groups table (GroupKey int not null);

truncate table dbo.Example;

select top(1) @RecordKey = RecordKey 
from Records 
where RecordKey > @RecordKey 
order by RecordKey;

while @@ROWCOUNT > 0
begin
    print @Recordkey;

    delete @Groups;

    insert @Groups(GroupKey)
    select distinct C
    from
    (
        -- Hard-code * from OP's statistics
        select FLOOR(RAND() * @GroupCount)
        union all
        select FLOOR(RAND() * @GroupCount)
        union all
        select FLOOR(RAND() * @GroupCount)
        union all
        select FLOOR(RAND() * @GroupCount)
        union all
        select FLOOR(RAND() * @GroupCount)
        union all
        select FLOOR(RAND() * @GroupCount)
        union all
        select FLOOR(RAND() * @GroupCount)
        union all
        select FLOOR(RAND() * @GroupCount)
    ) as T(C);

    insert dbo.Example(GroupKey, RecordKey)
    select
        GroupKey, @RecordKey
    from @Groups;

    select top(1) @RecordKey = RecordKey 
    from Records 
    where RecordKey > @RecordKey 
    order by RecordKey;
end

-- Rebuild the indexes to have a consistent environment
alter index iExample on dbo.Example rebuild partition = all 
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, 
      ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);


-- Check what we ended up with:
select COUNT(*) from dbo.Example;  -- Should be @RecordCount * 8
                                   -- Often a little less due to random clashes
select 
    ByGroup = AVG(C)
from
(
    select CONVERT(float, COUNT(1) over(partition by GroupKey)) 
    from dbo.Example
) as T(C);

select
    ByRecord = AVG(C)
from
(
    select CONVERT(float, COUNT(1) over(partition by RecordKey)) 
    from dbo.Example
) as T(C);

मैंने अभी महसूस किया है कि मैंने ओपी की परिभाषा से अनुपात को गलत तरीके से प्राप्त किया है। मुझे विश्वास नहीं है कि यह समय को प्रभावित करेगा। रिकॉर्ड और समूह इस प्रक्रिया के लिए सममित हैं। एल्गोरिथ्म के लिए वे सभी एक ग्राफ में सिर्फ नोड्स हैं।

डेटा के परीक्षण में हमेशा एक ही घटक का गठन किया। मेरा मानना ​​है कि यह डेटा के समान वितरण के कारण है। अगर स्टैटिक 1: 8 अनुपात के बजाय हार्ड-कोडेड पीढ़ी की दिनचर्या में मैंने अनुपात को अलग-अलग करने की अनुमति दी थी तो अधिक संभावना है कि आगे घटक होंगे।



1 मशीन युक्ति: Microsoft SQL सर्वर 2017 (RTM-CU12), डेवलपर संस्करण (64-बिट), विंडोज 10 होम। 16GB रैम, SSD, 4 कोर हाइपरथ्रेडेड i7, 2.8GHz नाममात्र। परीक्षण केवल उस समय चल रहे आइटम थे, जो सामान्य सिस्टम गतिविधि (लगभग 4% सीपीयू) के अलावा अन्य थे।


6

एक पुनरावर्ती CTE विधि - जो कि बड़ी तालिकाओं में बहुत ही अयोग्य होने की संभावना है:

WITH rCTE AS 
(
    -- Anchor
    SELECT 
        GroupKey, RecordKey, 
        CAST('|' + CAST(GroupKey AS VARCHAR(10)) + '|' AS VARCHAR(100)) AS GroupKeys,
        CAST('|' + CAST(RecordKey AS VARCHAR(10)) + '|' AS VARCHAR(100)) AS RecordKeys,
        1 AS lvl
    FROM Example

    UNION ALL

    -- Recursive
    SELECT
        e.GroupKey, e.RecordKey, 
        CASE WHEN r.GroupKeys NOT LIKE '%|' + CAST(e.GroupKey AS VARCHAR(10)) + '|%'
            THEN CAST(r.GroupKeys + CAST(e.GroupKey AS VARCHAR(10)) + '|' AS VARCHAR(100))
            ELSE r.GroupKeys
        END,
        CASE WHEN r.RecordKeys NOT LIKE '%|' + CAST(e.RecordKey AS VARCHAR(10)) + '|%'
            THEN CAST(r.RecordKeys + CAST(e.RecordKey AS VARCHAR(10)) + '|' AS VARCHAR(100))
            ELSE r.RecordKeys
        END,
        r.lvl + 1
    FROM rCTE AS r
         JOIN Example AS e
         ON  e.RecordKey = r.RecordKey
         AND r.GroupKeys NOT LIKE '%|' + CAST(e.GroupKey AS VARCHAR(10)) + '|%'
         -- 
         OR e.GroupKey = r.GroupKey
         AND r.RecordKeys NOT LIKE '%|' + CAST(e.RecordKey AS VARCHAR(10)) + '|%'
)
SELECT 
    ROW_NUMBER() OVER (ORDER BY GroupKeys) AS SuperGroupKey,
    GroupKeys, RecordKeys
FROM rCTE AS c
WHERE NOT EXISTS
      ( SELECT 1
        FROM rCTE AS m
        WHERE m.lvl > c.lvl
          AND m.GroupKeys LIKE '%|' + CAST(c.GroupKey AS VARCHAR(10)) + '|%'
        OR    m.lvl = c.lvl
          AND ( m.GroupKey > c.GroupKey
             OR m.GroupKey = c.GroupKey
             AND m.RecordKeys > c.RecordKeys
              )
          AND m.GroupKeys LIKE '%|' + CAST(c.GroupKey AS VARCHAR(10)) + '|%'
          AND c.GroupKeys LIKE '%|' + CAST(m.GroupKey AS VARCHAR(10)) + '|%'
      ) 
OPTION (MAXRECURSION 0) ;

Dbfiddle.uk में परीक्षण किया गया

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.