2012-12-28 17 views
27

私はMySQLに問い合わせるためにSlickを使用する方法を学んでいます。私が知りたいのですがどのようなScala slick query where in list

Q.query[(Int,Int), Visit](""" 
    select * from visit where vistor = ? and location_code = ? 
""").firstOption(visitorId,locationCode) 

は、私は場所のコレクションの一覧[ログイン]を取得するために照会するには、上記を変更する方法である:私は、クエリの次のタイプは、1回の訪問オブジェクトを取得するために取り組んでいます。 ..このようなもの:

val locationCodes = List("loc1","loc2","loc3"...) 
Q.query[(Int,Int,List[String]), Visit](""" 
    select * from visit where vistor = ? and location_code in (?,?,?...) 
""").list(visitorId,locationCodes) 

これはSlickでも可能ですか?

+0

これは機能しませんか?うまくいくはずです。 –

+0

値のタプルに固執できませんか?クエリに渡すパラメータの数が固定されていることが保証されます。 –

答えて

28

もう1つの答えが示唆しているように、これは静的クエリでは煩雑です。静的照会インターフェースでは、バインド・パラメーターをProductと記述する必要があります。 (Int, Int, String*) は有効なスカラーではなく、(Int,Int,List[String])を使用するといくつかのkludgesも必要です。さらに、locationCodes.sizeが常に(?, ?...)の数に等しいことを保証することは、クエリにあなたが脆いことです。

実際には、クエリモナードを代わりに使用したいので、これはあまり問題にはなりません。これは、Slickを使用するための型安全で推奨される方法です。あなたはいつもの方法でクエリをラップすることができ

case class Visitor(visitor: Int, ... location_code: String) 

object Visitors extends Table[Visitor]("visitor") { 
    def visitor = column[Int]("visitor") 
    def location_code = column[String]("location_code") 
    // .. etc 
    def * = visitor ~ .. ~ location_code <> (Visitor, Visitor.unapply _) 
} 

注:

val visitorId: Int = // whatever 
val locationCodes = List("loc1","loc2","loc3"...) 
// your query, with bind params. 
val q = for { 
    v <- Visits 
    if v.visitor is visitorId.bind 
    if v.location_code inSetBind locationCodes 
    } yield v 
// have a look at the generated query. 
println(q.selectStatement) 
// run the query 
q.list 

この

はあなたのように設定するテーブルを持っていると仮定しています。

def byIdAndLocations(visitorId: Int, locationCodes: List[String]) = 
    for { 
    v <- Visits 
    if v.visitor is visitorId.bind 
    if v.location_code inSetBind locationCodes 
    } yield v 
} 

byIdAndLocations(visitorId, List("loc1", "loc2", ..)) list 
+0

これは有望そうです。私は水曜日まで職場に戻っていませんが、それ以前にこれをテストする時間を見つけようとしています。 – ShatyUT

+0

s.thを使用することもできますか。 Parameters [List [Int]]のようにして、 "def byIdAndLocations"をQueryTemplate "val byIdAndLocations"に置き換えますか? – longliveenduro

+0

'val ids = List(1,2,3)|を使用できません。 val結果:DBIO [Seq [T]] = query.filter(_。id inSet ids) 'http://stackoverflow.com/questions/17408444/is-it-possible-to-use-in-clause-in -plain-sql-slick#comment52439626_17422901? –

5

StaticQuery objectQ)が暗黙的に(タイプscala.slick.jdbc.SetParameter[T]の)セッターオブジェクトの並べ替えを作成するために、queryメソッドの型パラメータを使用して、クエリ文字列にパラメータを設定することを期待ので、それは動作しません。
SetParameter[T]の役割は、クエリパラメータをタイプTの値に設定することです。ここで、必要なタイプはquery[...]タイプのパラメータから取得されます。私は、一般的なAためT = List[A]ために定義されたそのようなオブジェクトはありません見て、あなたが実際にIN (?, ?, ?,...)

のパラメータの動的なリストでSQLクエリを記述することはできませんので、それは、賢明な選択を思われるものから、


範囲でこれに次のコード

import scala.slick.jdbc.{SetParameter, StaticQuery => Q} 

def seqParam[A](implicit pconv: SetParameter[A]): SetParameter[Seq[A]] = SetParameter { 
    case (seq, pp) => 
     for (a <- seq) { 
      pconv.apply(a, pp) 
     } 
} 

implicit val listSP: SetParameter[List[String]] = seqParam[String] 

を通じて、このような暗黙の価値を提供することにより、実験では、あなたのコードを実行することができるはず私はなかった

val locationCodes = List("loc1","loc2","loc3"...) 
Q.query[(Int,Int,List[String]), Visit](""" 
    select * from visit where vistor = ? and location_code in (?,?,?...) 
""").list(visitorId,locationCodes) 

しかし、あなたは常に手動locationCodesサイズは、私はきれいな回避策は、マクロを使用して作成することができることを信じて最後にあなたのIN


?の数と同じであることを保証しなければなりません、シーケンスタイプを一般化する。しかし、シーケンスサイズの動的な性質に関する前述の問題を考えれば、フレームワークのための賢明な選択であるとは思えません。

3

あなたは自動にこのよう句で生成することができます。

def find(id: List[Long])(implicit options: QueryOptions) = { 
    val in = ("?," * id.size).dropRight(1) 
    Q.query[List[Long], FullCard](s""" 
     select 
      o.id, o.name 
     from 
      organization o 
     where 
      o.id in ($in) 
     limit 
      ? 
     offset 
      ? 
      """).list(id ::: options.limits) 
    } 

そして、あなたは複雑なクエリを持っており、上記のための理解がされていない場合pagoda_5b says

def seqParam[A](implicit pconv: SetParameter[A]): SetParameter[Seq[A]] = SetParameter { 
    case (seq, pp) => 
     for (a <- seq) { 
     pconv.apply(a, pp) 
     } 
    } 

    implicit def setLongList = seqParam[Long] 
2

として暗黙のsetParameterを使用を使用しますオプションでは、Slick 3では次のようなことができますが、SQLインジェクションを防ぐためにリストクエリパラメータのデータを自分で検証する必要があります。

val locationCodes = "'" + List("loc1","loc2","loc3").mkString("','") + "'" 
sql""" 
    select * from visit where visitor = $visitor 
    and location_code in (#$locationCodes) 
""" 

変数参照の前にある#は型の検証を無効にし、リストクエリパラメータの暗黙の変換に関数を指定せずにこれを解決できます。