具有特定起点和内部联接的cte递归查询

时间:2013-02-19 23:09:02

标签: sql-server sql-server-2008 tsql jdbctemplate

假设我有以下架构:

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. 我希望按type的{​​{1}}进行过滤。
  5. 由于purchase关系在另一个表中,因此意味着必须进行连接。再次它很可怕但我无法更改数据库以在father_id表中包含father_id字段。
  6. 如果某人没有父亲,则person表中没有条目。这将使情况进一步复杂化。
  7. 现在我已经阅读了这些例子,我理解了其中几个:
    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

    但是,我在理解起点和终点时遇到了问题。我的起点COULD有一个父亲,因此不能为空。我的端点应该从relationships表上没有条目的人那里获得purchases

    所以我在想的是:

    relationships

    然而,它根本不起作用。

    任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:2)

你有一个非常详细的问题,但我的猜测是你在递归CTE中的谓词造成了严重破坏。通常,您需要通过递归和搜索树来完成两件重要的事情:

  1. 你需要知道你在'位置'(我的例子中的位置)
  2. 的等级
  3. 您需要确定递归中位置的关系,以使递归有意义。否则,你再次显示相同的值,而不是真正使用它可以做的递归功能。
  4. 以下是可能适用于您的示例:

    1. 会给你一个层次结构
    2. 会找到树的最远梯级(最低位置)
    3. 添加后代和孩子的订单。
    4. 递归的好处不是一个或两个级别,而是当你达到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 :(得分: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