首页 文章

光滑的架构leftJoin 生产环境 笛卡尔积

提问于
浏览
1

我有这两个表:

select * from "DEPARTMENTS";
 ID | NAME  | MANAGER_ID 
----+-------+------------
  1 | FOO   |          1
  3 | XXX   |          2
  4 | dept1 |           
(3 rows)

select * from "EMPLOYEES";
 NAME | LAST |      EMAIL       | PHONE | SKYPE | DEPT_ID | ID 
------+------+------------------+-------+-------+---------+----
 AAA  | xxxx | abcdef@gmail.com |       |       |         |  1
 BBB  | yyyy |                  |       |       |         |  2
(2 rows)

I want to get all departments with manager's name (if manager exists) total of 3 rows .

但是当我这样做时,在我的架构中:

import scala.slick.driver.PostgresDriver.simple._
import play.api.Play.current
case class DepartmentManager(id:Int,name:String,manager:String="")
case class Department(id:Option[Int],name:String,managerId:Option[Int])
class Departments (tag: Tag) extends Table[Department](tag, "DEPARTMENTS") {
  val employees = TableQuery[Employees]
  def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
  def name = column[String]("NAME", O.NotNull)
  def managerId = column[Int]("MANAGER_ID", O.Nullable)
  def manager = foreignKey("EMP_FK",managerId,employees)(_.id)
  def * = (id.?,name,managerId.?) <> (Department.tupled, Department.unapply)
}

object Departments{
  val db = play.api.db.slick.DB
  val departments = TableQuery[Departments]
//this is the problematic query
  def allWithMngr = db.withSession { implicit session =>
    val employees = TableQuery[Employees]
    val dm = (departments leftJoin employees ).list
    dm.map(x =>DepartmentManager(x._1.id.getOrElse(0),x._1.name, x._2.name + " " + x._2.last))
  }
}

我得到笛卡尔积(dm.size = 6)

试图像这样查询:

val dm = (departments leftJoin employees on((a,b)=> a.id === b.id)).list

结果出错:

[SlickException:为ResultSet列路径s2._4读取NULL值(null)]

这是我的员工模型:

case class Employee(name: String,last: String,email:Option[String]=None,phone:Option[String]=None,skype:Option[String]=None,department: Option[Int] = None, id: Option[Int] = None)
class Employees (tag: Tag) extends Table[Employee](tag, "EMPLOYEES") {
  def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
  def name = column[String]("NAME", O.NotNull)
  def last = column[String]("LAST", O.NotNull)
  def email = column[String]("EMAIL", O.Nullable)
  def phone = column[String]("PHONE", O.Nullable)
  def skype = column[String]("SKYPE", O.Nullable)
  def deptId = column[Int]("DEPT_ID", O.Nullable)
  def dept = foreignKey("EMP_FK",deptId,departments)(_.id)
  def * = (name,last,email.?,phone.?,skype.?,deptId.?, id.?) <> (Employee.tupled, Employee.unapply)
val departments = TableQuery[Departments]

}

作为输出我想要相当于这个本机Sql:

val query =sql""" select x."ID", x."NAME", y."NAME" from "DEPARTMENTS" x  LEFT OUTER JOIN "EMPLOYEES" y ON (x."MANAGER_ID" = y."ID");""".as[DepartmentManager].list

这将导致:

List (DepartmentManager(1,"FOO","AAA"),DepartmentManager(3,"XXX","BBB"),DepartmentManager(4,"FOO",""))

1 回答

  • 1

    你在这里遇到的是Slick 2处理连接中的空值的尴尬方式 . Slick 3中的情况更好,但我们可以在Slick 2中使用联接 .

    你有的查询......

    (departments leftJoin employees ).list
    

    ...将是一个交叉连接,因为你没有 join .

    使用 join 时遇到的错误是因为您要选择的列之一为NULL . 使用Slick 2,您必须明确选择这些列并使用 .? 将它们提升为一个选项:

    val query = 
       departments.leftJoin(employees).on(_.managerId === _.id )
                  .map { case (d, e) => (d.id, d.name, e.name.?) }
    

    请注意 e.name.? ---因为该部门可能没有 e (员工) .

    此查询与测试数据的结果是:

    (1, FOO,   Some(AAA))
    (2, XXX,   Some(BBB))
    (3, dept1, None)
    

    ......我认为这就是你所追求的 .

相关问题