如何在 Slick 中使用 `ConstColumn` 作为可选值
How to use `ConstColumn` for optional values in the Slick
有一些table:
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
table中有如下数据:
| 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 值(例如 numbers
、strings
),但不适用于表示为 Option[String]=None
的可空值。
在这种情况下如何使用 ConstColumn
作为 None
值?
在这种情况下,我预期的错误是代码中两个 //Error
点处 Option[String]
和 None.type
之间的某种 type-mismatch。
你可以做的是在 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
模式。
我找到了这个任务的另一个解决方案。也许它对某人有用。
我们可以使用 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) 这个方法有点短
有一些table:
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
table中有如下数据:
| 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 值(例如 numbers
、strings
),但不适用于表示为 Option[String]=None
的可空值。
在这种情况下如何使用 ConstColumn
作为 None
值?
在这种情况下,我预期的错误是代码中两个 //Error
点处 Option[String]
和 None.type
之间的某种 type-mismatch。
你可以做的是在 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
模式。
我找到了这个任务的另一个解决方案。也许它对某人有用。
我们可以使用 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) 这个方法有点短