sheh
sheh

Reputation: 1023

Creating table view using slick

How can I create queries for postgresql view using slick 3?

I didn't find an answer in the slick documentation.

The question relates to my another question. I got right answer but I don't know how to implement it using slick.

Upvotes: 3

Views: 1682

Answers (2)

Epicurist
Epicurist

Reputation: 923

What about appending the query text after the view preamble:

val yourAwesomeQryComposition : TableQuery = ...

val qryText = yourAwesomeQryComposition.map(reg => (reg.id, ....)).result.statements.head

val createViewSchema = sqlu"""CREATE VIEW #$viewName AS #${qryText}"""

Upvotes: 2

Max Plevako
Max Plevako

Reputation: 1852

There is only rudimentary support for views in Slick 3, that doesn't guarantee full compile-time safety and compositionality, the latter especially matters considering most views strongly depend on data in other tables. You can describe a view as a Table and separate schema manipulation statements, which you must use instead of standard table schema extension methods like create and drop. Here is an example for your registries-n-rows case subject to the REGISTRY and ROWS table are already present in the database:

case class RegRn(id: Int, name: String, count: Long)

trait View{
  val viewName = "REG_RN"
  val registryTableName = "REGISTRY"
  val rowsTableName = "ROWS"

  val profile: JdbcProfile
  import profile.api._

  class RegRns(tag: Tag) extends Table[RegRn](tag, viewName) {

    def id    = column[Int]   ("REGISTRY_ID")
    def name  = column[String]("NAME", O.SqlType("VARCHAR"))
    def count = column[Long]  ("CT", O.SqlType("VARCHAR"))

    override def * = (id, name, count) <> (RegRn.tupled, RegRn.unapply)
    ...
  }

  val regRns = TableQuery[RegRns]
  val createViewSchema = sqlu"""CREATE VIEW #$viewName AS
                                   SELECT R.*, COALESCE(N.ct, 0) AS CT
                                   FROM   #$registryTableName R
                                   LEFT   JOIN (
                                     SELECT REGISTRY_ID, count(*) AS CT
                                     FROM   #$rowsTableName
                                     GROUP  BY REGISTRY_ID
                                   ) N ON R.REGISTRY_ID=N.REGISTRY_ID"""

  val dropViewSchema = sqlu"DROP VIEW #$viewName"
  ...
}

You can now create a view with db.run(createViewSchema), drop it with db.run(dropViewSchema) and of course call MTable.getTables("REG_RN") to expectedly find its tableType is "VIEW". Queries are the same as for other tables, e.g. db run regRns.result.head. You can even insert values into a view as you do for a normal Slick table if the rules allow (not your case due to COALESCE and the subquery). As I mentioned everything will become a mess when you want to compose existing Tables to create a view. You will have to always keep their names and definitions in sync, as it is not possible now to write anything that would at least guarantee the shape of the view conforms to combined shape of the underlying tables for example. Well, there is no way apart from ugly ones like this:

trait View{
  val profile: JdbcProfile
  import profile.api._

  val registryTableName = "REGISTRY"
  val registryId   = "REGISTRY_ID"
  val regitsryName = "NAME"

  class Registries(tag: Tag) extends Table[Registry](tag, registryTableName) {

    def id   = column[Int]   (registryId)
    def name = column[String](regitsryName, O.SqlType("VARCHAR"))

    override def * = (id, name) <> (Registry.tupled, Registry.unapply)
    ...
  }

  val rowsTableName = "ROWS"
  val rowsId  = "ROW_ID"
  val rowsRow = "ROW"

  class Rows(tag: Tag) extends Table[Row](tag, rowsTableName) {

    def id  = column[String](rowsId, O.SqlType("VARCHAR"))
    def rid = column[Int]   (registryId)
    def r   = column[String]("rowsRow", O.SqlType("VARCHAR"))

    override def * = (id, rid, r) <> (Row.tupled, Row.unapply)
    ...
  }

  val viewName = "REG_RN"

  class RegRns(tag: Tag) extends Table[RegRn](tag, viewName) {

    def id    = column[Int]   ("REGISTRY_ID")
    def name  = column[String]("NAME", O.SqlType("VARCHAR"))
    def count = column[Long]  ("CT", O.SqlType("VARCHAR"))

    override def * = (id, name, count) <> (RegRn.tupled, RegRn.unapply)
    ...
  }

  val registries = TableQuery[Registries]
  val rows = TableQuery[Rows]
  val regRns = TableQuery[RegRns]
  val createViewSchema = sqlu"""CREATE VIEW #$viewName AS
                                       SELECT R.*, COALESCE(N.ct, 0) AS CT
                                       FROM   #$registryTableName R
                                       LEFT   JOIN (
                                         SELECT #$registryId, count(*) AS CT
                                         FROM   #$rowsTableName
                                         GROUP  BY #$registryId
                                       ) N ON R.#$registryId=N.#$registryId"""

  val dropViewSchema = sqlu"DROP VIEW #$viewName"
  ...
}

Upvotes: 4

Related Questions