可选表和非可选表之间的联合

时间:2016-07-05 16:38:12

标签: sql scala join union slick

我有两个查询选择需要联合的记录,其中一个是左连接,另一个是常规(即内部)连接。

这是左连接案例:

def regularAccountRecords = for {
      (customer, account) <- customers joinLeft accounts on (_.accountId === _.accountId) // + some other special conditions
} yield (customer, account)

这是常规联合案例:

def specialAccountRecords = for {
  (customer, account) <- customers join accounts on (_.accountId === _.accountId) // + some other special conditions
} yield (customer, account)

现在我想把这两个记录集联合起来:

regularAccountRecords ++ specialAccountRecords

显然这不起作用,因为在常规连接情况下它返回Query[(Customer, Account),...],在左连接情况下它返回Query[(Customer, Rep[Option[Account]]),...],这会导致类型不匹配错误。

现在,如果这是常规列类型(例如Rep[String]),我可以通过?运算符(即record.?)将其转换为可选项,然后获取Rep[Option[String]]但在桌子上使用它(即帐户表)会导致:

Error:(62, 85) value ? is not a member of com.test.Account

如何解决此问题并正确进行联合?

1 个答案:

答案 0 :(得分:0)

Okay, looks like this is what the '?' projection is for but I didn't realize it because I disabled the optionEnabled option in the Codegen. Here's what your codegen extension is supposed to look like:

class MyCodegen extends SourceCodeGenerator(inputModel) {
    override def TableClass = new TableClassDef {
        override def optionEnabled = true
    }
}

Alternatively, you can use implicit classes to tack this thing onto the generated TableClass yourself. Here is how that would look:

implicit class AccountExtensions(account:Account) {
    def ? = (Rep.Some(account.id), account.name).shaped.<>({r=>r._1.map(_=> Account.tupled((r._2, r._1.get)))}, (_:Any) =>  throw new Exception("Inserting into ? projection not supported."))
}

NOTE: be sure to check the field ordering, depending on how this projection is done, the union query might put the ID field in the wrong place in the output, use println(query.result.statements.headOption) to debug the output SQL to be sure.

Once you do that, you will be able to use account.? in the yield statement:

def specialAccountRecords = for {
  (customer, account) <- customers join accounts on (_.accountId === _.accountId) 
} yield (customer, account.?)

...and then you will be able to unionize the tables correctly

regularAccountRecords ++ specialAccountRecords

I really wish the Slick people would put a note on how the '?' projection is useful in the documentation beyond the vague statement 'useful for outer joins'.