从另一行的列值 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 如何使用主键 IDheight 的先前值递增 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") 

}

能简单的做到吗?

这有两部分:

  1. 如何使用依赖于其他行的值插入行?
  2. 您如何以逻辑 "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);