【问题标题】:Can SQL Server Hierarchy type method IsDescendantOf accept multiple input values?SQL Server Hierarchy 类型方法 IsDescendantOf 可以接受多个输入值吗?
【发布时间】:2012-05-05 08:42:23
【问题描述】:

我使用 HierarchyId 数据类型来存储位置。用户可能受限于位置 (LocationId)。如果用户有超过 1 个位置限制,则必须使用 OR 再次调用 HierarchyId 数据类型的 IsDescendantOf 方法。

示例(按 LocationId 5 和 6 过滤员工):

SELECT * FROM Employee
INNER JOIN Location ON Employee.LocationId = Location.LocationId
WHERE Location.Node.IsDescendantOf((SELECT TOP 1 Node
    FROM Location 
    WHERE LocationId = 5)) = 1
OR 
Location.Node.IsDescendantOf((SELECT TOP 1 Node
    FROM Location 
    WHERE LocationId=6)) = 1`

这适用于 2 个LocationId 过滤器,但如果它增长并且有人说有 10 个过滤器怎么办。 IsDescendantOf 可以像 sql IN 子句一样工作吗?

使用的表格:

CREATE TABLE Location (
LocationId int NOT NULL PRIMARY KEY IDENTITY(1,1),
Name       nvarchar(100) NOT NULL,
[Node]  hierarchyid    NOT NULL,
[ParentNode]  AS ([Node].[GetAncestor]((1))) PERSISTED,
[Level]  AS ([Node].[GetLevel]()) PERSISTED,
);
CREATE TABLE [dbo].[Employee] (
[EmployeeId] [int] PRIMARY KEY IDENTITY(1,1) NOT NULL,
[LocationId] [int] NULL,
[Name] [nvarchar](50) NULL
) ;

【问题讨论】:

    标签: sql sql-server sql-server-2008 hierarchyid


    【解决方案1】:

    注意:我添加了第二种解决方案(第 6 点)。

    1. 您可以使用表变量来存储所有搜索到的位置(例如DECLARE @SearchedAncestorLocation TABLE(LocationId INT PRIMARY KEY))。

    2. 您必须为来自@SearchedAncestorLocation 的每个位置ID 找到HIERARCHYID's 节点。

    3. 您必须使用此过滤器对员工的位置进行INNER JOINemployee_location.Node.IsDescendantOf(searched_location.Node) = 1

    4. 我认为您应该在Location 表中添加一个UNIQUE(Node) 约束,以防止重复位置(重复节点)。

    5. 第一个解决方案demo here

      声明@位置表( LocationId int 非空主键, 名称 nvarchar(100) 非空, [节点] hierarchyid 非空, 唯一([节点]) );

      声明@Employee 表 ( [EmployeeId] [int] 主键, [LocationId] [int] NULL, [名称] nvarchar NULL );

      插入@Location(LocationId,名称,[节点]) 值(1,N'A','/1/'), (2, N'AA', '/1/1/'), ( 3, N'AA-1', '/1/1/1/'), --

           ( 9, N'B',     '/2/'),
           (10, N'BA',    '/2/1/'),
           (11, N'BA-1',  '/2/1/1/'), -- <-- Second employee @ BA-1
           (12, N'BA-2',  '/2/1/2/'),
           (13, N'BA-3',  '/2/1/3/'),
           (14, N'BB',    '/2/2/'),
           (15, N'BB-1',  '/2/2/1/');
      

      INSERT @Employee(EmployeeId, [Name], LocationId) 价值观 (1, N'Ion Ionescu', 3), -- AA-1 (2,N'Geo Georgescu',11); -- BA-1

      DECLARE @SearchedAncestorLocation TABLE(LocationId INT PRIMARY KEY); 插入@SearchedAncestorLocation 值 (1),--A (2),--AA (3)、--AA-1 (9),--B (10), --BA (14); --BB

      选择 e.*, el.Name AS EmpLocationName, el.Node.ToString() 作为 EmpLocationHID, s.LocationId AS SearchedLocationId, sl.Name AS SearchedLocationName, sl.Node.ToString() AS SearchedLocationHID 来自@Employee e INNER JOIN @Location el ON e.LocationId = el.LocationId INNER JOIN @Location sl ON el.Node.IsDescendantOf(sl.Node) = 1 INNER JOIN @SearchedAncestorLocation s ON sl.LocationId = s.LocationId --AND sl.Node el.Node

    结果:

    EmployeeId LocationId  Name          EmpLocationName EmpLocationHID SearchedLocationId SearchedLocationName SearchedLocationHID
    ---------- ----------- ------------- --------------- -------------- ------------------ -------------------- -------------------
    1          3           Ion Ionescu   AA-1            /1/1/1/        1                  A                    /1/
    1          3           Ion Ionescu   AA-1            /1/1/1/        2                  AA                   /1/1/
    1          3           Ion Ionescu   AA-1            /1/1/1/        3                  AA-1                 /1/1/1/
    2          11          Geo Georgescu BA-1            /2/1/1/        9                  B                    /2/
    2          11          Geo Georgescu BA-1            /2/1/1/        10                 BA                   /2/1/
    

    取消注释最后一行的结果 (AND sl.Node &lt;&gt; el.Node):

    EmployeeId LocationId  Name          EmpLocationName EmpLocationHID SearchedLocationId SearchedLocationName SearchedLocationHID
    ---------- ----------- ------------- --------------- -------------- ------------------ -------------------- -------------------
    1          3           Ion Ionescu   AA-1            /1/1/1/        1                  A                    /1/
    1          3           Ion Ionescu   AA-1            /1/1/1/        2                  AA                   /1/1/
    2          11          Geo Georgescu BA-1            /2/1/1/        9                  B                    /2/
    2          11          Geo Georgescu BA-1            /2/1/1/        10                 BA                   /2/1/
    
    1. 第二种解决方案

      选择 e.EmployeeId, e.LocationId, e.姓名 来自@Employee e INNER JOIN @Location el ON e.LocationId = el.LocationId 存在于何处 ( 选择 * FROM @SearchedAncestorLocation INNER JOIN @Location sl ON s.LocationId = sl.LocationId WHERE el.Node.IsDescendantOf(sl.Node) = 1 --AND el.Node sl.Node );

    【讨论】:

    • 这太棒了!非常感谢。我注意到你得到了重复的 Employee 实体(即 3 x 相同的 EmployeeId)。 Group By 似乎没有用。
    • 我用它来过滤重复的SELECT * FROM dbo.Employee e INNER JOIN ( SELECT DISTINCT ( dbo.Employee.EmployeeId ) FROM Employee INNER JOIN Location el ON Punches.LocationId = el.LocationId INNER JOIN Location sl ON el.Node.IsDescendantOf(sl.Node) = 1 INNER JOIN @SearchFilters s ON sl.LocationId = s.LocationId ) filtered ON e.EmployeeId = filtered.EmployeeId
    • @JeremyChild:我不清楚你真正需要什么记录。我的查询显示了这些员工的所有员工和所有祖先(来自@SearchedAncestorLocation)。如果您想(仅)显示员工信息,您可以尝试第二种解决方案。它应该提供更好的性能(更少的逻辑读取)
    • 感谢您的无上天才!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多