2013-05-16 7 views
11

मैं मॉड्यूलर तरीके से Database.Esqueleto क्वेरी कैसे लिख सकता हूं जैसे कि "आधार" क्वेरी और संबंधित परिणाम सेट को परिभाषित करने के बाद, मैं अतिरिक्त आंतरिक जोड़ों को जोड़कर परिणाम सेट को प्रतिबंधित कर सकता हूं और जहां अभिव्यक्तियां।डाटाबेस लिखना। प्रश्न पूछना, सशर्त जुड़ना और गिनती

इसके अलावा, मैं मूल क्वेरी को कैसे परिवर्तित कर सकता हूं जो एक क्वेरी में इकाइयों (या फ़ील्ड टुपल्स) की एक सूची देता है जो परिणाम सेट की गणना करता है क्योंकि बेस क्वेरी निष्पादित नहीं होती है, लेकिन इसका एक संशोधित संस्करण LIMIT के साथ होता है और ऑफसेट।

the Yesod Book से अपनाए गए निम्नलिखित गलत हास्केल कोड स्निपेट को उम्मीद है कि मैं क्या लक्ष्य कर रहा हूं।

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-} 
{-# LANGUAGE GADTs, FlexibleContexts #-} 
import qualified Database.Persist as P 
import qualified Database.Persist.Sqlite as PS 
import Database.Persist.TH 
import Control.Monad.IO.Class (liftIO) 
import Data.Conduit 
import Control.Monad.Logger 
import Database.Esqueleto 
import Control.Applicative 

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| 
Person 
    name String 
    age Int Maybe 
    deriving Show 
BlogPost 
    title String 
    authorId PersonId 
    deriving Show 
Comment 
    comment String 
    blogPostId BlogPostId 
|] 

main :: IO() 
main = runStdoutLoggingT $ runResourceT $ PS.withSqliteConn ":memory:" $ PS.runSqlConn $ do 
    runMigration migrateAll 

    johnId <- P.insert $ Person "John Doe" $ Just 35 
    janeId <- P.insert $ Person "Jane Doe" Nothing 

    jackId <- P.insert $ Person "Jack Black" $ Just 45 
    jillId <- P.insert $ Person "Jill Black" Nothing 

    blogPostId <- P.insert $ BlogPost "My fr1st p0st" johnId 
    P.insert $ BlogPost "One more for good measure" johnId 
    P.insert $ BlogPost "Jane's" janeId 

    P.insert $ Comment "great!" blogPostId 

    let baseQuery = select $ from $ \(p `InnerJoin` b) -> do  
     on (p ^. PersonId ==. b ^. BlogPostAuthorId) 
     where_ (p ^. PersonName `like` (val "J%")) 
     return (p,b) 

    -- Does not compile 
    let baseQueryLimited = (,) <$> baseQuery <*> (limit 2) 

    -- Does not compile 
    let countingQuery = (,) <$> baseQuery <*> (return countRows) 

    -- Results in invalid SQL 
    let commentsQuery = (,) <$> baseQuery 
       <*> (select $ from $ \(b `InnerJoin` c) -> do 
         on (b ^. BlogPostId ==. c ^. CommentBlogPostId) 
         return()) 

    somePosts <- baseQueryLimited 
    count <- countingQuery 
    withComments <- commentsQuery 
    liftIO $ print somePosts 
    liftIO $ print ((head count) :: Value Int) 
    liftIO $ print withComments 
    return() 

उत्तर

7

LIMIT और COUNT के लिए, हैमर का जवाब पूरी तरह से सही है इसलिए मैं उन्हें नहीं दूंगा। मैं बस दोहरा दूंगा कि एक बार जब आप select का उपयोग करेंगे तो आप किसी भी तरह से क्वेरी को फिर से बदलने में सक्षम नहीं होंगे।

JOIN रों लिए, वर्तमान में आप एक प्रश्न है कि एक अलग from (और न ही (FULL|LEFT|RIGHT) OUTER JOIN रों) में परिभाषित किया गया था के साथ एक INNER JOIN ऐसा करने में सक्षम नहीं हैं। हालांकि, आप निहित जोड़ सकते हैं। उदाहरण के लिए, आपके द्वारा निर्धारित करता है, तो:

baseQuery = 
    from $ \(p `InnerJoin` b) -> do 
    on (p ^. PersonId ==. b ^. BlogPostAuthorId) 
    where_ (p ^. PersonName `like` val "J%") 
    return (p, b) 

तो फिर तुम सिर्फ इतना कहना हो सकता है:

commentsQuery = 
    from $ \c -> do 
    (p, b) <- baseQuery 
    where_ (b ^. BlogPostId ==. c ^. CommentBlogPostId) 
    return (p, b, c) 

Esqueleto तो की तर्ज पर कुछ उत्पन्न करेगा:

SELECT ... 
FROM Comment, Person INNER JOIN BlogPost 
ON Person.id = BlogPost.authorId 
WHERE Person.name LIKE "J%" 
AND BlogPost.id = Comment.blogPostId 

सुंदर नहीं है, लेकिन हो जाता है INNER JOIN एस के लिए काम किया गया। यदि आपको OUTER JOIN करने की आवश्यकता है तो आपको अपने कोड को फिर से कॉन्फ़िगर करना होगा ताकि सभी OUTER JOIN एस उसी from में हों (ध्यान दें कि आप OUTER JOIN एस के बीच एक अंतर्निहित जुड़ाव कर सकते हैं)।

+1

अंतराल को भरने और एक निर्णायक उत्तर प्रदान करने के लिए धन्यवाद। – Tero

+0

मुझे यह भी ध्यान रखना चाहिए कि 'commentsQuery' में आप' ठीक 'का उपयोग करने से पहले 'baseQuery' का भी उपयोग कर सकते हैं। –

+0

इसके अलावा, कृपया किसी भी एस्क्यूलेट क्वेरी के बग के रूप में रिपोर्ट करें जिसके परिणामस्वरूप अमान्य एसक्यूएल है ताकि हम इसकी जड़ों की जांच कर सकें। जिसे आपने देखा है उसे '() ', ज्ञात-लेकिन-निश्चित-निश्चित बग के उपचार के साथ करना है।कामकाज के रूप में आप कुछ ऐसा कर सकते हैं जैसे 'रिटर्न (वैल ट्रू)'। –

8

दस्तावेज और select के प्रकार को देखते हुए:

select :: (...) => SqlQuery a -> SqlPersistT m [r] 

ऐसा नहीं है कि select बुला लिए जाने पर हम शुद्ध composable प्रश्नों (SqlQuery a) की दुनिया को छोड़ स्पष्ट है और साइड इफेक्ट की दुनिया में प्रवेश (SqlPersistT m [r])। इसलिए हमें select से पहले बस लिखना होगा।

let baseQuery = from $ \(p `InnerJoin` b) -> do 
     on (p ^. PersonId ==. b ^. BlogPostAuthorId) 
     where_ (p ^. PersonName `like` (val "J%")) 
     return (p,b) 

let baseQueryLimited = do r <- baseQuery; limit 2; return r 
let countingQuery = do baseQuery; return countRows 

somePosts <- select baseQueryLimited 
count  <- select countingQuery 

यह सीमित और गिनने के लिए काम करता है। मुझे अभी तक शामिल होने के लिए यह नहीं पता है, लेकिन ऐसा लगता है कि यह संभव होना चाहिए।

+0

आंशिक रूप से समस्या को हल करने के लिए धन्यवाद। मेरे उपयोग के मामले में मुझे भी शामिल होने की आवश्यकता है, इसलिए समय के लिए मैं कच्चे एसक्यूएल तारों को जोड़ना चाहता हूं। – Tero

+3

चीयर्स @hammar। किसी और के लिए जो संकलन के लिए countingQuery संस्करण प्राप्त करने में परेशानी में भाग लेता है: मुझे परिणाम प्रकार निर्दिष्ट करना था, जो (मेरे निरंतर कॉन्फ़िगरेशन के लिए) '[Value Int64]' है। अर्थात। यह एक सिंगलटन सूची का मूल्यांकन करता है जिसका सदस्य गिनती है। –

संबंधित मुद्दे