SQL HierarchyID - 返回属于匹配查询词的后代和祖先

时间:2014-03-27 09:50:21

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

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

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

我的桌子是这样的:

ID         Hierarchy            Name
1            /                  Products  
2            /1/                Cars
3            /1/1/              Red 
4            /1/2/              Blue
5            /2/                Bike
6            /2/1/              Green
7            /2/2/              Red

我想通过描述进行搜索,所有匹配都应该与他们的后代和祖先一起返回。

e.g。搜索字词='红色'

结果

ID          Hierarchy           Name
1            /                  Products  
2            /1/                Cars
3            /1/1/              Red 
5            /2/                Bike
7            /2/2/              Red

另一个例子:

搜索字词=' bi'

结果(因为bi包含在自行车字符串

ID          Hierarchy           Name
1            /                  Products  
5            /2/                Bike
6            /2/1/              Green
7            /2/2/              Red

非常感谢,

罗布

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

我差点遇到以下问题。但是它只检索所有祖先而不是后代。

WITH Ancestors(Hierarchy, [Name], AncestorId) AS
(
      SELECT
            Hierarchy, [Name], Hierarchy.GetAncestor(1)
      FROM
            dbo.SpecProducts
      WHERE
            Name = 'Chrome'  -- or whatever you need to select that node

      UNION ALL

      SELECT
            ht.Hierarchy, ht.[Name], ht.Hierarchy.GetAncestor(1)
      FROM
            dbo.SpecProducts ht
      INNER JOIN 
            Ancestors a ON ht.Hierarchy = a.AncestorId
)
SELECT DISTINCT *, Hierarchy.ToString() FROM Ancestors

** * *** 编辑07- 3月14日 ** * ** * ** * ** < EM> * *

@Yousseff DAOUI - 在回答您的回答时,我很难将您的代码转换为使用我的表格。以下是我尝试使用我的表格编写代码:

DECLARE @p_name NVARCHAR = 'Gr';

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

,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

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

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

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

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

你知道我怎么能让它发挥作用吗?

非常感谢。

** 第二次编辑 - 07年3月7日 * < / p>

这看起来好一点,但查询真的很慢(我认为它会拉出比它应该更多的行)。是因为HierarchyID&#39; /&#39;正在返回,然后找到根的所有孩子&#39; /&#39;,这就是一切?

DECLARE @p_name NVARCHAR = 'wallpaper';

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

,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.ToString() LIKE tab.Hierarchy.ToString()+'_/')

,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.ToString() LIKE d.Hierarchy.ToString()+'_/')


SELECT *
FROM Temp_parents
UNION
SELECT *
FROM Temp_descendants

** * ** 第3次编辑 - 07年3月7日 * ** * ** *

好的,非常抱歉编辑次数。但现在这似乎是基于优素福的代码。

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

WITH Temp_parents AS (
    SELECT ID, Hierarchy, Name --, 0 _Level
    FROM
        SpecProducts
    WHERE
            Name LIKE '%'+@p_name+'%'
   UNION ALL
    SELECT tab.ID, tab.Hierarchy, tab.Name --, p._Level+1
    FROM
        SpecProducts tab
    INNER JOIN
        Temp_parents p
            ON p.Hierarchy.ToString() LIKE tab.Hierarchy.ToString()+'_/')

,Temp_descendants AS (
    SELECT ID, Hierarchy, Name --, 0 _Level
    FROM
        SpecProducts
    WHERE
            Name LIKE '%'+@p_name+'%'
   UNION ALL
    SELECT tab.ID, tab.Hierarchy, tab.Name --, d._Level-1
    FROM
        SpecProducts tab
    INNER JOIN
        Temp_descendants d
            ON tab.Hierarchy.ToString() LIKE d.Hierarchy.ToString()+'_/')


SELECT *
FROM Temp_parents
UNION
SELECT *
FROM Temp_descendants

3 个答案:

答案 0 :(得分: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

祝你好运:)

答案 1 :(得分:1)

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

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

祝你好运

编辑07-Mar-14 - 为了使用我的桌子,我必须将其修改为以下内容 - 现在看起来效果很好**

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

WITH Temp_parents AS (
    SELECT ID, Hierarchy, Name --, 0 _Level
    FROM
        SpecProducts
    WHERE
            Name LIKE '%'+@p_name+'%'
   UNION ALL
    SELECT tab.ID, tab.Hierarchy, tab.Name --, p._Level+1
    FROM
        SpecProducts tab
    INNER JOIN
        Temp_parents p
            ON p.Hierarchy.ToString() LIKE tab.Hierarchy.ToString()+'_/')

,Temp_descendants AS (
    SELECT ID, Hierarchy, Name --, 0 _Level
    FROM
        SpecProducts
    WHERE
            Name LIKE '%'+@p_name+'%'
   UNION ALL
    SELECT tab.ID, tab.Hierarchy, tab.Name --, d._Level-1
    FROM
        SpecProducts tab
    INNER JOIN
        Temp_descendants d
            ON tab.Hierarchy.ToString() LIKE d.Hierarchy.ToString()+'_/')


SELECT *
FROM Temp_parents
UNION
SELECT *
FROM Temp_descendants

答案 2 :(得分:0)

也许是这样的:

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)的HierarchyID中找到的完整数字的长度以及您选择的搜索词。

  2. 使用(1.)我们对my_tab表(b)进行右连接以获取所有其他值。 第一种情况:如果(a)中的id等于(b)的id,我们取标记1(因为这是我们用搜索项得到的值)。第二种情况:如果标记是1并且(a)的长度> 1(这意味着我们有一个像“/ 2/1 /”的值)和(b)的长度&lt; (a)的长度(表示“/ 2/2 /”有标记= 1,长度为2;“/ 2/1 /”具有相同的长度,但我们不想要他;我们想要“/ 2 / “)你可以在HierarchyID(a)中找到的完整数字大于你在HierarchyID(b)中找到的完整数字(这意味着我们将得到”/ 2 /“值)我们将标记设置为1。情况何时:类似于“第二种情况”。在这里,我们看一下标记= 1和长度为1(“/ 2 /”)的值,以标记我们想要的所有值,如“/ 2/1 /”和“/ 2/2 /”。 / p>

  3. 现在,您可以使用marker = 1获得所需的所有值。