首页 文章

使用Slick进行递归树状表查询

提问于
浏览
5

我的表数据形成一个树结构,其中一行可以引用同一个表中的父行 .

我试图使用Slick实现的是编写一个返回行和所有子行的查询 . 此外,我想做同样的事情,但写一个将返回一个孩子及其所有祖先的查询 .

换一种说法:

findDown(1) 应该返回

List(Group(1, 0, "1"), Group(3, 1, "3 (Child of 1)"))

findUp(5) 应该回来

List(Group(5, 2, "5 (Child of 2)"), Group(2, 0, "2"))

这是一个功能齐全的工作表(除了缺少的解决方案;-) .

package com.exp.worksheets

import scala.slick.driver.H2Driver.simple._

object ParentChildTreeLookup {

  implicit val session = Database.forURL("jdbc:h2:mem:test1;", driver = "org.h2.Driver").createSession()

  session.withTransaction {
    Groups.ddl.create
  }

  Groups.insertAll(
    Group(1, 0, "1"),
    Group(2, 0, "2"),
    Group(3, 1, "3 (Child of 1)"),
    Group(4, 3, "4 (Child of 3)"),
    Group(5, 2, "5 (Child of 2)"),
    Group(6, 2, "6 (Child of 2)"))

  case class Group(
    id: Long = -1,
    id_parent: Long = -1,
    label: String = "")

  object Groups extends Table[Group]("GROUPS") {
    def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
    def id_parent = column[Long]("ID_PARENT")
    def label = column[String]("LABEL")
    def * = id ~ id_parent ~ label <> (Group, Group.unapply _)
    def autoInc = id_parent ~ label returning id into {
      case ((_, _), id) => id
    }

    def findDown(groupId: Long)(implicit session: Session) = { ??? }

    def findUp(groupId: Long)(implicit session: Session) = { ??? }
  }

}

一个非常糟糕的静态尝试 findDown 可能是这样的:

private def groupsById = for {
  group_id <- Parameters[Long]
  g <- Groups; if g.id === group_id
} yield g

private def childrenByParentId = for {
  parent_id <- Parameters[Long]
  g <- Groups; if g.id_parent === parent_id
} yield g


def findDown(groupId: Long)(implicit session: Session) = { groupsById(groupId).list union childrenByParentId(groupId).list }

但是,我正在寻找一种方法让Slick使用id和id_parent链接以递归方式搜索同一个表 . 任何其他解决问题的好方法都非常受欢迎 . 但请记住,最好尽量减少数据库往返次数 .

2 回答

  • -1

    您可以尝试从光滑调用SQL . 对 up 层次结构的SQL调用看起来像这样(这是针对SQL Server的):

    WITH org_name AS 
    (
        SELECT DISTINCT
            parent.id AS parent_id,
            parentname.label as parent_label,
            child.id AS child_id,
            childname.ConceptName as child_label
        FROM
            Group parent RIGHT OUTER JOIN 
            Group child ON child.parent_id = parent.id
    ), 
    jn AS 
    (   
        SELECT
            parent_id,
            parent_label,
            child_id,
            child_label
        FROM
            org_name 
        WHERE
            parent_id = 5
        UNION ALL 
            SELECT
                C.parent_id,
                C.parent_label,
                C.child_id,
                C.child_label 
            FROM
                jn AS p JOIN 
                org_name AS C ON C.child_id = p.parent_id
    ) 
    SELECT DISTINCT
        jn.parent_id,
        jn.parent_label,
        jn.child_id,
        jn.child_label
    FROM
        jn 
    ORDER BY
        1;
    

    如果您想要进入层次结构,请更改以下行:

    org_name AS C ON C.child_id = p.parent_id
    

    org_name AS C ON C.parent_id = p.child_id
    
  • 1

    在简单的SQL中,这将是棘手的 . 你有多种选择:

    • 使用存储过程收集正确的记录(递归) . 然后使用代码将这些记录转换为树

    • 选择所有记录并使用代码将它们转换为树

    • 使用更高级的技术here(来自Optimized SQL for tree structures)和here . 然后使用代码将这些记录转换为树

    根据您希望在SQL中执行此操作的方式,您需要构建一个Slick查询 . Leaky Abstractions的概念在这里非常明显 .

    因此,获取树结构需要两个步骤:

    • 获取正确(或所有记录)

    • 使用这些记录构建(使用常规代码)树

    由于您使用的是Slick I,所以选择另一种数据库类型,但另一种数据库类型可能更适合您的数据模型 . 查看NoSQL了解不同类型之间的差异 .

相关问题