色んなSQLをSlickで書くと?
上記の記事のSlick版です。
groupByに関するSlick 1.0.0のバグを回避するため、1.0.1-RC1を使用します。
例によってテーブル名とかは弄りました。 テーブル定義を含む全てのコードは以下のGistを参照してください。
LEFT JOIN + IS NULL
SQL
SELECT * FROM Students s LEFT JOIN Clubs c on (s.club_id = c.id) WHERE c.id IS NULL
Slick
val q1 = for { (s, c) <- Students leftJoin Clubs.map(_.??) on (_.clubId === _._1) if c._1.isNull } yield (s, c) val r1: List[(Student, (Option[Long], Option[String]))] = q1.list()
いきなり苦労した。
??
はid.? ~ name.?
のaliasとしてClubsに定義してあります。
普通にStudents leftJoin Clubs on ...
とするとNULLだよボケみたいな実行時エラーになるので注意が必要です。
難点は元エントリの趣旨に反してSlickが生成するSQLが以下のように意図したものとは違うことでしょうか。
select x2.x3, x2.x4, x2.x5, x2.x6, x7.x8, x7.x9 from ( select x10."NAME" as x3, x10."CLASSROOM" as x4, x10."CLUB_ID" as x5, x10."ID" as x6 from "STUDENTS" x10 ) x2 left outer join ( select x11."ID" as x8, x11."NAME" as x9 from "CLUBS" x11 ) x7 on x2.x5 = x7.x8 where x7.x8 is null
SlickのTabe定義が実際のDBテーブルのカラムを全て含まなくてもいいからこうなる? どうしても必要ならLifted EmbeddingじゃなくてPlain SQLを書かなければいけません。(前述のGist参照)
これ以外の問題では意図した通りのSQLが生成されました。(*
は使われないけど)
相関サブクエリー
SQL
SELECT * FROM Students s WHERE EXISTS ( SELECT c.id FROM Clubs c WHERE s.club_id = c.id )
Slick
val q2 = for { s <- Students if (for { c <- Clubs if s.clubId === c.id } yield c.id).exists } yield s val r2: List[Student] = q2.list()
割と素直な見た目。
複数のカラムでgroup by
SQL
SELECT s.club_id, s.classroom, count(*) FROM Sutudents s GROUP BY s.club_id, u.classroom
Slick
val q3 = for { ((ci, cr), ss) <- Students.groupBy(s => (s.clubId, s.classroom)) } yield (ci, cr, ss.length) val r3: List[(Option[Long], String, Int)] = q3.list()
Slick 1.0.0は複数カラムのgroup byがバグってて実行時エラーになるので1.0.1-RC1を使ってください。
SQLのcountはlengthメソッドで取得することになってます。countメソッドもありますがdeprecatedです。