使用 Slick Plain Sql 查询删除所有表

Drop all tables using Slick Plain Sql queries

我正在使用 Slick Plain Sql queries 删除 MariaDb 数据库中的所有 table。

这是我使用的代码:

import dbConfig.profile.api._
val databaseName : String

import slick.jdbc.SetParameter
implicit val SetString = SetParameter[Vector[String]](
  (s, pp) => pp.setString(s(pp.pos))
)

def dropTables = {
  val tablesToDrop : DBIO[Vector[String]] = 
    sql"SELECT concat(table_name) FROM information_schema.tables WHERE table_schema = '#$databaseName';".as[String]   

  val dbio : DBIO[Vector[String]] = for { 
    table <- tablesToDrop
    _ <- sqlu"DROP TABLE IF EXISTS '$table';"
  } yield table

  val future = dbConfig.db.run(dbio)
  val r = Await.result(future.andThen { case _ => dbConfig.db.close },
  Duration.Inf)
}

它成功获取了要删除的 table 列表,但随后出现错误Exception in thread "main" java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0). 我怀疑这是由于 Setparameter 代码造成的。

有什么想法吗?


这是一个使用地图的版本(我猜它们应该是平面地图?):

val tablesToDrop : DBIO[Vector[String]] = sql"SELECT concat(table_name) FROM information_schema.tables WHERE table_schema = '#$databaseName';".as[String]
def dropTable(tableName: String) : DBIO[Vector[String]] = sql"DROP TABLE IF EXISTS '$tableName';".as[String]
val dt = tablesToDrop.map(dbio => dbio.map(dropTable))

这运行没有错误,但不会删除 tables。这是日志:

DEBUG slick.basic.BasicBackend.action - #1: StreamingResultAction [SELECT concat(table_name) FROM information_schema.tables WHERE table_schema = 'altairdb';]
DEBUG slick.jdbc.JdbcBackend.statement - Preparing statement: SELECT concat(table_name) FROM information_schema.tables WHERE table_schema = 'altairdb';
DEBUG slick.jdbc.JdbcBackend.benchmark - Execution of prepared statement took 7ms
DEBUG slick.jdbc.StatementInvoker.result - /--------------------\
DEBUG slick.jdbc.StatementInvoker.result - | 1                  |
DEBUG slick.jdbc.StatementInvoker.result - | concat(table_name) |
DEBUG slick.jdbc.StatementInvoker.result - |--------------------|
DEBUG slick.jdbc.StatementInvoker.result - | Org                |
DEBUG slick.jdbc.StatementInvoker.result - | OrgUser            |
DEBUG slick.jdbc.StatementInvoker.result - | PermissionList     |
DEBUG slick.jdbc.StatementInvoker.result - | PermissionType     |
DEBUG slick.jdbc.StatementInvoker.result - | User               |
DEBUG slick.jdbc.StatementInvoker.result - \--------------------/
DEBUG slick.jdbc.StatementInvoker.result - 1 more rows read (6 total)
DEBUG slick.basic.BasicBackend.action - #2: success Vector(slick.jdbc.SQLActionBuilder$$anon@3e4a6e4b, slick.jdbc.SQLActionBuilder$$anon@267dd7e5, slick.jdbc.SQLActionBuilder$$anon@6a736f6d, slick.jdbc.SQLActionBuilder$$anon@68834f9f, slick.jdbc.SQLActionBuilder$$anon@5d64cf2, slick.jdbc.SQLActionBuilder$$anon@4d5c7152)

更新 1

根据 James 的建议,我写了以下内容:

val tablesToDrop : DBIO[Vector[String]] = sql"SELECT concat(table_name) FROM information_schema.tables WHERE table_schema = '#$databaseName';".as[String]

def dropTable(tableNames: Vector[String]) : DBIO[Vector[String]] =
      sql"DROP TABLE IF EXISTS #${tableNames.mkString(", ")};".as[String]

val dropTablesDbio : DBIO[Vector[String]] = for {
  tables <- tablesToDrop
  _  <- sqlu"SET FOREIGN_KEY_CHECKS = 0;"
  _ <- dropTable(tables)
  _ <- sqlu"SET FOREIGN_KEY_CHECKS = 1;"
} yield tables

只要 table 列表不为空,它就可以工作。空列表大小写导致 sql 语法错误。有没有一种优雅的方法来检查空 table?

我在想如果我可以发送

DBIO.seq(sqlu"DROP TABLE IF EXISTS Table0;",..., sqlu"DROP TABLE IF EXISTS TableN;")

as sql 那么它将更优雅地覆盖空的 table 列表案例。

更新 2

此版本不适用 table:

def dropTables = {
    val tablesToDrop: DBIO[Vector[String]] = sql"SELECT table_name FROM information_schema.tables WHERE table_schema = '#$databaseName';".as[String]

    def dropTables(tableNames: Vector[String]): DBIO[Int] =
      sqlu"DROP TABLE IF EXISTS #${tableNames.mkString(", ")};"

    def dropTable(name: String): DBIO[Int] = sqlu"DROP TABLE IF EXISTS #$name;"

    val dropTablesDbio: DBIO[Vector[String]] = {
      tablesToDrop.flatMap(tables => {
        if (tables.isEmpty)
          DBIO.successful[Vector[String]](Vector.empty)
        else {
          for {
            _ <- sqlu"SET FOREIGN_KEY_CHECKS = 0;"
            _ <- dropTables(tables)
            _ <- sqlu"SET FOREIGN_KEY_CHECKS = 1;"
          } yield tables
        }
      })
    }
    val future = dbConfig.db.run(dropTablesDbio.withPinnedSession)

    val r = Await.result(future, Duration.Inf)
    r.foreach(println(_))
  }

Gist to this and other versions

DROP TABLE IF EXISTS 需要 table 的列表,如下所示:

DROP TABLE IF EXISTS A, B, C

我认为此查询不接受绑定变量(要在准备好的语句中设置的变量)。相反,您想要做的是使用 table 名称作为字符串文字,而不是绑定变量。

您不需要 SetParameter 隐式值。尝试像下面这样替换 DELETE 查询:

DROP TABLE IF EXISTS #${table.mkString(",")};

参考:http://slick.lightbend.com/doc/3.2.3/sql.html#splicing-literal-values

还值得注意的是,您应该在删除 table 之前禁用外键检查,然后在删除它们之后重新启用它。参考: