2013-01-20 62 views
7

我使用Slick 1.0.0-RC1。我有這樣的定義表對象:爲什麼在調用take()方法時生成子查詢

object ProductTable extends Table[(Int, String, String, String, Double, java.sql.Date, Int, Option[Int], Int, Boolean)]("products") { 
    def id = column[Int]("productId", O.PrimaryKey, O.AutoInc) 
    def title = column[String]("title") 
    def description = column[String]("description") 
    def shortDescription = column[String]("shortDescription") 
    def price = column[Double]("price") 
    def addedDate = column[java.sql.Date]("addedDate") 
    def brandId = column[Int]("brandId") 
    def defaultImageId = column[Option[Int]]("defaultImageId") 
    def visitCounter = column[Int]("visitCounter") 
    def archived = column[Boolean]("archived") 
    def * = id ~ title ~ description ~ shortDescription ~ price ~ addedDate ~ brandId ~ defaultImageId ~ visitCounter ~ archived 
} 

我需要一個簡單的查詢,其選擇從數據庫8行:

ProductTable.filter(_.title === "something") 
    .sortBy(_.visitCounter) 
    .map(_.title) 
    .take(8) 
    .selectStatement 

,輸出是:

select x2.x3 from 
    (select x4.`title` as x3 from `products` x4 
    where x4.`title` = 'something' 
    order by x4.`visitCounter` limit 8) x2 

如果我擺脫take()方法:

ProductTable.filter(_.title === "something") 
.sortBy(_.visitCounter) 
.map(_.title) 
.selectStatement 

則輸出是:

select x2.`title` from `products` x2 
where x2.`title` = 'something' 
order by x2.`visitCounter` 

所以我的問題是:當其查詢對象與take()方法構造爲什麼油滑生成一個子查詢?

P.S.如果它可以相關,我使用MySql驅動程序與所有這些

+0

翻蓋周圍的地圖,並採取 – virtualeyes

+0

你有試過嗎?它不會改變輸出SQL – wassertim

+0

發佈到Slick用戶組,Zeiger會比任何人都更瞭解爲什麼會生成一個子選擇.... – virtualeyes

回答

11

有一個簡短的答案和一個長。簡短的一個是:子查詢在那裏,因爲到目前爲止沒有人打算刪除它。

較長的答案與交換「map」和「take」並沒有什麼區別,這是由於查詢的彙編非常簡單和直接。

在查詢編譯器相對較早的時候,有一個「forceOuterBinds」階段,它引入了(潛在地)大量額外的Bind(a.k.a. flatMap)操作,這些操作在語義上是等價的和冗餘的。這個想法是採取一些x它有一個集合類型,並將它變成綁定(s,x,Pure(s))其中s是一個新鮮的符號。如果x已經是形狀Pure(y),我們將它變成而不是綁定(s,Pure(ProductNode()),Pure(y))。在Scala代碼,認爲這是一個轉動×:列表[T]x.flatMap(S =>列表(一個或多個))列表(y)的列表(())flatMap (s => List(y))。此轉換的目的是通過給我們一個位置來修改投影(它是作爲身份映射創建的)在所有我們可能想要執行此操作的位置,從而使後面編譯器階段的樹重寫變得更加容易。

後來,在「convertToComprehensions」,從一元形式(綁定,純淨,過濾,冒,滴等)的所有節點都轉換單獨來理解節點(代表SQL 選擇語句)。但結果仍然不是合法的SQL:SQL理解不是monad理解。它們具有非常有限的範圍規則,其不允許來自子句的引用由子句(在相同理解或封閉理解中)的先前的引入的變量。

這就是爲什麼我們需要下一個階段,「fuseComprehensions」,它可能看起來像一個乍看起來優化,但實際上需要生成正確的代碼。這個階段試圖儘可能融合個人的理解,以避免這些非法引用。我們已經取得了一些進展,我們可以融合,但100%的解決方案並不在眼前(事實上,我確信這是不可能解決的)。

重申,這一階段的進展主要是由於需要正確性,而不僅僅是生成更好的代碼。那麼我們可以刪除額外的子查詢嗎?是的,當然,但沒有人實現它。

如果你想嘗試實施這樣的優化,這裏有幾點值得思考:

  • 你與刪除純屬混淆預測(即理解的內容,其中選擇槽的形式一些(ProductNode(ch))ch的每個元素都是一個Path)?
  • 或者,也許你認爲選擇x + 1從(...限制...))也應該融合。你可以允許什麼樣的表情?例如。 RowNum會好嗎?
  • 子查詢需要什麼樣的形狀?例如,它可能包含組通過orderBy子句?

(而且是我要思考:?多久會採取比較能夠解釋爲什麼它還不存在實現最優化)

+1

您的答案在Slick 3.0中仍然有效嗎?或者改變了我找不到的東西。 –

相關問題