2012-05-05 128 views
3

有沒有人有一個想法,如何在Scala Query中存檔多人到多人?
ScalaQuery多對多

我想將博客帖子連接到一系列標籤。
這是我的數據庫設計: enter image description here

我已經與我的自定義代碼成功,但檢查斯卡拉Querys生成的SQL當我不開心我的解決方案。
我玩過一種功能性的方法,它產生了許多SQL查詢,導致很多往返。
我可以弄清楚如何減少大約一半的查詢次數。

一個手工製作的查詢獲取我所有的數據很好地格式化在一個查詢,

select 
    p.id, p.content, p.posted_date, GROUP_CONCAT(t.name) 
from 
    post p, 
    tag t, 
    tag_post tp 
where 
    tp.post_id = p.id and tp.tag_id = t.id 
group by 
    p.id 

從斯卡拉查詢生成的查詢給予同樣的數據。

SELECT `t1`.`id`,`t1`.`content`,`t1`.`posted_date` FROM `post` `t1` 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=2) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=3) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=4) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=5) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=6) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=7) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=8) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=9) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=10) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=11) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=12) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=13) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=14) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=15) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=16) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=17) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=2) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=18) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=19) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=1) 
SELECT `t1`.`id`,`t1`.`name` FROM `tag` `t1` WHERE (`t1`.`id`=3) 
SELECT `t1`.`tag_id` FROM `tag_post` `t1` WHERE (`t1`.`post_id`=20) 

我擔心所有這些往返行程可能會帶來很多開銷。

有沒有人能成功製作一個很好的Scala Query多對多的實現?

回答

3

編輯
您可以模擬GROUP_CONCAT像這樣:

val groupConcat = SimpleFunction[String]("GROUP_CONCAT") 

如果您在查詢範圍這種方法,應該就這麼簡單:

yield (alias.a, alias.b, groupConcat(alias.c)) 

因爲我存儲這些輔助函數在抽象數據庫包裝器中執行,並在特定的DBMS(如MySQL)中實現,因此它會變得更加複雜,因爲SimpleFunction類型簽名需要此抽象方法defin銀行足球比賽:

val groupConcat: (Seq[Column[_]] => OperatorColumn[String]) 

這意味着實現需要一個在SEQ(alias.c),這是一個有點直覺相反,我們只是傳遞一個單列通過。無論如何,愉快地工作,GROUP_CONCAT是在MySQL

ORIGINAL相當方便
神知無不言,發佈您的代碼什麼是錯的,但試試這個:

val q = (for { 
    tp <- TagPost 
    p <- Post if tp.post_id is p.id 
    t <- Tag if tp.tag_id is t.id 
    _ <- Query groupBy p.id 
} yield (p.id, p.content, p.posted_date, group_concat(t.name))) 
println(q.selectStatement) 

你將需要創建一個函數來複制MySQL的GROUP_CONCAT。見SimpleFunction source;這個對象的一元方法允許你將一個命名列傳遞給底層的DBMS函數。

val group_concat = 
    SimpleFunction.unary[NamedColumn[String], String]("GROUP_CONCAT") 
+0

謝謝,這幾乎是我最初的詢問。是的,你只是發現了一個帖子,而不是連接的標籤,所有的Scala查詢查詢都是用這些標籤來標註這個結果。如果有一種方法可以直接將它作爲scala查詢中的列表,我不必使用組concat。如果我今天晚些時候可以擴展SQ的話,我會在一個Ipad上回答問題。 – Farmor

+0

阿哈我看到你已經更新了你的答案,現在看起來很有希望。我想我必須手動解析結果來創建我的對象。由於我不常規地使用顯式列,但在「對象」中定義的對象擴展了Table的伴隨案例類。 – Farmor

+0

@Farmor得到了group_concat的工作,請參閱編輯。以上應該複製所需的手邊SQL。 ScalaQuery繼續留下深刻的印象... – virtualeyes

1

我終於完成了該方法。
不幸的是它是供應商特定的。

def allPosts = database.withSession { implicit db: Session => 
    val group_concat_string_tmp = SimpleFunction[String]("GROUP_CONCAT") 
    def group_concat_string(c: Column[String]) = group_concat_string_tmp(Seq(c)) 
    def group_concat_long(c: Column[Long]) = group_concat_string_tmp(Seq(c)) 
    val query = for{ 
     tp <- TagPostTable 
     tag <- TagTable if tp.tag_id is tag.id 
     post <- PostTable if tp.post_id is post.id 
     _ <- Query groupBy post.id 
    } yield post.id ~ post.content ~ post.postedDate ~ group_concat_long(tag.id) ~ group_concat_string(tag.name) 
    println(query.selectStatement) 
    def parseToTagList(ids: String, names: String) : List[Tag] = { 
     (ids.split(',') map (_.toLong) , names.split(',')).zipped map (Tag(_,_)) toList 
    } 
    query.list map (queryResult => Post(queryResult._1, queryResult._2, queryResult._3, Option(parseToTagList(queryResult._4, queryResult._5)))) 
    } 

而生成的SQL查詢是奇異:) YES!

SELECT `t1`.`id`,`t1`.`content`,`t1`.`posted_date`,GROUP_CONCAT(`t2`.`id`),GROUP_CONCAT(`t2`.`name`) 
FROM `tag_post` `t3`,`post` `t1`,`tag` `t2` 
WHERE (`t3`.`tag_id`=`t2`.`id`) AND (`t3`.`post_id`=`t1`.`id`) 
GROUP BY `t1`.`id`