2013-02-17 49 views
9

我想生产这种SQL用油滑1.0.0:如何写嵌套查询在SELECT子句

select 
    cat.categoryId, 
    cat.title, 
    (
     select 
     count(product.productId) 
     from 
     products product 
     right join products_categories productCategory on productCategory.productId = product.productId 
     right join categories c on c.categoryId = productCategory.categoryId 
     where 
     c.leftValue >= cat.leftValue and 
     c.rightValue <= cat.rightValue 
    ) as productCount 
from 
    categories cat 
where 
    cat.parentCategoryId = 2; 

我最成功的尝试是(我放弃了“加入”的一部分,所以它更具有可读性):

def subQuery(c: CategoriesTable.type) = (for { 
     p <- ProductsTable 

     } yield(p.id.count)) 
     for { 
     c <- CategoriesTable 
     if (c.parentId === 2) 
     } yield(c.id, c.title, (subQuery(c).asColumn)) 

产生的子查询缺少括号中的SQL:

select 
    x2.categoryId, 
    x2.title, 
    select count(x3.productId) from products x3 
    from 
    categories x2 
    where x2.parentCategoryId = 2 

这显然是无效的SQL 任何想法如何让SLICK把这些括号放在正确的地方?或者也许有不同的方式来实现这一目标?

+1

你可以发布你一起尝试? – 2013-02-17 10:33:47

+0

我发布了迄今为止我得到的结果 – wassertim 2013-02-17 13:28:13

+0

这看起来像查询编译器中的一个bug /监督。也许你应该提交一份错误报告。 – 2013-02-17 22:37:07

回答

11

我从来没有使用Slick或ScalaQuery,所以它是一个相当冒险找出如何实现这一点。 Slick是非常可扩展的,但扩展文档有点棘手。它可能已经存在,但这是我想出来的。如果我做了不正确的事情,请纠正我。

首先我们需要创建一个自定义驱动程序。我扩展了H2Driver以便能够轻松测试。

trait CustomDriver extends H2Driver { 

    // make sure we create our query builder 
    override def createQueryBuilder(input: QueryBuilderInput): QueryBuilder = 
    new QueryBuilder(input) 

    // extend the H2 query builder 
    class QueryBuilder(input: QueryBuilderInput) extends super.QueryBuilder(input) { 

    // we override the expr method in order to support the 'As' function 
    override def expr(n: Node, skipParens: Boolean = false) = n match { 

     // if we match our function we simply build the appropriate query 
     case CustomDriver.As(column, LiteralNode(name: String)) => 
     b"(" 
     super.expr(column, skipParens) 
     b") as ${name}" 

     // we don't know how to handle this, so let super hanle it 
     case _ => super.expr(n, skipParens) 
    } 
    } 
} 

object CustomDriver extends CustomDriver { 
    // simply define 'As' as a function symbol 
    val As = new FunctionSymbol("As") 

    // we override SimpleSql to add an extra implicit 
    trait SimpleQL extends super.SimpleQL { 

    // This is the part that makes it easy to use on queries. It's an enrichment class. 
    implicit class RichQuery[T: TypeMapper](q: Query[Column[T], T]) { 

     // here we redirect our as call to the As method we defined in our custom driver 
     def as(name: String) = 
     CustomDriver.As.column[T](Node(q.unpackable.value), name) 
    } 
    } 

    // we need to override simple to use our version 
    override val simple: SimpleQL = new SimpleQL {} 
} 

为了使用它,我们需要导入具体的事情:

import CustomDriver.simple._ 
import Database.threadLocalSession 

然后,要使用它,你可以做以下的(我使用的表从官方油滑的文件在我的例子) 。

// first create a function to create a count query 
def countCoffees(supID: Column[Int]) = 
    for { 
    c <- Coffees 
    if (c.supID === supID) 
    } yield (c.length) 

// create the query to combine name and count 
val coffeesPerSupplier = 
    for { 
    s <- Suppliers 
    } yield (s.name, countCoffees(s.id) as "test") 

// print out the name and count 
coffeesPerSupplier foreach { case (name, count) => 
    println(s"$name has $count type(s) of coffee") 
} 

结果是这样的:

Acme, Inc. has 2 type(s) of coffee 
Superior Coffee has 2 type(s) of coffee 
The High Ground has 1 type(s) of coffee 
+0

它现在很好用。非常感谢你! – wassertim 2013-02-21 09:40:16