当前位置: 首页 > 面试题库 >

如何在SQL Server中将层次结构关系分组在一起

刘意
2023-03-14
问题内容

我在表“ 示例* 中有一个列名称“ 父级” 和“ 子级” ,以下是表数据 *

|     Parent          |     Child        |
|---------------------|------------------|
|      100            |      101         |
|---------------------|------------------|
|      101            |      102         |
|---------------------|------------------|
|      200            |     201          |
|---------------------|------------------|
|      103            |      102         |
|---------------------|------------------|
|      202            |      201         |
|---------------------|------------------|

如果我将输入设为 100,那么 我应该得到的结果为 100,101,102,103。 由于 100- > 101-> 102->
103
,而且如果我将输入设为 102, 则它也将得到与上述相同的结果。 102- > 101-> 100和102->
103
。我只需要使用存储过程来实现此目的。

以下是我正在尝试的示例代码

CREATE PROCEDURE GetAncestors(@thingID varchar(MAX))
AS
BEGIN
SET NOCOUNT ON;

WITH
CTE
AS
(
    SELECT
        Example.Parent, Example.Child
    FROM Example
    WHERE Parent = @thingID or Child = @thingID

    UNION ALL

    SELECT
        Example.Parent, Example.Child
    FROM
        CTE
        INNER JOIN Example ON Example.Parent = CTE.Child
)
SELECT
    Parent AS Result
FROM CTE

UNION

SELECT
    Child AS Result
FROM CTE
;

END
GO

问题答案:

您尝试的问题是在一开始就进行过滤。如果我是对的,则您希望按数据的升序或后代关系,或它们的混合关系对数据进行聚类(将所有数据分组在一起)。例如,ID100具有child
101,具有另一个child 102,但是102具有父级,103并且您希望该100, 101, 102, 103集合中任何输入的结果均为这四个()。这就是为什么您不能一开始就进行过滤的原因,因为您没有任何方法可以知道哪个关系将在另一个关系中链接在一起。

解决这个问题并不像看起来那么简单,并且您将无法仅通过1次递归来解决它。

以下是我很久以前就将所有这些关系分组在一起的解决方案。请记住,对于大型数据集(超过10万个),可能要花一些时间进行计算,因为它必须首先识别所有组,然后最后选择结果。

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价值100101102103结果是这四个,和值200201并且202结果是这三种。

我很确定这不是一个最佳解决方案,但是它可以提供正确的输出,而且我不需要对其进行调整,因为它可以快速满足我的要求。



 类似资料:
  • 问题内容: 我有一个“班级”,称为: 对于我的项目而言,它们所涉及的所有枚举都非常重要(因为这是类的构造函数中的一个参数)。 我如何使用层次结构/嵌套来实现以下目的: 一种测试an 是否属于A,B或C组的方法。例如,类似于或将是返回true的公共方法。 能够做同样的事情组的子组,和。例如,A组可能有子组,和。然后,我想要一个做某事的方法,例如which 。 一种无需所有复杂名称即可完成所有代码的方

  • 问题内容: 假设您有以下HTML: 请注意,层次结构是平坦的。 现在尝试选择元素的“中间对” 。这可能吗?我真的不知道怎么做。 此选择器仅获取后面的第一个: 但是此选择器将获取正确的元素对 以及我们想要的对之后出现的所有以下元素: 可能吗? 没有JavaScript。 没有标记更改。通用解决方案。允许任意数量的s或s,在这种情况下,数量2是任意的。 我在想使用选择器使用某些方法是可能的,但我似乎无

  • 问题内容: 我有代表文件夹的对象,我想知道是否应该在数据库中代表它们。 一方面,似乎最简单的方法是不表示文件夹对象,而只存储文件夹中包含的对象的路径值。我看到的问题是,您不能保留其后代不包含任何项目的文件夹,这并不是一件大事。我也不清楚如何在不将所有内容预先加载到内存的情况下加载要显示的文件夹层次结构(例如在TreeView中),这可能是性能问题。 另一种方法是拥有一个“文件夹”表,该表引用其父文

  • 5.4.1 层次化体系结构 层次化设计是构造复杂系统的一个基本方法,按此方法设计出的系统具有层次化体系结构。现实世界中这种层次化结构俯拾皆是。例如,一幢高楼总是从最底层打基础开始,一层 一层地加高。又如,我国的行政组织具有街道、区、市、省、中央这样的层次化结构。 计算机软件的各个构件也经常组织成这样的层次体系结构。在层次体系中,下层构件为 上层构件提供服务,上层构件使用下层构件的服务,上层和下层之

  • 问题内容: 我刚刚用ViewController类创建了一个单视图应用程序项目。我想显示一个位于我自己的类中的函数的UIAlertController。 这是我的课程,带有警告。 这是执行警报的ViewController。 这是我得到的,而不是优美的警报。 警告:尝试在Sprint1.AlertController:0x797cc500上显示其视图不在窗口层次结构中的UIAlertControl

  • 我正在为我的项目使用Spring Data JPA(以hibernate作为提供程序),并想知道何时需要实体之间的双向映射? 在我的用例中,我有一个实体,与实体具有关系。目前,我在实体中没有相应的关系。从我所能理解的查看其他帖子,除非我需要通过我的实体/Hibernate删除,否则我可能不需要这样做。我想知道通过双向关系我还能得到什么? 这是我的,不包括访问者: 这是,不包括访问器: 如果我一直在