我对 Scala 比较陌生,对 Doobie 也比较陌生。我正在连接到 SQL Server 2014,需要创建一个临时表,然后插入到该临时表中。在 SQL Server 中,当您创建临时表并断开连接时,临时表会自动删除。
在以下代码片段中,我收到此异常:
Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: Invalid object name '#temp'
片段:
val create: doobie.ConnectionIO[Int] = sql"CREATE TABLE #temp (tmp CHAR(20))".update.run
val insert: doobie.ConnectionIO[Int] = sql"INSERT INTO #temp values ('abc'), ('def')".update.run
val query: doobie.ConnectionIO[List[String]] = sql"select * from #temp ".query[String].to[List]
def wrapper(): ConnectionIO[List[String]] = {
for {
c <- create
i <- insert
q <- query
} yield q
}
wrapper().transact(xa).debug.as(ExitCode.Success)
我相信这告诉我 Doobie 正在删除 create 和 insert 语句之间的连接?
预期/期望的行为是它将返回
List("abc","def")
。
预先感谢您的帮助!
更新:
这是我所知道的实际上有效的一个小例子:
val create = sql"CREATE TABLE #temp (tmp CHAR(20))"
val insert: doobie.ConnectionIO[Int] = sql"INSERT INTO #temp values ('abc'), ('def')"
(create ++ insert).update.run.transact(xa).debug.as(ExitCode.Success)
(请注意,它仅适用于创建和插入部分,不适用于查询部分)
用头撞笔记本电脑一周后……我终于想通了。 Doobie 在执行
.query
: 时实际上会执行“更新”命令
val create: Fragment = sql"CREATE TABLE #temp (tmp CHAR(20))"
val insert: Fragment = sql"INSERT INTO #temp values ('abc'), ('def')"
val query: Fragment = sql"select * from #temp "
(create ++ insert ++ query).query[String].to[List].transact(xa).debug.as(ExitCode.Success)
输出:
List(abc ,def )
我也因此而用头撞我的笔记本电脑。
Doobie 似乎对下面的会话做了一些奇怪的事情。临时表在每个会话中共享。理论上,他们应该有相同的会话,因此他们应该共享临时表,但不知怎的,他们在 Doobie 中却没有。
我有一个部分的、hacky 解决方案:
如果您使用global临时表(双
##
而不是单个#
),它会起作用。
但是,我不确定您是否可以在您的用例中使用全局临时表。它们是每个连接共享的。
您可以确保全局表的表名称是随机的,以避免出现任何问题:
val tableId = "e0c0544d"
Fragment.const(s"CREATE TABLE ##temp_${tableId} (tmp CHAR(20))").update.run.void
但是它并没有解决最初的问题,即 Doobie 对会话做了一些奇怪的事情,因此本地临时表无法正常工作。我也不确定您是否不需要显式删除此表以确保安全,因此您不妨创建一个普通表...
您的解决方案是将 3 个查询粘合到一个查询中 - 不幸的是,它并不适用于所有情况。更多时候,我们希望在一个
sql"..."
中创建一个临时表,从另一个 sql"..."
填充它,并在另一个 sql"..."
中执行某些操作,并使用单个 ConnectionIO
作为一个 .transact
一起执行/处理。
编辑:也许这是一个设计选择。您可以在同一查询中使用本地临时表,在一个查询中使用全局临时表
.transact
。