sql >> データベース >  >> RDS >> Sqlserver

SQLServerで階層関係をグループ化する方法

    あなたの試みの問題は、最初にフィルタリングすることです。私が正しければ、アセンダントまたはディセンダント、あるいはそれらの組み合わせの関係によってデータをクラスター化(すべてグループ化)したいと考えています。たとえば、ID 100 子があります101 、別の子があります102 、ただし102 親が103 結果をこれらの4つ(100, 101, 102, 103)にします。 )そのセットに含まれるすべての入力に対して。これが、どの関係が別の関係全体に連鎖するかを知る手段がないため、最初にフィルタリングできない理由です。

    これを解決するのは見た目ほど簡単ではなく、1回の再帰で解決することはできません。

    以下は、これらすべての関係をグループ化するために私がずっと前に作成したソリューションです。大規模なデータセット(100kを超える)の場合、最初にすべてのグループを識別し、最後に結果を選択する必要があるため、計算に時間がかかる場合があることに注意してください。

    CREATE PROCEDURE GetAncestors(@thingID INT)
    AS
    BEGIN
    
        SET NOCOUNT ON
    
        -- Load your data
        IF OBJECT_ID('tempdb..#TreeRelationship') IS NOT NULL
            DROP TABLE #TreeRelationship
    
        CREATE TABLE #TreeRelationship (
            RelationID INT IDENTITY(1,1) PRIMARY KEY NONCLUSTERED,
            Parent INT,
            Child INT,
            GroupID INT)
    
        INSERT INTO #TreeRelationship (
            Parent,
            Child)
        SELECT
            Parent = D.Parent,
            Child = D.Child
        FROM
            Example AS D
        UNION -- Data has to be loaded in both ways (direct and reverse) for algorithm to work correctly
        SELECT
            Parent = D.Child,
            Child = D.Parent
        FROM
            Example AS D   
    
    
        -- Start algorithm
        IF OBJECT_ID('tempdb..#FirstWork') IS NOT NULL
            DROP TABLE #FirstWork
    
        CREATE TABLE #FirstWork (
            Parent INT,
            Child INT,
            ComponentID INT)
    
        CREATE CLUSTERED INDEX CI_FirstWork ON #FirstWork (Parent, Child)
    
        INSERT INTO #FirstWork (
            Parent, 
            Child,
            ComponentID)
        SELECT DISTINCT 
            Parent = T.Parent,
            Child = T.Child, 
            ComponentID = ROW_NUMBER() OVER (ORDER BY T.Parent, T.Child)
        FROM 
            #TreeRelationship AS T
    
    
        IF OBJECT_ID('tempdb..#SecondWork') IS NOT NULL
            DROP TABLE #SecondWork
    
        CREATE TABLE #SecondWork (
            Component1 INT,
            Component2 INT)
    
        CREATE CLUSTERED INDEX CI_SecondWork ON #SecondWork (Component1)
    
    
        DECLARE @v_CurrentDepthLevel INT = 0
    
        WHILE @v_CurrentDepthLevel < 100 -- Relationships depth level can be controlled with this value
        BEGIN
    
            SET @v_CurrentDepthLevel = @v_CurrentDepthLevel + 1
    
            TRUNCATE TABLE #SecondWork
    
            INSERT INTO #SecondWork (
                Component1,
                Component2)
            SELECT DISTINCT
                Component1 = t1.ComponentID,
                Component2 = t2.ComponentID
            FROM 
                #FirstWork t1
                INNER JOIN #FirstWork t2 on 
                    t1.child = t2.parent OR 
                    t1.parent = t2.parent
            WHERE
                t1.ComponentID <> t2.ComponentID
    
            IF (SELECT COUNT(*) FROM #SecondWork) = 0
                BREAK
    
            UPDATE #FirstWork SET 
                ComponentID = CASE WHEN items.ComponentID < target THEN items.ComponentID ELSE target END
            FROM 
                #FirstWork items
                INNER JOIN (
                    SELECT
                        Source = Component1, 
                        Target = MIN(Component2)
                    FROM
                        #SecondWork
                    GROUP BY
                        Component1
                ) new_components on new_components.source = ComponentID
    
    
            UPDATE #FirstWork SET
                ComponentID = target
            FROM #FirstWork items
                INNER JOIN(
                    SELECT
                        source = component1, 
                        target = MIN(component2)
                    FROM
                        #SecondWork
                    GROUP BY
                        component1
                ) new_components ON new_components.source = ComponentID
    
        END
    
        ;WITH Groupings AS
        (
            SELECT 
                parent,
                child,
                group_id = DENSE_RANK() OVER (ORDER BY ComponentID  DESC)
            FROM
                #FirstWork
        )
        UPDATE FG SET
            GroupID = IT.group_id
        FROM
            #TreeRelationship FG
            INNER JOIN Groupings IT ON
                IT.parent = FG.parent AND
                IT.child = FG.child
    
    
        -- Select the proper result
        ;WITH IdentifiedGroup AS
        (
            SELECT TOP 1
                T.GroupID
            FROM
                #TreeRelationship AS T
            WHERE
                T.Parent = @thingID
        )
        SELECT DISTINCT
            Result = T.Parent
        FROM
            #TreeRelationship AS T
            INNER JOIN IdentifiedGroup AS I ON T.GroupID = I.GroupID
    
    END
    

    @thingIDで表示されます 値の100101102 および103 結果はこれらの4つであり、値は200201 および202 結果はこれら3つです。

    これは最適なソリューションではないと確信していますが、正しい出力が得られ、要件に合わせて高速に動作するため、調整する必要はありませんでした。



    1. postgreSQL:行を複製する方法

    2. SQLで列の名前を変更する方法

    3. postgres列からバイナリデータを選択する方法

    4. password_hash、password_verify、MySQLの誤解?