web-dev-qa-db-ja.com

リンクされたすべてのオブジェクトを無向循環グラフにグループ化する方法

顧客とその共同顧客がいるテーブルがあります。たとえば、顧客1には共同顧客2があります。顧客1は顧客3の共同顧客でもあります。

リンクされているすべての顧客をグループ化し、同じGroupCustNoを割り当てようとしています。以下の表1-2はリンクされており、3-1はリンクされています。したがって、2〜3もリンクされています。したがって、以下の表の1から8までのすべての顧客は相互にリンクされ、同じGroupCustNoを持ちます。

 tbl_GroupCustomers:

CustNo   JtCustNo  GroupCustNo    
---      -------     ------   
1           2          null
2           null       null
3           1          null
4           1          null
4           5          null
5           6          null
5           7          null
6           null       null
7           null       null
8           5          null

私が書いた再帰的なストアドプロシージャは以下のとおりです。私はこれを各CustNoのwhileループで呼び出しています:

exec usp_UpdateGroupCustomerNo 1、1ストアドプロシージャはほとんどの顧客で正常に実行されましたが、スローされました

一部のお客様では、再帰制限32がエラーに達しました。これらは、多くの共同顧客を持ち、他の顧客の共同顧客でもある顧客です。

ここでは再帰が機能しないようで、どのように進めるか途方に暮れています。この問題に対処する別の方法があるかどうか教えてください。

CREATE PROCEDURE [dbo].[usp_UpdateGroupCustomerNo]
     @MainCustNo int, @GrpNo int
AS 
    declare @JtCustNo int; declare @MainCustNo2 int;

if exists(select 1 from tbl_GroupCustomer 
          where CustNo = @MainCustNo and groupcustomernumber is null)
begin    
    update tbl_GroupCustomer 
    set groupcustomernumber = @grpno 
    where CustNo = @MainCustNo 
      and groupcustomernumber is null

    DECLARE db_cursor CURSOR LOCAL FOR  
         select JtCustNo 
         from tbl_GroupCustomer 
         where CustNo = @MainCustNo and JtCustNo is not null

    OPEN db_cursor   

    FETCH NEXT FROM db_cursor INTO @JtCustNo   

    WHILE @@FETCH_STATUS = 0   
    BEGIN   
        select @JtCustNo as JtCustNo      

        exec usp_UpdateGroupCustomerNo @JtCustNo, @GrpNo

        DECLARE db_cursor2 CURSOR LOCAL FOR  
            select CustNo 
            from tbl_GroupCustomer 
            where JtCustNo = @JtCustNo 
              and groupcustomernumber is null

        OPEN db_cursor2

        FETCH NEXT FROM db_cursor2 INTO @MainCustNo2   

        WHILE @@FETCH_STATUS = 0   
        BEGIN   
            if exists(select 1 from tbl_GroupCustomer 
                      where CustNo = @MainCustNo2 
                        and groupcustomernumber is null)
            begin     
                exec usp_UpdateGroupCustomerNo @MainCustNo2, @GrpNo
            end

            FETCH NEXT FROM db_cursor INTO @MainCustNo2   
        END   

        CLOSE db_cursor2   
        DEALLOCATE db_cursor2

        FETCH NEXT FROM db_cursor INTO @JtCustNo   
END  

CLOSE db_cursor   
DEALLOCATE db_cursor

終わり

5
Arcturus

この回答は、プロシージャのどこかでOPTION (MAXRECURSION 0)を使用して再帰制限を削除できないことを前提としています。

再帰的なソリューションを試すのにしばらく時間を費やしましたが、何を試しても、再帰の制限を下回ることを保証できませんでした。常に、より多くの反復を必要とするチェーンを構築できました。

だから、ここにテスト用の私のDDLと一緒に簡単なループソリューションがあります:

/*
IF OBJECT_ID('tempdb..#tbl_GroupCustomers') IS NOT NULL
DROP TABLE #tbl_GroupCustomers

CREATE TABLE #tbl_GroupCustomers
(CustNo int,
JtCustNo int,
GroupCustNo int)

INSERT #tbl_GroupCustomers
(CustNo, JtCustNo)
VALUES
(1,2),
(2,NULL),
(3,1),
(4,1),
(4,5),
(5,3),
(6,7),
(7,8),
(8,6),
(9,null)

SELECT *
FROM #tbl_GroupCustomers
*/


DECLARE @GN int = 1
DECLARE @seedCust int
DECLARE @count1 int, @count2 int

--Outer loop: iterate group numbers
WHILE EXISTS(SELECT * FROM #tbl_GroupCustomers WHERE GroupCustNo IS NULL)
BEGIN
--Find next ungrouped customer
SET @seedCust = (SELECT TOP 1 CustNo FROM #tbl_GroupCustomers WHERE GroupCustNo IS NULL)
--reset counts
SET @count1 = 0
SET @count2 = 1

--Start with a customer
--Give that customer a Group number
UPDATE GC
SET GroupCustNo = @GN
FROM #tbl_GroupCustomers GC
WHERE CustNo = @seedCust

    --Inner loop
    WHILE @count1 <> @count2
    BEGIN

    --How many records?
    SELECT @count1 = COUNT(*) FROM #tbl_GroupCustomers WHERE GroupCustNo = @GN

    --Give all linked records the same GN
    UPDATE GC2
    SET GC2.GroupCustNo = @GN
    FROM (SELECT CustNo, JtCustNo
        FROM #tbl_GroupCustomers
        WHERE GroupCustNo = @GN
        ) GC1
    INNER JOIN #tbl_GroupCustomers GC2
    ON GC1.CustNo = GC2.JtCustNo
    OR GC1.JtCustNo = GC2.CustNo
    OR GC1.CustNo = GC2.CustNo
    OR GC1.JtCustNo = GC2.JtCustNo

    --How many records? If same, it ends loop
    SELECT @count2 = COUNT(*) FROM #tbl_GroupCustomers WHERE GroupCustNo = @GN
    END

SET @GN +=1
END

開始テーブル:

CustNo      JtCustNo    GroupCustNo
----------- ----------- -----------
1           2           NULL
2           NULL        NULL
3           1           NULL
4           1           NULL
4           5           NULL
5           3           NULL
6           7           NULL
7           8           NULL
8           6           NULL
9           NULL        NULL

結果:

CustNo      JtCustNo    GroupCustNo
----------- ----------- -----------
1           2           1
2           NULL        1
3           1           1
4           1           1
4           5           1
5           3           1
6           7           2
7           8           2
8           6           2
9           NULL        3
1
Forrest