简体   繁体   中英

Scala Slick: How to do an except join (left outer join with is null)?

I'm using scala 2.11, slick 2.1.0 and postgres 9.6. Major version upgrade is out of the question.

I have 2 identical tables (created from the same model) and I want to do an except join (left outer join with is null): 除了加入

My model looks like this:

trait Coffee {
  val coffeeLoversId:         Option[Int]
  val taxId:                  Option[Long]
  val internationalCoffeeId:  String
  val providerId:             String
}

class Coffees(tag: Tag, schema: String) extends Table[Coffee](tag, Some(schema), "coffees")

  val coffeeLoversId: Column[Option[Int]] = column[Option[Int]]("coffee_lovers_id")
  val taxId: Column[Option[Long]] = column[Option[Long]]("tax_id")
  val internationalCoffeeId: Column[String] = column[String]("international_coffee_id", O.DBType("VARCHAR"))
  val providerId: Column[String] = column[String]("provider_id", O.DBType("VARCHAR"))

  def * = (coffeeLoversId, taxId, internationalCoffeeId, providerId) <> (Coffee.tupled, Coffee.unapply)

  def ? = (coffeeLoversId, taxId, internationalCoffeeId.?, providerId.?).shaped.<>({r=>import r._; _1.map(_=> Coffee.tupled((_1, _2, _3.get, _4.get)))}, (_:Any) =>  throw new Exception("Inserting into ? projection not supported."))
}


object Coffees {
  def tableQuery(implicit schema: TableSchema) = TableQuery[Coffees]{ tag : Tag => new Coffees(tag, schema.name) }
}

The SQL query I would like to run is:

SELECT * from previous.coffees PRV 
LEFT JOIN current.coffees CUR 
ON PRV.international_coffee_id = CUR.international_coffee_id 
WHERE PRV.international_coffee_id IS NULL;

I was able to get as far as:

for {
  (c,s) <- Coffees.tableQuery(previousSchema).leftJoin(
    Coffees.tableQuery(currentSchema)
    ) on((x,y) => x.internationalCoffeeId === y.internationalCoffeeId)
} yield(c)

that seems to give me a left outer join, but I'm having trouble filtering for nulls server side (adding the WHERE PRV.international_coffee_id IS NULL )

I'll be grateful for any pointers or ideas.

I was able to achieve my goal. First I tried using scala's conditional for-comprehension:

for {
  (c,s) <- Coffees.tableQuery(previousSchema).leftJoin(
    Coffees.tableQuery(currentSchema)
    ) on((x,y) => x.internationalCoffeeId === y.internationalCoffeeId)
if(s.internationalCoffeeId.isEmpty)
} yield(c)

This worked, and testing on the DB I got the expected result, by I knew I was filtering the result in scala, rather than on the DB server.

So I kept trying and finally got to

for {
  (c,s) <- Coffees.tableQuery(previousSchema).leftJoin(
    Coffees.tableQuery(currentSchema)
    ) on(
    (x,y) => x.internationalCoffeeId === y.internationalCoffeeId
    ).filter(_._2.internationalCoffeeId.isEmpty)
} yield(c)

I'm not clear yet on why the compiler couldn't pick up the type correctly the type of the input parameter for the anonymous function in my filter, but I dealt with it by operating directly on the tuple. I verified that I'm getting the desired SQL query using selectStatement directly on my Query (or in this case my CompiledStreamingExecutable ):

val query = Coffees.tableQuery(previousSchema).leftJoin(
    Coffees.tableQuery(currentSchema)
) on(
    (x,y) => x.internationalCoffeeId === y.internationalCoffeeId
).filter(_._2.internationalCoffeeId.isEmpty)
query.selectStatement

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM