【问题标题】:Slick plain sql query with pagination带有分页的光滑纯 sql 查询
【发布时间】:2020-05-14 23:59:55
【问题描述】:

我有这样的东西,使用 Akka、Alpakka + Slick

 Slick
 .source(
     sql"""select #${onlyTheseColumns.mkString(",")} from #${dbSource.table}"""
    .as[Map[String, String]]
    .withStatementParameters(rsType = ResultSetType.ForwardOnly, rsConcurrency = ResultSetConcurrency.ReadOnly, fetchSize = batchSize)
    .transactionally
).map( doSomething )...

我想通过跳过第一个 N 元素来更新这个普通的 sql 查询。 但这是非常特定于数据库的。

是否可以获取 Slick 生成的分页位? [就像类型安全的查询一样,只需进行删除、过滤、获取?]

ps:我没有 Schema,所以我不能采用类型安全的方式,只想将所有表作为 Map、过滤器、删除等。 ps2:在 akka 级别,flow.drop 可以工作,但它不是最佳/缓慢的,因为它仍然会消耗行。 干杯

【问题讨论】:

    标签: sql scala slick alpakka


    【解决方案1】:

    由于您使用的是普通 SQL,因此您必须在代码 sn-p 中提供一个可行的 SQL。纯 SQL 可能不是类型安全的,但很灵活。

    顺便说一句,最好的方法是通过Database跳过第N个元素,例如mysql中的limit。

    【讨论】:

      【解决方案2】:

      根据你的数据库引擎,你可以使用类似的东西

      val page = 1
      val pageSize = 10
      val query = sql"""
        select #${onlyTheseColumns.mkString(",")} 
        from #${dbSource.table} 
        limit #${pageSize + 1}
        offset #${pageSize * (page - 1)}
      """
      

      pageSize+1 部分告诉您下一页是否存在

      【讨论】:

        【解决方案3】:

        我想通过跳过第一个 N 元素来更新这个普通的 sql 查询。但这是非常特定于数据库的。

        由于您担心更改不同数据库的 SQL,我建议您抽象出这部分 SQL,并根据所使用的 Slick 配置文件决定要做什么。

        如果您正在使用多个数据库产品,您可能已经从任何特定配置文件中抽象出来,可能使用了JdbcProfile。在这种情况下,您可以将“跳过 N 个元素”助手放在一个类中,并使用活动的 slickProfile 来决定要使用的 SQL。 (作为替代方案,您当然可以通过其他方式进行检查,例如您设置的环境值)。

        实际上可能是这样的:

        case class Paginate(profile: slick.jdbc.JdbcProfile) {
          // Return the correct LIMIT/OFFSET SQL for the current Slick profile
          def page(size: Int, firstRow: Int): String =
            if (profile.isInstanceOf[slick.jdbc.H2Profile]) {
              s"LIMIT $size OFFSET $firstRow"
            } else if (profile.isInstanceOf[slick.jdbc.MySQLProfile]) {
              s"LIMIT $firstRow, $size"
            } else {
              // And so on... or a default
              // Danger: I've no idea if the above SQL is correct - it's just placeholder
              ???
            }
        }
        

        你可以用作:

        // Import your profile
        import slick.jdbc.H2Profile.api._
        
        val paginate = Paginate(slickProfile)
        
        val action: DBIO[Seq[Int]] = 
          sql""" SELECT cols FROM table #${paginate.page(100, 10)}""".as[Int]
        

        通过这种方式,您可以在一个地方隔离(和控制)特定于 RDBMS 的 SQL。

        为了使助手更有用,并且由于 slickProfile 是隐式的,您可以改为:

        def page(size: Int, firstRow: Int)(implicit profile: slick.jdbc.JdbcProfile) = 
            // Logic for deciding on SQL goes here
        

        我觉得有必要评论一下,如果用户提供了任何值,那么在普通 SQL 中使用拼接 (#$) 会使您面临 SQL 注入攻击。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-02-24
          • 1970-01-01
          • 2019-05-17
          • 2013-07-28
          • 2014-02-27
          • 2021-07-09
          • 2014-08-15
          • 2018-03-08
          相关资源
          最近更新 更多