2013-02-17 7 views
9

Я пытаюсь произвести это SQL с SLICK 1.0.0:Как писать вложенные запросы в предложении выбора

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

Вы можете разместить рядом с вашими попытками? –

+0

Я отправил то, что у меня до сих пор – wassertim

+0

Это похоже на ошибку/недосмотр в компиляторе запросов ко мне. Может быть, вы должны подать отчет об ошибке. – 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 

Затем, чтобы использовать его, вы можете сделать следующее (я использовал таблицы с официальной Slick документации в моем примере) ,

// 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

Смежные вопросы