Is it possible to use IN clause in plain sql Slick for integers?

安稳与你 提交于 2019-12-30 06:32:20

问题


There is a similar question here but it doesn't actually answer the question.

Is it possible to use IN clause in plain sql Slick?

Note that this is actually part of a larger and more complex query, so I do need to use plain sql instead of slick's lifted embedding. Something like the following will be good:

val ids = List(2,4,9)
sql"SELECT * FROM coffee WHERE id IN ($ids)"

回答1:


The sql prefix unlocks a StringContext where you can set SQL parameters. There is no SQL parameter for a list, so you can easily end up opening yourself up to SQL injection here if you're not careful. There are some good (and some dangerous) suggestions about dealing with this problem with SQLServer on this question. You have a few options:

Your best bet is probably to use the #$ operator together with mkString to interpolate dynamic SQL:

val sql = sql"""SELECT * FROM coffee WHERE id IN (#${ids.mkString(",")})"""

This doesn't properly use parameters and therefore might be open to SQL-injection and other problems.

Another option is to use regular string interpolation and mkString to build the statement:

val query = s"""SELECT * FROM coffee WHERE id IN (${ids.mkString(",")})"""
StaticQuery.queryNA[Coffee](query)

This is essentially the same approach as using #$, but might be more flexible in the general case.

If SQL-injection vulnerability is a major concern (e.g. if the elements of ids are user provided), you can build a query with a parameter for each element of ids. Then you'll need to provide a custom SetParameter instance so that slick can turn the List into parameters:

implicit val setStringListParameter = new SetParameter[List[String]]{
    def apply(v1: List[String], v2: PositionedParameters): Unit = {
        v1.foreach(v2.setString)
    }
}

val idsInClause = List.fill(ids.length)("?").mkString("(", ",", ")")
val query = s"""SELECT * FROM coffee WHERE id IN ($idsInClause)"""
Q.query[List[String], String](query).apply(ids).list(s)

Since your ids are Ints, this is probably less of a concern, but if you prefer this method, you would just need to change the setStringListParameter to use Int instead of String:




回答2:


  val ids = List(610113193610210035L, 220702198208189710L)

  implicit object SetListLong extends SetParameter[List[Long]] {
    def apply(vList: List[Long], pp: PositionedParameters) {
      vList.foreach(pp.setLong)
    }
  }

  val select = sql"""
        select idnum from idnum_0
        where idnum in ($ids#${",?" * (ids.size - 1)})
    """.as[Long]

@Ben Reich is right. this is another sample code, test on slick 3.1.0.

($ids#${",?" * (ids.size - 1)})




回答3:


Although this is not universal answer and may not be what the author wanted, I still want to point this out to whoever views this question.

Some DB backends support array types, and there are extensions to Slick that allow setting these array types in the interpolations.

For example, Postgres has the syntax where column = any(array), and with slick-pg you can use this syntax like so:

def query(ids: Seq[Long]) = db.run(sql"select * from table where ids = any($ids)".as[Long])

This brings a much cleaner syntax, which is friendlier to the statement compiler cache and also safe from SQL injections and overall danger of creating a malformed SQL with the #$var interpolation syntax.



来源:https://stackoverflow.com/questions/31156613/is-it-possible-to-use-in-clause-in-plain-sql-slick-for-integers

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!