【问题标题】:How to use `ConstColumn` for optional values in the Slick如何在 Slick 中使用“ConstColumn”作为可选值
【发布时间】:2019-07-04 02:27:49
【问题描述】:

有一些表:

case class Thing(name: String, color: Option[String], height: Option[String])

class ThingSchema(t: Tag) extends Table[Thing](t, "things") {
  def name = column[String]("name")
  def color = column[Option[String]]("color")
  def height = column[Option[String]]("height")
  def * = (name, color, height) <> (Thing.tupled, Thing.unapply)
}
val things = TableQuery[ThingSchema]

例如things表中有如下数据:

|  name   |   color   | height |
+---------+-----------+--------+
|   n1    |  green    | <null> |
|   n1    |  green    | <null> |
|   n1    |  <null>   | normal |
|   n1    |  <null>   | normal |
|   n1    |  red      | <null> |
|   n2    |  red      | <null> |

我需要从以上数据中得到如下结果:

|  name   |   color   | height | size |
+---------+-----------+--------+------+
|   n1    |  green    | <null> |  2   |
|   n1    |  <null>   | normal |  2   |
|   n1    |  red      | <null> |  1   |
|   n2    |  red      | <null> |  1   |

为了解决这个任务,我使用了以下分组查询:

SELECT name, color, null, count(*) AS size
FROM things
GROUP BY name, color

UNION ALL

SELECT name, null, height, count(*) AS size
FROM things
GROUP BY name, height

我尝试使用 Slick 创建此查询:

val query1 = 
      things.groupBy(t => (t.name, t.color))
            .map { case ((name, color), g) => (name,color,None, g.size)} //Error#1

val query2 = 
      things.groupBy(t => (t.name, t.height)) 
            .map { case ((name, height), g) => (name,None,height,g.size)} //Error#1

val query = query1 ++ query2

但上面的代码没有被编译,因为 Slick 不能为 None 值定义 ConstColumn 的类型(参见上面代码中的 //Error#1 注释)。

这适用于 NOT-null 值(例如 numbersstrings),但不适用于表示为 Option[String]=None 的 Nullable 值。

在这种情况下,如何将ConstColumn 用于None 值?

Here is the link to the same question

【问题讨论】:

    标签: scala slick


    【解决方案1】:

    我已经为这项任务找到了另一种解决方案。也许它对某人有用。

    我们可以使用Rep.None[T]Rep.Some[T] 为可选类型生成ConstColumn 值。

    这个例子也有效:

    val query1 = 
         things.groupBy(t => (t.name, t.color))
               .map { case ((name, color), g) => 
                            (name,color, Rep.None[String], g.size)
                    }
    

    这种方法有两个优点:

    1) 我们可以分配给更一般的类型。例如:

    val field: Rep[String] = ...
    val x: (Rep[String], Rep[Option[String]]) = (field, Rep.None[String])
    
    // it is not compiled because a tuple has type (Rep[String], Option[String])
    val y: (Rep[String], Rep[Option[String]]) = (field, None: Option[String])
    

    2) 这种方法有点短

    【讨论】:

      【解决方案2】:

      在这种情况下,我预计的错误是代码中的两个 //Error 点处 Option[String]None.type 之间存在某种类型的不匹配。

      您可以做的是在None 上添加类型注释:

      val query1 = 
            things.groupBy(t => (t.name, t.color))
                  .map { case ((name, color), g) => (name,color, None: Option[String], g.size)}
      

      这将被编译成您正在使用的SELECT name, color, null, count 模式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-19
        • 2012-11-01
        • 2019-03-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多