在位置树中查找联系人

时间:2013-02-18 16:09:17

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

我有下表:

位置:

ID Name        Depth ParentID    HierachyPath       ContactID
1  US            0     NULL           \                25
2  Florida       1     1             \001             NULL
3  Miami         2     2             \001\001         NULL
4  Dade County   3     3             \001\001\001     NULL
5  Orlando       2     2             \001\002         15
6  County1       3     3             \001\002\001     12

我将获得一个位置ID,例如4.我将立即将多个位置ID传递给我,因此我可能需要一次找到1个位置或1000个位置的联系人。如果在位置4处没有联系人,我需要沿着HierachyPath向上行进,直到找到联系人。关于如何做到这一点的任何想法?

3 个答案:

答案 0 :(得分:1)

您可以使用递归公用表表达式向上走,直到找到具有联系人的父级。例如:

; with  CTE as
        (
        select  ID
        ,       ContactID
        ,       ParentID
        ,       ID BaseID
        ,       1 as Level
        from    Location
        where   ID in (4,5)
        union all
        select  parent.ID
        ,       parent.ContactID
        ,       parent.ParentID
        ,       child.BaseID
        ,       child.Level + 1
        from    Location parent
        join    CTE child
        on      parent.ID = child.ParentID
        -- Stop when we have a contact          
        where   child.ContactID is null
        )
select  CTE.BaseID as ID
,       CTE.ContactID
from    CTE
where   ContactID is not null;

Example at SQL Fiddle.

答案 1 :(得分:0)

我不确定您的数据是否正确,因为佛罗里达州在给定的数据列表中没有ParentId。我认为以下查询将为您提供所需的结果。使用CTE

SQL-FIDDLE-DEMO

declare @myId int = 4

;with cte as (
  select id,pid, cid
  from L where  id = @myId
  union all
  select L.id,L.pid,L.cid
  from cte join L on cte.pid = L.id 
  where cte.cid is null
)
select top (1) cid from cte
order by pid

仅当您传递单个ID时才有效,而不是您更新的问题。几乎没有什么变化可以使它适用于此。 @Andomar已经解决了上述问题。

答案 2 :(得分:0)

我添加了第二个解决方案(多个ID)

第一个解决方案

CREATE TABLE CocoJamboTable (
    ID INT PRIMARY KEY,
    Name NVARCHAR(50) NOT NULL,
    HierachyPath HIERARCHYID NOT NULL,
    ContactID INT NULL -- REFERENCES .... (ContactID)
);
INSERT INTO dbo.CocoJamboTable (ID, Name, HierachyPath, ContactID)
VALUES 
 (1, 'US              ', '/',           25)
,(2, 'Florida         ', '/1/',         NULL)
,(3, 'Miami           ', '/1/1/',       NULL)
,(4, 'Dade County     ', '/1/1/1/',     NULL)
,(5, 'Orlando         ', '/1/2/',       15)
,(6, 'County1         ', '/1/2/1/',     12);

CREATE UNIQUE INDEX IUN_CocoJamboTable_HierachyPath_#_ContactID
ON dbo.CocoJamboTable(HierachyPath) 
INCLUDE (ContactID);
GO

DECLARE @ID INT=4;

DECLARE @ContactID INT = 
(
SELECT d.ContactID
FROM  (
    SELECT  c.ContactID,
            ROW_NUMBER() OVER(
                ORDER BY CASE WHEN c.ContactID IS NOT NULL THEN 1 ELSE 2 END, -- First: NOT NULL ContacIDs 
                b.Number ASC    
            ) AS RowNum
    FROM    dbo.CocoJamboTable a
    JOIN    (VALUES (0),(1),(2),(3),(5),(6),(7),(8),(9),(10)) b(Number) ON a.HierachyPath.GetLevel() >= b.Number
    JOIN    dbo.CocoJamboTable c WITH(FORCESEEK) ON c.HierachyPath=a.HierachyPath.GetAncestor(b.Number)
    -- FORCESEEK: For some reason, the index (IUN_CocoJamboTable_HierachyPath_#_ContactID) is not used by default
    WHERE   a.ID=@ID
) d
WHERE d.RowNum=1
);

SELECT  @ContactID AS [@ContactID];

SELECT  a.ID, 
        c.ContactID,
        a.HierachyPath.ToString() AS HierachyPathAsString, 
        a.HierachyPath.GetLevel() AS HierachyPathGetLevel,
        b.Number,
        a.HierachyPath.GetAncestor(b.Number).ToString() AS HierachyPathGetAncestor_n_Number
FROM    dbo.CocoJamboTable a
JOIN    (VALUES (0),(1),(2),(3),(5),(6),(7),(8),(9),(10)) b(Number) ON a.HierachyPath.GetLevel() >= b.Number
JOIN    dbo.CocoJamboTable c ON c.HierachyPath=a.HierachyPath.GetAncestor(b.Number)
WHERE   a.ID=@ID
ORDER BY b.Number DESC

结果:

@ContactID
-----------
25


ID ContactID HierachyPathAsString HierachyPathGetLevel Number      HierachyPathGetAncestor_n_Number
-- --------- -------------------- -------------------- ----------- --------------------------------
4  25        /1/1/1/              3                    3           /
4  NULL      /1/1/1/              3                    2           /1/
4  NULL      /1/1/1/              3                    1           /1/1/

第二个解决方案(多个ID)

DECLARE @IDs TABLE (ID INT PRIMARY KEY);
INSERT  @IDs 
VALUES (4), (5), (6);

SELECT  *
FROM    @IDs v
OUTER APPLY
(
    SELECT d.ContactID
    FROM  (
        SELECT  c.ContactID,
                ROW_NUMBER() OVER(
                    ORDER BY CASE WHEN c.ContactID IS NOT NULL THEN 1 ELSE 2 END, -- First: NOT NULL ContacIDs 
                    b.Number ASC    
                ) AS RowNum
        FROM    dbo.CocoJamboTable a
        JOIN    (VALUES (0),(1),(2),(3),(5),(6),(7),(8),(9),(10)) b(Number) ON a.HierachyPath.GetLevel() >= b.Number
        JOIN    dbo.CocoJamboTable c WITH(FORCESEEK) ON c.HierachyPath=a.HierachyPath.GetAncestor(b.Number)
        -- FORCESEEK: For some reason, the index (IUN_CocoJamboTable_HierachyPath_#_ContactID) is not used by default
        WHERE   a.ID=v.ID
    ) d
    WHERE d.RowNum=1
) oa;