2013-02-19 90 views
1

可以說我有以下模式:CTE遞歸查詢和內部連接

person (person_id, person_account, name) 
relationships (person_id, father_id) 
purchases(person_account, total, type) 

免責聲明:我沒有設計這個架構,我知道這是可怕的,所以我道歉。這也是一個例子,這更復雜。還要注意,這是一個我將在JDBCTemplate中使用的查詢,這就是我包含該標記的原因。

現在我想得到一個人的購買count,他或她的兒子購買和他們的兒子購買等。我想要一個特定的起始點。

夫婦的事情,從迄今爲止我見過的例子不同:

  1. 一個特定的起點(讓我們說這是person_account)。
  2. purchases沒有person_id字段,所以它使事情變得複雜。
  3. 我想要一個count而不是其他的東西。
  4. 我想過濾typepurchase
  5. 由於father_id關係在另一個表中,這意味着必須進行連接。再次,它是可怕的,但我不能改變數據庫,以包括在personfather_id字段。
  6. 如果一個人沒有父親,relationships表中沒有條目。這會使它更加複雜。

現在我讀過的例子中,我知道他們夫婦:
How to get all children of a parent and then their children using recursion in query
SQL Server recursive query
http://msdn.microsoft.com/en-us/library/ms186243.aspx

不過,我有問題理解出發點和落腳點。我的出發點可能有一個父親,所以不能爲空。基本上,我的終點應該從relationships表中沒有條目的人那裏得到purchases

那麼我現在的想法是:

declare @id int = 234 

;with CTEexample as (
    select p.person_account, p.person_id, p.type, r.father_id from purchases as s 
     join person p on p.person_account = s.person_account 
     join relationships r on r.person_id = p.person_id 
     where r.person_id = @id 
    union all 
    select p.person_account, p.person_id, p.type, r_father_id from purchases as s 
     join person p on p.person_account = s.person_account 
     join relationships r on r.person_id = p.person_id 
     join CTEexample c on p.person_id = r.father_id 
) 
select count(*) from CTEexample 
     where type = 's' 

但是,它是不工作的。

任何幫助,將不勝感激。

+0

不能肯定,但嘗試扭轉你的加盟CTEexample。因此,在p.person_id = r.father_id上加入CTEexample c變爲在r.person_id = p.father_id上加入CTEexample c – 2013-02-19 23:15:20

回答

2

你有一個非常詳細的問題,但我的猜測是你在遞歸CTE謂詞造成破壞。很多時候,你需要做的2件重要的事情遞歸和追捕一棵樹:

  1. 你需要知道你是在「位置」(在我的例子POS)
  2. 您需要確定從關係是什麼水平您的遞歸中的位置使遞歸有意義。否則,您再次顯示相同的值,而沒有真正使用它可以執行的遞歸功能。

下面是可能適用於你的一個例子:

  1. 會給你一個層次
  2. 會發現一棵樹的最遠的梯級(最低位置)
  3. 添加訂單後代和小孩在一起。

遞歸的好處並不是一個或兩個級別,但是當你走5級或更多的級別,並有權力告訴表達式哪部分是你想要的。我通常在做遞歸時做2個cte,一個做遞歸,另一個找到最大遞歸,然後給我看。否則,每個人都會隨着每一層層面的回報而返回。除非你真的想要這樣,否則你應該用窗口化的表達來限制它。

我希望這可以幫助,有時你不得不調整遞歸的CTE爲你自己的情況了一會兒:

Declare @table table (PersonId int identity, PersonName varchar(512), Account int, ParentId int, Orders int); 

insert into @Table values ('Brett', 1, NULL, 1000),('John', 1, 1, 100),('James', 1, 1, 200),('Beth', 1, 2, 300),('John2', 2, 4, 400); 

select 
    PersonID 
, PersonName 
, Account 
, ParentID 
from @Table 

; with recursion as 
    (
    select 
     t1.PersonID 
    , t1.PersonName 
    , t1.Account 
    --, t1.ParentID 
    , cast(isnull(t2.PersonName, '') 
      + Case when t2.PersonName is not null then '\' + t1.PersonName else t1.PersonName end 
      as varchar(255)) as fullheirarchy 
    , 1 as pos 
    , cast(t1.orders + 
      isnull(t2.orders,0) -- if the parent has no orders than zero 
      as int) as Orders 
    from @Table t1 
     left join @Table t2 on t1.ParentId = t2.PersonId 
    union all 
    select 
     t.PersonID 
    , t.PersonName 
    , t.Account 
    --, t.ParentID 
    , cast(r.fullheirarchy + '\' + t.PersonName as varchar(255)) 
    , pos + 1 -- increases 
    , r.orders + t.orders 
    from @Table t 
     join recursion r on t.ParentId = r.PersonId 
    ) 
, b as 
    (
    select *, max(pos) over(partition by PersonID) as maxrec -- I find the maximum occurrence of position by person 
    from recursion 
    ) 
select * 
from b 
where pos = maxrec -- finds the furthest down tree 
-- and Account = 2 -- I could find just someone from a different department 
1

保持遞歸簡單(更容易讓其他人在路上管理)並使用它來獲取關係。從那裏,你可以加入Person來獲得賬號,然後購買。

DECLARE @PersonID INT = 1 

;WITH Family (PersonID, FatherID) AS (
    SELECT p.PersonID, null 
    FROM Person p 
    WHERE p.PersonID = @PersonID 

    UNION ALL 

    SELECT p.PersonID, r.FatherID 
    FROM Person p 
    INNER JOIN Relationships r 
    ON r.PersonID = p.PersonID -- Me 
    INNER JOIN Family f 
    ON f.PersonID = r.FatherID -- Father 
) 
SELECT * 
FROM Family f 
JOIN Person p 
    ON p.PersonID = f.PersonID 
JOIN Purchases ps 
    ON ps.PersonAccount = p.PersonAccount 
WHERE ps.Type is null 

SQLFiddle