slick schema left Join 产生笛卡尔积
slick schema leftJoin produce Cartesian product
我有这两张表:
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)
我想得到所有有经理姓名的部门(如果经理存在)总共 3 行。
然而在我的架构中,当我这样做时:
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: Read NULL value (null) for ResultSet column Path
s2._4]
这是我的员工模型:
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",""))
您 运行 在这里看到的是 Slick 2 处理连接中空值的笨拙方式。 Slick 3 中的情况更好,但我们可以让连接在 Slick 2 中工作。
您的查询...
(departments leftJoin employees ).list
...将是交叉连接,因为您没有 join
.
您在使用 join
时 运行 遇到的错误是因为您想要 select 的列之一是 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)
...我认为这就是您所追求的。
我有这两张表:
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)
我想得到所有有经理姓名的部门(如果经理存在)总共 3 行。
然而在我的架构中,当我这样做时:
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: Read NULL value (null) for ResultSet column Path s2._4]
这是我的员工模型:
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",""))
您 运行 在这里看到的是 Slick 2 处理连接中空值的笨拙方式。 Slick 3 中的情况更好,但我们可以让连接在 Slick 2 中工作。
您的查询...
(departments leftJoin employees ).list
...将是交叉连接,因为您没有 join
.
您在使用 join
时 运行 遇到的错误是因为您想要 select 的列之一是 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)
...我认为这就是您所追求的。