Почему Slick создает подзапрос, когда вызывается метод take()

Я использую Slick 1.0.0-RC1. У меня есть это определение для объекта таблицы:

object ProductTable extends Table[(Int, String, String, String, ******, 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[******]("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`

Итак, мой вопрос: почему Slick генерирует подзапрос, когда его объект запроса сконструирован с помощью метода take()?

P.S. Если это может быть связано, я использую драйвер MySql со всеми этими

1 ответ

Там короткий ответ и длинный. Короткий: Подзапрос есть, потому что до сих пор никто не удосужился его удалить.

Более длинный ответ связан с тем фактом, что замена "map" и "take" не имеет никакого значения, что связано с тем, что компиляция запросов почти проста и прямолинейна.

Относительно рано в компиляторе запроса есть фаза "forceOuterBinds", которая вводит (потенциально) много дополнительных операций Bind (a.k.a. flatMap), которые семантически эквивалентны и избыточны. Идея состоит в том, чтобы взять некоторый х, который имеет тип коллекции и превратить его в Bind (s, x, Pure (s)), где s - новый символ. Если x уже имеет форму Pure (y), мы превращаем его в Bind (s, Pure (ProductNode()), Pure (y)). В коде Scala подумайте об этом, превратив x: List [T] в x.flatMap(s = > List (s)) или List (y) в List (()). FlatMap (s = > List (у)). Цель этого преобразования состоит в том, чтобы упростить реорганизацию дерева на более поздних этапах компилятора, предоставив нам место для изменения проекции (которая была создана как отображение идентичности) во всех местах, где мы могли бы это сделать.

Позже в "convertToComprehensions" все узлы из монадической формы (Bind, Pure, Filter, Take, Drop и т.д.) преобразуются индивидуально в узлы понимания (представляющие оператор SQL select). Результат по-прежнему не является законным SQL, хотя: понимание SQL - это не понимание монады. Они имеют очень ограниченные правила области, которые не позволяют предложению from ссылаться на переменную, введенную предыдущим предложением (в том же понимании или охватывающем понимании).

Вот почему нам нужен следующий этап - "fuseComprehensions", который может выглядеть просто как оптимизация с первого взгляда, но на самом деле требуется генерировать правильный код. Эта фаза пытается как можно больше спланировать индивидуальные понимания, чтобы избежать этих незаконных ссылок. Мы достигли некоторого прогресса в том, что мы можем слить, но 100% -ное решение проблемы области не видно (и на самом деле я уверен, что это невозможно решить).

Повторяю, что прогресс на этом этапе был в основном обусловлен необходимостью правильности, а не просто хорошим кодом. Можем ли мы удалить этот дополнительный подзапрос? Да, конечно, но никто еще не реализовал его.

Если вы хотите попытаться реализовать такую ​​оптимизацию, вот некоторые оговорки, о которых нужно подумать:

  • Вы довольствуетесь удалением сугубо псевдонизирующих прогнозов (т.е. пониманием, в котором слот выбора имеет форму Some (ProductNode (ch)), где каждый элемент ch является Path)?
  • Или, может быть, вы думаете, что выбрать x + 1 из (... limit...)) также следует слить. Какие выражения вы можете разрешить? Например. будет ли RowNum в порядке?
  • Какую форму должен иметь подзапрос? Например, может ли он содержать предложения groupBy или orderBy?

(И что-то для меня, чтобы подумать: сколько времени потребуется для реализации этой оптимизации по сравнению с объяснением, почему она еще не существует?)

licensed under cc by-sa 3.0 with attribution.