Доктрина пропускает наборы данных при использовании соединений и смещений - PullRequest
3 голосов
/ 27 мая 2011

Я работаю с Doctrine 1.2 в Symfony 1.4 на PHP5.3.

У меня есть база данных, в которой события имеют несколько времени начала (1: n) и типов (n: m).

Для программы мне нужен список времени начала и заголовка события, которые относятся к специальному типу и не устарели:

$dql = Doctrine_Query::create()
  ->select('ed.start_datetime,e.title')/*edd.*,*/
  ->from('EventDate ed')
  ->leftJoin('ed.Event e')
  ->leftJoin('e.EventTypes et')
  ->leftJoin('e.Domain d')
  ->orderBy('ed.start_datetime, ed.event_id');
  ->where('e.start_datetime >= ? AND e3.id = ?',array('2011-05-27 17:19:41',2))
  ->fetchArray();

Схему можно просмотреть по адресу pastebin.com / SdjvsaxW .

Теперь доктрина делает два утверждения

SELECT DISTINCT e5.event_id FROM event_date e5 LEFT JOIN event e6 ON e5.event_id = e6.id LEFT JOIN event_event_type e8 ON (e6.id = e8.event_id) LEFT JOIN event_type e7 ON e7.id = e8.event_type_id WHERE (e5.start_datetime >= '2011-05-27 17:19:41' AND e7.id = '2') ORDER BY e5.start_datetime, e5.event_id

SELECT e.event_id AS e__event_id, e.start_datetime AS e__start_datetime, e2.id AS e2__id, e2.title AS e2__title FROM event_date e LEFT JOIN event e2 ON e.event_id = e2.id LEFT JOIN event_event_type e4 ON (e2.id = e4.event_id) LEFT JOIN event_type e3 ON e3.id = e4.event_type_id WHERE e.event_id IN (*all the IDs from the first query*) AND (e.start_datetime >= '2011-05-27 17:19:41' AND e3.id = '2') ORDER BY e.start_datetime, e.event_id

Проблема начинается с ввода пагинации: при добавлении смещения оно будет добавлено в первый запрос. Теперь, поскольку для события может быть более одного времени начала, количество идентификаторов из первого запроса SELECT DISTINCT может быть меньше числа дат. Следовательно, даты могут быть пропущены. В моем случае даже менее 50 различных событий, поэтому на вторых страницах, отображающих дату события 51-100, действительно пусто, несмотря на то, что существует несколько сотен дат события (из-за повторяющихся событий).

Я думаю, что проблема могла бы быть решена, если оставить в стороне различное, но я не могу это контролировать.

Кто-нибудь есть идеи?

1 Ответ

3 голосов
/ 02 июня 2011

Doctrine запускает два запроса, первый отдельный, в режиме, называемом «подзапрос лимита», когда у вас есть базовая таблица и вы объединяетесь через отношение один ко многим. Это потому, что вы собираетесь выбрать продукт объединения, и в итоге будет больше строк, чем есть записей в базовой таблице. Например, база данных вернет событие № 5 в две строки, если для события № 5 существует два типа события. Эти два запроса относятся только к MySQL, другие базы данных могут выполнять ограничение в подзапросе, а Doctrine генерирует один запрос.

Поскольку ваши События имеют несколько дат, а EventDate имеет составной первичный ключ, Doctrine ДОЛЖНА выбрать отдельный набор (event_id, start_datetime) в первом запросе, но, похоже, игнорирует вторую часть вашего составного первичного ключа, вероятно, потому что он не может вставить их в предложение ON во втором запросе.

Вероятно, вы можете обойти это одним из двух, а может быть и трех способов: 1) добавить уникальный первичный ключ в EventDate (столбец автоинкремента). Вы, вероятно, не должны ссылаться на это где-либо еще, но доктрина начнет использовать это 2) сделать 2 запроса вручную. Выберите все различные event_id, start_datetime из EventDate, добавьте свой лимит и смещение, а затем программно создайте предложение where:

 ... WHERE ((event_id = ? AND start_datetime = ?) OR (event_id = ? AND start_datetime = ?))... 

3) Измените порядок запроса, чтобы начать с другой таблицы или добавьте DISTINCT в выборку. Попробуйте выбрать разные (ed.event_id, ed.start_datetime). Иногда эти вещи могут избежать запуска алгоритма limit-subquery. 4) Выключите эту систему, см. Ссылку ниже, никогда не пробовал этого, и я не уверен, какие побочные эффекты существуют в гидратации и кеше объектов доктрины.

Лично я бы пошел с # 1. Я обнаружил, что всегда полезно иметь дополнительный столбец первичного ключа с Doctrine, даже если вы можете сформировать первичный ключ в нескольких столбцах.

Если вы хотите увидеть, как доктрина использует для этого код, см. Lib / Doctrine / Record.php, функция getLimitSubquery (). Вы заметите комментарий "// как насчет составных ключей?" в коде дважды. Вы также можете прочитать http://www.doctrine -project.org / Documentation / manual / 1_2 / ru / dql-doctrine-query-language: limit-and-offset-Clauses: алгоритм-limit-subquery-алгоритм

...