2012-12-28 32 views
27

मैं MySQL क्वेरी करने के लिए Slick का उपयोग करना सीखने का प्रयास कर रहा हूं।स्कैला slick क्वेरी जहां सूची

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

मैं जानता हूँ कि को क्या कहना चाहेंगे कि कैसे मैं स्थान का एक संग्रह के लिए एक सूची [यात्रा] प्राप्त करने के लिए क्वेरी करने के लिए ऊपर बदल सकते हैं: मैं क्वेरी के निम्नलिखित प्रकार एक भी जाएँ वस्तु प्राप्त करने के लिए काम कर रहा है। .. इस तरह कुछ:

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) 

क्या यह स्लिम के साथ संभव है?

+0

क्या यह काम नहीं करता है? कार्य करना चाहिए। –

+0

क्या आप मूल्यों के टुपल्स तक नहीं रह सकते हैं? यह गारंटी देगा कि क्वेरी में आपके द्वारा पारित पैरामीटर की संख्या तय की गई है। –

उत्तर

28

जैसा कि अन्य उत्तर से पता चलता है, यह स्थिर प्रश्नों के साथ बोझिल है। स्थिर क्वेरी इंटरफ़ेस के लिए आपको Product के रूप में बाइंड पैरामीटर का वर्णन करने की आवश्यकता होती है। (Int, Int, String*) मान्य स्कैला नहीं है, और (Int,Int,List[String]) का उपयोग करके कुछ क्लूज की भी आवश्यकता है। इसके अलावा, यह सुनिश्चित करना है कि locationCodes.size हमेशा (?, ?...) की संख्या के बराबर है जो आपकी क्वेरी में भंगुर है।

प्रैक्टिस में, यह एक समस्या का बहुत अधिक नहीं है क्योंकि आप इसके बजाय क्वेरी मोनैड का उपयोग करना चाहते हैं, जो कि स्लिम का उपयोग करने के लिए प्रकार-सुरक्षित और अनुशंसित तरीका है।

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

क्या एसएच का उपयोग करना भी संभव है। पैरामीटर्स [सूची [Int]] की तरह और फिर "def byIdAndLocations" को QueryTemplate "val byIdAndLocations" के साथ प्रतिस्थापित करें? – longliveenduro

+0

क्या आप 'वैल आईडी = सूची (1,2,3) का उपयोग नहीं कर सकते हैं वैल परिणाम: डीबीआईओ [सेक [टी]] = query.filter (_। आईडी इनसेट आईडी) 'प्रति http://stackoverflow.com/questions/17408444/is-it-possible-to-use-in-clause-in -plain-एसक्यूएल-चालाक # comment52439626_17422901? –

5

क्योंकि StaticQuery object (Q) परोक्ष (प्रकार scala.slick.jdbc.SetParameter[T] का) सेटर वस्तु का एक प्रकार बनाने के लिए query विधि के प्रकार मानकों का प्रयोग करके, क्वेरी स्ट्रिंग में पैरामीटर सेट करने की उम्मीद है यह काम नहीं करता।
SetParameter[T] की भूमिका T के मान पर एक क्वेरी पैरामीटर सेट करना है, जहां आवश्यक प्रकार query[...] प्रकार पैरामीटर से लिया गया है।

मैं क्या देखते हैं ऐसी कोई वस्तु एक सामान्य A के लिए T = List[A] के लिए परिभाषित किया है, और यह एक समझदार विकल्प लगता है, जब से तुम नहीं वास्तव में IN (?, ?, ?,...) खंड

के लिए मानकों का एक गतिशील सूची के साथ एक एसक्यूएल क्वेरी लिख सकते हैं से


दायरे में इस के साथ निम्नलिखित कोड

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) 
""" 

# चर संदर्भ के सामने प्रकार सत्यापन अक्षम कर देता है और आप सूची क्वेरी पैरामीटर की अंतर्निहित रूपांतरण के लिए एक समारोह की आपूर्ति के बिना इस को हल करने के लिए अनुमति देता है।