如何找到以前的&单个查询中的数据前向关系

时间:2014-05-20 06:52:32

标签: sql-server oracle

我有桌子:

ID      Person_ID        Person_Relative_ID
 1         10                   20
 2         20                   30
 3         13                   15
 4         30                   40 
 5         55                   56
 6         40                   50   

在这里,我们可以看到人与人。 person_relative_id链条大概是10 - 20 - 30 - 40 - 50

现在,如果用户搜索Person_Relative_ID = 20的person_id,那么结果就像:[所有关系]

          Person_Relative_ID
                  10
                  30
                  40 
                  50 

或者用户想要Person_Relative_ID搜索person_id = 40。然后结果像

 Person_Relative_ID
            10
            20
            30 
            50 

或者用户想要Person_Relative_ID搜索person_id = 50。然后结果像

Person_Relative_ID
      10
      20
      30
      40

任何建议都非常感谢。

4 个答案:

答案 0 :(得分:1)

嗯,我不确定这是性能最有效的解决方案,而且我喜欢处理单个递归CTE而不是两个,但这至少有效。

用你的真实表替换对我用来测试它的临时表的引用。但它的作用是,它使用两个递归CTE来查找上面的所有引用(CTEUp)和下面(CTEDown)您的ID,然后按顺序显示它们,除了您搜索到的ID。

注意:这适用于SQL Server,而非Oracle。

-- Creating dummy variables for testing
DECLARE @PERSONS TABLE (ID INT IDENTITY(1,1), Person_ID INT, Person_Relative_ID INT)

INSERT INTO @PERSONS VALUES (10,20), (20,30), (13,15), (30,40), (55,56), (40,50)

-- Variable for searched ID, the actual script begins here
DECLARE @SEARCHED_ID INT
SET @SEARCHED_ID = 20

;WITH CTEUp AS 
            -- Fetching all relations above the ID
        (SELECT Person_ID
        FROM @PERSONS
        WHERE Person_ID = @SEARCHED_ID
        UNION ALL   
        SELECT Person_Relative_ID
        FROM @PERSONS P
        JOIN CTEUp C ON C.Person_ID = P.Person_ID
            AND P.Person_Relative_ID > C.Person_ID)
    , CTEDown AS 
            -- Fetching all relations below the ID
        (SELECT Person_ID
        FROM @PERSONS
        WHERE Person_Relative_ID = @SEARCHED_ID
        UNION ALL   
        SELECT P.Person_ID
        FROM @PERSONS P
        JOIN CTEDown C ON C.Person_ID = P.Person_Relative_ID
            AND P.Person_ID < C.Person_ID)  
-- Showing results
SELECT Person_ID
FROM 
    (SELECT * 
    FROM CTEDown
    UNION ALL
    SELECT *
    FROM CTEUp) SRC
WHERE Person_ID <> @SEARCHED_ID --... minus the ID, as per your example
ORDER BY Person_ID ASC

答案 1 :(得分:1)

刚刚尝试了Oracle,可能会更好但仍然有效

with tab(ID, Person_ID, Person_Relative_ID) as (
          SELECT 1, 10, 20 from dual union all
          SELECT 2, 20, 30 from dual union all
          SELECT 3, 13, 15 from dual union all
          SELECT 4, 30, 40 from dual union all
          SELECT 5, 55, 56 from dual union all
          SELECT 6, 40, 50 from dual),
---------
--End of data preparation
---------
filter_tab as (
          select 40 as id from dual), --> Put the search id here
final_tab(person_id) as (
          select person_id
            from tab
           start with person_relative_id = (select id from filter_tab)
         connect by prior person_id = person_relative_id
           union 
          select person_relative_id
            from tab
           start with person_id = (select id from filter_tab)
         connect by prior person_relative_id = person_id)
select * 
  from final_tab 
 where not exists (select 'x' 
                     from filter_tab 
                    where id = person_id )
 order by 1;

输出:

PERSON_ID
---------
10
20
30
50

我刚刚从起点遍历到两端,结果联合并排除了搜索ID。

答案 2 :(得分:0)

在SQL Server 2012及更高版本中,您可以使用LAGLEAD功能。 这是一个示例:

SELECT TOP 1000 [n], LAG([n]) OVER(ORDER BY [n] ), LEAD([n]) OVER (ORDER BY [n])
FROM [dbo].[Nums]

它会产生结果:

n   LAG LEAD
1   NULL    2
2   1   3
3   2   4
4   3   5
5   4   6

答案 3 :(得分:0)

如果您使用的是SQL2005及更高版本,SQL Recursive CTE结构化SQL查询可用于查询分层数据模型(如您的情况)

以下查询还使用multiple CTE queries来防止使用不必要的临时表或表变量

declare @id smallint = 50 

;with cte as (
    select Person_ID, Person_Relative_ID 
    from PersonRelative 
    where Person_ID = @id OR Person_Relative_ID = @id
    union all
    select P.Person_ID, P.Person_Relative_ID 
    from PersonRelative P
    inner join CTE on CTE.Person_ID = P.Person_Relative_ID
), cte2 as (
    select Person_ID from cte
    union
    select Person_Relative_ID from cte
), cte3 as (
    select Person_ID, Person_Relative_ID 
    from PersonRelative 
    where Person_ID = @id OR Person_Relative_ID = @id
    union all
    select P.Person_ID, P.Person_Relative_ID 
    from PersonRelative P
    inner join CTE3 on CTE3.Person_Relative_ID = P.Person_ID
), cte4 as (
    select Person_ID from cte3
    union
    select Person_Relative_ID from cte3
)
select * from cte4 where Person_ID <> @id
union
select * from cte2 where Person_ID <> @id