从另一行的列值 Slick 中导出列值
Derive column value from another row's column value Slick
我正在尝试从先前插入的列的值派生新插入的列的值。例如,如果我有这个数据库,其中每一行(第一行除外)都必须引用另一行的 ID
。我想将 Height
列从 Previous ID
引用
的行中 Height
的值增加一
----------
ID | Previous ID | Height
0 | null | 123
1 | 0 | 124
2 | 1 | 125
3 | 1 | 125
请注意 height
如何使用主键 ID
从 height
的先前值递增 1。有什么简单的方法可以用 Slick 做到这一点吗? table 可能看起来像这样
case class ExampleCaseClass(id: Option[Long], previousId: Long)
class ExampleTable(tag: Tag) extends Table[ExampleCaseClass](tag,"example") {
def id = column[Long]("id",O.PrimaryKey, O.AutoInc)
def previousId = column[Long]("previous_id")
//this is the column that needs to be derived based on the height of 'previousId'
def height = column[Long]("height")
}
能简单的做到吗?
这有两部分:
- 如何使用依赖于其他行的值插入行?
- 您如何以逻辑 "transparent" 对 table 的用户进行打包?
(1)有两种方法:
(1a) 在单独的 SQL 语句中执行查询和插入,两次往返数据库。 (几乎在所有情况下都表现出色。)
(1b) 使用 INSERT INTO ... SELECT
语句执行查询并在一个 SQL 语句中插入。 (在某些不常见的情况下速度稍快)。
除了性能,两者还需要不同的句法方法。 Slick 中的两种方法都在以下两个答案中进行了讨论:
其实我觉得 (1b) 无论如何更简洁。我提到 (1a) 是为了给你另一种探索的选择。 (1b) 使用 forceInsertQuery
完成,如下所述:
http://slick.lightbend.com/doc/3.1.1/queries.html#inserting
val example = TableQuery[ExampleTable]
def insertAuto(previousId: Int) = {
val query = example.filter(_.id == previousId).map(r => (previousId, r.height))
DBIO.seq(example.forceInsertQuery(query))
}
(请注意,使用 INSERT INTO .. SELECT
时,您可以将用户提供的文字值与查询值混合,只需将文字嵌入 SELECT 子句即可。)
可能存在一个问题,我们需要提供 id,即使它是自动递增的,因为文档似乎建议 forceInsertQuery
即使对于 autoinc 列也需要显式值。
现在,您想要如何打包该 def 以方便您的用户调用,我实际上并不确定。这应该是一件更容易完成的事情。您可以考虑将其作为 TableQuery[ExampleTable]
:
的扩展方法
implicit class RichExampleTable(example: TableQuery[ExampleTable]) {
// def extension methods here
}
但这确实需要您的用户调用 insertAuto
方法而不是使用 "direct" Slick 语句。我认为没有任何解决办法。
如果您不想公开 height
字段,您可以使用简单的 sql 查询来执行此操作:
def insertExample(previousId: Int): DBIO[Int] = {
sqlu"insert into example (previous_id, height) select $previousId, height + 1 from example where id = $previousId"
}
另一种实现方法是添加数据库触发器。通过这种方式,您可以使用普通插入,数据库将自动递增:
CREATE TRIGGER auto_height BEFORE INSERT ON example
FOR EACH ROW
SET NEW.height = 1 + (SELECT height FROM example WHERE id = NEW.previous_id);
我正在尝试从先前插入的列的值派生新插入的列的值。例如,如果我有这个数据库,其中每一行(第一行除外)都必须引用另一行的 ID
。我想将 Height
列从 Previous ID
引用
Height
的值增加一
----------
ID | Previous ID | Height
0 | null | 123
1 | 0 | 124
2 | 1 | 125
3 | 1 | 125
请注意 height
如何使用主键 ID
从 height
的先前值递增 1。有什么简单的方法可以用 Slick 做到这一点吗? table 可能看起来像这样
case class ExampleCaseClass(id: Option[Long], previousId: Long)
class ExampleTable(tag: Tag) extends Table[ExampleCaseClass](tag,"example") {
def id = column[Long]("id",O.PrimaryKey, O.AutoInc)
def previousId = column[Long]("previous_id")
//this is the column that needs to be derived based on the height of 'previousId'
def height = column[Long]("height")
}
能简单的做到吗?
这有两部分:
- 如何使用依赖于其他行的值插入行?
- 您如何以逻辑 "transparent" 对 table 的用户进行打包?
(1)有两种方法:
(1a) 在单独的 SQL 语句中执行查询和插入,两次往返数据库。 (几乎在所有情况下都表现出色。)
(1b) 使用
INSERT INTO ... SELECT
语句执行查询并在一个 SQL 语句中插入。 (在某些不常见的情况下速度稍快)。
除了性能,两者还需要不同的句法方法。 Slick 中的两种方法都在以下两个答案中进行了讨论:
其实我觉得 (1b) 无论如何更简洁。我提到 (1a) 是为了给你另一种探索的选择。 (1b) 使用 forceInsertQuery
完成,如下所述:
http://slick.lightbend.com/doc/3.1.1/queries.html#inserting
val example = TableQuery[ExampleTable]
def insertAuto(previousId: Int) = {
val query = example.filter(_.id == previousId).map(r => (previousId, r.height))
DBIO.seq(example.forceInsertQuery(query))
}
(请注意,使用 INSERT INTO .. SELECT
时,您可以将用户提供的文字值与查询值混合,只需将文字嵌入 SELECT 子句即可。)
可能存在一个问题,我们需要提供 id,即使它是自动递增的,因为文档似乎建议 forceInsertQuery
即使对于 autoinc 列也需要显式值。
现在,您想要如何打包该 def 以方便您的用户调用,我实际上并不确定。这应该是一件更容易完成的事情。您可以考虑将其作为 TableQuery[ExampleTable]
:
implicit class RichExampleTable(example: TableQuery[ExampleTable]) {
// def extension methods here
}
但这确实需要您的用户调用 insertAuto
方法而不是使用 "direct" Slick 语句。我认为没有任何解决办法。
如果您不想公开 height
字段,您可以使用简单的 sql 查询来执行此操作:
def insertExample(previousId: Int): DBIO[Int] = {
sqlu"insert into example (previous_id, height) select $previousId, height + 1 from example where id = $previousId"
}
另一种实现方法是添加数据库触发器。通过这种方式,您可以使用普通插入,数据库将自动递增:
CREATE TRIGGER auto_height BEFORE INSERT ON example
FOR EACH ROW
SET NEW.height = 1 + (SELECT height FROM example WHERE id = NEW.previous_id);