【问题标题】:SQL HierarchyID - Return Descendants and Ancestors that belong to matching query termSQL HierarchyID - 返回属于匹配查询词的后代和祖先
【发布时间】:2014-03-27 09:50:21
【问题描述】:

感谢您抽出宝贵时间阅读这篇文章,非常感谢所有帮助。

我需要帮助创建一个 SQL 查询,该查询返回匹配或包含我的 WHERE 查询字符串的项目的所有祖先和后代。

我的桌子是这样的:

ID 层次结构 名称 1 / 产品 2 /1/ 汽车 3 /1/1/ 红色 4 /1/2/ 蓝色 5 /2/ 自行车 6 /2/1/ 绿色 7 /2/2/ 红色

我想按描述搜索,所有匹配项都应该连同他们的后代和祖先一起返回。

例如搜索词 = '红色'

结果

ID 层次结构 名称 1 / 产品 2 /1/ 汽车 3 /1/1/ 红色 5 /2/ 自行车 7 /2/2/ 红色

另一个例子:

搜索词 = 'bi'

Result(因为 bi 包含在 Bike 字符串中

ID 层次结构 名称 1 / 产品 5 /2/ 自行车 6 /2/1/ 绿色 7 /2/2/ 红色

非常感谢,

罗伯

********** 编辑 28 -Mar-14 ***********

下面的查询我快到了。但是它只检索所有祖先而不是后代。

WITH Ancestors(Hierarchy, [Name], AncestorId) AS ( 选择 层次结构,[名称],层次结构.GetAncestor(1) 从 dbo.SpecProducts 在哪里 Name = 'Chrome' -- 或选择该节点所需的任何内容 联合所有 选择 ht.Hierarchy, ht.[名称], ht.Hierarchy.GetAncestor(1) 从 dbo.SpecProducts ht 内部联接 祖先 a ON ht.Hierarchy = a.AncestorId ) SELECT DISTINCT *, Hierarchy.ToString() FROM Ancestors

****** 2014 年 3 月 7 日编辑 *************

@Yousseff DAOUI - 作为对您的回答的回应,我很难将您的代码转换为与我的表格一起使用。下面是我尝试让您的代码与我的表一起使用:

声明@p_name NVARCHAR = 'Gr';

WITH Temp AS (SELECT * FROM SpecProducts) -- 数据库表

,Temp_parents AS (
    选择 ID、层次结构、名称 --、0 _Level
    从
        温度
    在哪里
            名称 LIKE '%'+@p_name+'%'
   联合所有
    SELECT tab.ID, tab.Hierarchy, tab.Name --, p._Level+1
    从
        温度选项卡
    内部联接
        Temp_parents p
            ON p.Hierarchy LIKE tab.Hierarchy+'_/')

,Temp_descendants AS (
    选择 ID、层次结构、名称 --、0 _Level
    从
        温度
    在哪里
            名称 LIKE '%'+@p_name+'%'
   联合所有
    SELECT tab.ID, tab.Hierarchy, tab.Name --, d._Level-1
    从
        温度选项卡
    内部联接
        Temp_descendants d
            ON tab.Hierarchy LIKE d.Hierarchy+'_/')


选择 *
来自 Temp_parents
联盟
选择 *
FROM Temp_descendants

注意第 3 行如何尝试从名为 SpecProducts 的表中提取数据:

WITH Temp AS (SELECT * FROM SpecProducts) -- DB Table

但是,当我尝试执行代码时,MS SQL Server Management Studio 不断输出以下错误:

消息 403,第 16 级,状态 1,第 3 行 数据类型的运算符无效。运算符等于add,type等于hierarchyid。

你知道我怎样才能让它工作吗?

非常感谢。

** 第二次编辑 - 2014 年 3 月 7 日 *

这似乎好一点,但查询真的很慢(我认为它提取的行数超过了应有的行数)。是不是因为 HierarchyID '/' 被返回,然后找到根 '/' 的所有孩子,这就是一切?

DECLARE @p_name NVARCHAR = '壁纸';

WITH Temp AS (SELECT * FROM SpecProducts) -- 数据库表

,Temp_parents AS (
    选择 ID、层次结构、名称 --、0 _Level
    从
        温度
    在哪里
            名称 LIKE '%'+@p_name+'%'
   联合所有
    SELECT tab.ID, tab.Hierarchy, tab.Name --, p._Level+1
    从
        温度选项卡
    内部联接
        Temp_parents p
            ON p.Hierarchy.ToString() LIKE tab.Hierarchy.ToString()+'_/')

,Temp_descendants AS (
    选择 ID、层次结构、名称 --、0 _Level
    从
        温度
    在哪里
            名称 LIKE '%'+@p_name+'%'
   联合所有
    SELECT tab.ID, tab.Hierarchy, tab.Name --, d._Level-1
    从
        温度选项卡
    内部联接
        Temp_descendants d
            ON tab.Hierarchy.ToString() LIKE d.Hierarchy.ToString()+'_/')


选择 *
来自 Temp_parents
联盟
选择 *
FROM Temp_descendants

***** 第三次编辑 - 2014 年 3 月 7 日 *** ****

好的,对于编辑的数量真的很抱歉。但是现在这似乎基于 Yousseff 的代码。

DECLARE @p_name NVARCHAR(255) = 'natural';

WITH Temp_parents 作为(
    选择 ID、层次结构、名称 --、0 _Level
    从
        规格产品
    在哪里
            名称 LIKE '%'+@p_name+'%'
   联合所有
    SELECT tab.ID, tab.Hierarchy, tab.Name --, p._Level+1
    从
        规格产品选项卡
    内部联接
        Temp_parents p
            ON p.Hierarchy.ToString() LIKE tab.Hierarchy.ToString()+'_/')

,Temp_descendants AS (
    选择 ID、层次结构、名称 --、0 _Level
    从
        规格产品
    在哪里
            名称 LIKE '%'+@p_name+'%'
   联合所有
    SELECT tab.ID, tab.Hierarchy, tab.Name --, d._Level-1
    从
        规格产品选项卡
    内部联接
        Temp_descendants d
            ON tab.Hierarchy.ToString() LIKE d.Hierarchy.ToString()+'_/')


选择 *
来自 Temp_parents
联盟
选择 *
FROM Temp_descendants

【问题讨论】:

  • 你试过我的脚本了吗?

标签: sql sql-server-2008-r2 hierarchy


【解决方案1】:

我有一个解决方案,希望对你有帮助,代码如下:

DECLARE @p_name NVARCHAR(255) = 'b'


IF OBJECT_ID('Temp') IS NOT NULL
    DROP TABLE Temp

IF OBJECT_ID('Temp_parents') IS NOT NULL
    DROP TABLE Temp_parents

IF OBJECT_ID('Temp_descendants') IS NOT NULL
    DROP TABLE Temp_descendants

SELECT *
INTO Temp
FROM  (
SELECT 1 AS ID, '/' AS Hierarchy, 'Products' Name UNION ALL
SELECT 2 AS ID, '/1/' AS Hierarchy, 'Cars' Name UNION ALL
SELECT 3 AS ID, '/1/1/' AS Hierarchy, 'Red' Name UNION ALL
SELECT 4 AS ID, '/1/2/' AS Hierarchy, 'Blue' Name UNION ALL
SELECT 5 AS ID, '/2/' AS Hierarchy, 'Bike' Name UNION ALL
SELECT 6 AS ID, '/2/1/' AS Hierarchy, 'Green' Name UNION ALL
SELECT 7 AS ID, '/2/2/' AS Hierarchy, 'Red' Name) AS T


DECLARE @v_name_1 NVARCHAR(255)= @p_name;

WITH parents AS (
    SELECT ID, Hierarchy, Name, 0 _Level
    FROM
        Temp
    WHERE
            Name LIKE '%'+@v_name_1+'%'
   UNION ALL
    SELECT tab.ID, tab.Hierarchy, tab.Name, p._Level+1
    FROM
        Temp tab
    INNER JOIN
        parents p
            ON p.Hierarchy LIKE tab.Hierarchy+'_/')



select ID, Hierarchy, Name
INTO Temp_parents
from parents


DECLARE @v_name_2 NVARCHAR(255)= @p_name;

WITH descendants AS (
    SELECT ID, Hierarchy, Name, 0 _Level
    FROM
        Temp
    WHERE
            Name LIKE '%'+@v_name_2+'%'
   UNION ALL
    SELECT tab.ID, tab.Hierarchy, tab.Name, d._Level-1
    FROM
        Temp tab
    INNER JOIN
        descendants d
            ON tab.Hierarchy LIKE d.Hierarchy+'_/')



SELECT ID,Hierarchy ,Name
INTO Temp_descendants
FROM
    descendants
WHERE
        ID IS NOT NULL

SELECT *
FROM Temp_parents
UNION
SELECT *
FROM Temp_descendants


IF OBJECT_ID('Temp') IS NOT NULL
    DROP TABLE Temp

IF OBJECT_ID('Temp_parents') IS NOT NULL
    DROP TABLE Temp_parents

IF OBJECT_ID('Temp_descendants') IS NOT NULL
    DROP TABLE Temp_descendants

祝你好运:)

【讨论】:

    【解决方案2】:

    这是基于第一种编写代码的另一种方式,以使代码看起来更容易:)

    DECLARE @p_name NVARCHAR(255) = 'Gr';
    
    WITH Temp AS (
    SELECT 1 AS ID, '/' AS Hierarchy, 'Products' Name UNION ALL
    SELECT 2 AS ID, '/1/' AS Hierarchy, 'Cars' Name UNION ALL
    SELECT 3 AS ID, '/1/1/' AS Hierarchy, 'Red' Name UNION ALL
    SELECT 4 AS ID, '/1/2/' AS Hierarchy, 'Blue' Name UNION ALL
    SELECT 5 AS ID, '/2/' AS Hierarchy, 'Bike' Name UNION ALL
    SELECT 6 AS ID, '/2/1/' AS Hierarchy, 'Green' Name UNION ALL
    SELECT 7 AS ID, '/2/2/' AS Hierarchy, 'Red' Name)
    ,Temp_parents AS (
        SELECT ID, Hierarchy, Name --, 0 _Level
        FROM
            Temp
        WHERE
                Name LIKE '%'+@p_name+'%'
       UNION ALL
        SELECT tab.ID, tab.Hierarchy, tab.Name --, p._Level+1
        FROM
            Temp tab
        INNER JOIN
            Temp_parents p
                ON p.Hierarchy LIKE tab.Hierarchy+'_/')
    
    ,Temp_descendants AS (
        SELECT ID, Hierarchy, Name --, 0 _Level
        FROM
            Temp
        WHERE
                Name LIKE '%'+@p_name+'%'
       UNION ALL
        SELECT tab.ID, tab.Hierarchy, tab.Name --, d._Level-1
        FROM
            Temp tab
        INNER JOIN
            Temp_descendants d
                ON tab.Hierarchy LIKE d.Hierarchy+'_/')
    
    
    SELECT *
    FROM Temp_parents
    UNION
    SELECT *
    FROM Temp_descendants
    

    祝你好运

    2014 年 3 月 7 日编辑 - 为了与我的表格一起使用,我必须将其修改为以下 - 现在看起来效果很好 **

    DECLARE @p_name NVARCHAR(255) = 'natural';
    
    WITH Temp_parents 作为(
        选择 ID、层次结构、名称 --、0 _Level
        从
            规格产品
        在哪里
                名称 LIKE '%'+@p_name+'%'
       联合所有
        SELECT tab.ID, tab.Hierarchy, tab.Name --, p._Level+1
        从
            规格产品选项卡
        内部联接
            Temp_parents p
                ON p.Hierarchy.ToString() LIKE tab.Hierarchy.ToString()+'_/')
    
    ,Temp_descendants AS (
        选择 ID、层次结构、名称 --、0 _Level
        从
            规格产品
        在哪里
                名称 LIKE '%'+@p_name+'%'
       联合所有
        SELECT tab.ID, tab.Hierarchy, tab.Name --, d._Level-1
        从
            规格产品选项卡
        内部联接
            Temp_descendants d
                ON tab.Hierarchy.ToString() LIKE d.Hierarchy.ToString()+'_/')
    
    
    选择 *
    来自 Temp_parents
    联盟
    选择 *
    FROM Temp_descendants

    【讨论】:

    • 非常感谢您的回复。但是,我很难修改您的代码以使用我的数据库表。我试图在这里回复,但字符数太有限,无法发布我的代码。请参阅我在 2014 年 3 月 7 日的主要问题中的编辑。非常感谢。
    • 对不起 Youssefff,我现在似乎有这个工作。请参阅名为 3rd Edit 07-Mar-14 的编辑。非常感谢您的帮助。
    • 好的!乐意效劳!祝你好运
    • 嗯...这实际上似乎不是 100% 工作的。在某些情况下,并非所有后代都被带回。通常,它似乎是找到的搜索词的后代具有更多的子项。但不总是。我不能为我的生活找出原因。我已经使用它进行了测试,并向您的数据表“Temp”添加了更多行,它工作正常。但是在使用我的数据库表时它不能正常工作。
    • 抱歉 Youssef,我不得不删除这个作为答案。它并非在所有情况下都能正常工作。我还不能弄清楚导致问题的确切原因或方法。基本上,并非在所有情况下都返回所有匹配项。此外,并非所有根据查询词找到的产品都将所有祖先一直拉到根“/”。
    【解决方案3】:

    可能是这样的:

    select id, HierarchyID, Description from (
    select b.id, b.HierarchyID, b.Description, 
    case 
    when (a.id = b.id) then 1 
    when (marker = 1 and num_len > 1 and LEN(replace(b.HierarchyID,'/','')) < num_len and numbers > replace(b.HierarchyID,'/','')) then 1
    when (marker = 1 and num_len = 1 and LEN(replace(b.HierarchyID,'/','')) > num_len and numbers < replace(b.HierarchyID,'/','')) then 1
    else 0 end as marker from (
    select id, SUBSTRING(HierarchyID,2,1) as first_num, 1 as marker, replace(HierarchyID,'/','') as numbers, LEN(replace(HierarchyID,'/','')) as num_len
    from my_tab where Description like '%bi%' or HierarchyID = '/') a right join my_tab b on first_num = SUBSTRING(b.HierarchyID,2,1)) c where marker = 1;
    

    对不起我的英语!

    1. 选择 id,你在 HierarchyID 中找到的第一个数字(“/2/1/”是 2),你找到的值的一个“标记”并且你想要保留(标记 = 1),完整您可以在 HierarchyID 中找到的数字(“/2/1/”为 21)以及您在 my_tab 表 (a) 中找到的完整数字的长度以及您选择的搜索词。

    2. 使用 (1.) 我们对 my_tab 表 (b) 进行右连接以获取所有其他值。 第一种情况:如果 (a) 中的 id 与 (b) 中的 id 相等,我们使用标记 1(因为这是我们通过搜索词获得的值)。第二种情况:如果标记为 1 并且 (a) 的长度 > 1(这意味着我们有一个类似 "/2/1/" 的值)并且 (b) 的长度

    3. 现在您有了标记 = 1 所需的所有值。

    【讨论】:

    • 感谢您的意见。不幸的是,我收到以下错误:Msg 8116, Level 16, State 1, Line 1 Argument data type hierarchyid is invalid for argument 1 of substring function。消息 8156,级别 16,状态 1,第 9 行为“c”多次指定了“层次结构”列。
    • 我为列 hierarchyid 定义了 varchar。一切正常。您使用的是哪种数据类型?
    • 将最后一个选择改为:"select c.id, c.HierarchyID, c.Description from ("
    • 我使用 SQL HierarchyID 数据类型作为我的 Hierarchy 值
    • 你在标题中写了它,而我并没有真正阅读它。对不起!如果您对 HierarchyID 列使用 varchar 数据类型和“普通”列,则脚本将起作用;)
    猜你喜欢
    • 1970-01-01
    • 2017-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-19
    • 1970-01-01
    • 2012-04-23
    相关资源
    最近更新 更多