2016-01-15 4 views
1

У меня есть эти две таблицы:пятно схема LeftJoin производить декартово произведение

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 | [email protected] |  |  |   | 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","")) 
+0

Можете ли вы привести пример вывода того, что вы хотите в форме таблицы? – Jus12

+0

Off-Topic: Если проект db еще не установлен, я бы предложил использовать таблицы отношений вместо нулевых внешних ключей. Это избавляет вас от некоторых случаев кросс и упрощает запрос. – Roman

+0

@ Jus12 Я сделал сейчас, спасибо за ваш комментарий – igx

ответ

1

Что вы работаете в вот неудобный способ Slick 2 имеет дело с nulls в объединениях. Вещи лучше 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) 

... который я думаю, что вы после этого.

+0

Спасибо, это именно то, что я искал. – igx

 Смежные вопросы

  • Нет связанных вопросов^_^