Почему доктрина использует WHERE IN вместо LIMIT?

Почему доктрина (1.2) использует WHERE IN вместо LIMIT?

Этот код:

Doctrine_Query::create()
   ->from('Table t')
   ->limit(10)
   ->getSqlQuery();

Возвращает что-то вроде этого:

SELECT t.id_table AS t__id_table FROM table AS t WHERE t__id_table IN (1,2,3,4,10,12,18,20,21,25);

Вместо этого:

SELECT t.id_table AS t__id_table FROM table AS t LIMIT 10;

Это поведение одинаково для любого значения LIMIT. Это генерирует очень длинные запросы для высоких значений LIMIT.

Бонусный вопрос: как Doctrine узнает, какие идентификаторы использовать? (Отправив другой запрос в БД??)


person Petr Peller    schedule 10.01.2011    source источник
comment
Какую базу данных вы используете?   -  person Matt Gibson    schedule 10.01.2011
comment
Странный; Я только что сделал что-то подобное с Doctrine Symfony 1.4, которая, как я думаю, является версией 1.2.3, и она использует предложение LIMIT для MySQL, как и следовало ожидать.   -  person Matt Gibson    schedule 10.01.2011


Ответы (2)


Это потому, что LIMIT работает со строками базы данных, а не с «объектами». Когда вы вводите $q->limit(10), вы хотите получить десять объектов, а не десять строк из базы данных.

Рассмотрим следующий запрос (продукты и категории имеют отношения «многие ко многим»):

SELECT p.*, c.* FROM product p 
INNER JOIN product_category_ref pcr ON p.id = pcr.prodcut_id
INNER JOIN category c ON c.id = pcr.category_id
WHERE p.price < 123;

Чтобы получить 10 продуктов (объектов), ваш запрос должен получить не менее 20 строк. Вы не можете использовать LIMIT 10, потому что (например) будет возвращено только 3 продукта. Вот почему вам нужно выяснить, какие продукты должны быть получены (ограничение применяется к продуктам), а затем получить фактические данные.

Это приведет к следующим запросам:

SELECT p.id FROM product p WHERE p.price < 123;
SELECT ..... WHERE p.id IN (...);

Второй запрос может вернуть 20, 423 или 31 строку. Как видите, это не значение из limit().

PS. Doctrine2 в этом случае намного понятнее, так как использует метод setMaxResults() вместо limit(), что менее запутанно.

person Crozin    schedule 10.01.2011
comment
откуда у вас эта информация? согласно ручному limit() является частью DQL и используется перед гидратацией - person ; 10.01.2011
comment
Я знаю это по собственному опыту и изучению источников. И да, вы правы - LIMIT является частью DQL (метод limit() происходит от QueryBuilder) и, как вы можете видеть, он используется до гидратации (во втором запросе (тот, который на самом деле гидратирован) нет даже предложения LIMIT)) . - person Crozin; 10.01.2011
comment
Это звучит странно для меня. Я (и мои коллеги) думал, что LIMIT применяется к количеству строк, возвращаемых запросом (после обработки всех соединений, подзапросов и т. д.). - person Petr Peller; 12.01.2011
comment
SQL LIMIT ограничивает количество возвращаемых строк. DQL LIMIT ограничивает количество возвращаемых объектов гидратированных (и для выборки 10 объектов может потребоваться 10, 20, 30, 312 строк, если какой-либо запрос содержит соединения). - person Crozin; 12.01.2011
comment
Кажется, есть много путаницы в том, как это работает, и вы в чем-то правы, но позвольте мне объяснить, как это работает в Doctrine :) Во-первых, это никак не связано с гидратацией и DQL LIMIT НЕ переводится в SQL LIMIT. Как было сказано ранее, SQL LIMIT просто ограничивает количество возвращаемых строк. Таким образом, чтобы дополнительно бороться с impedance mismatch, запрос DISTINCT сначала выполняется с SQL LIMIT, который удваивает DQL LIMIT; затем идентификаторы используются в основном запросе в IN() для виртуального ограничения количества строк, которые необходимо извлечь. - person Piet Bijl; 05.06.2013

Используя доктрину 1.2.3:

<?php

include(dirname(__FILE__).'/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));

$dbh = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'testdb', 'testdb');
$conn = Doctrine_Manager::connection($dbh);

class Table extends Doctrine_Record {
  public function setTableDefinition() {
    $this->hasColumn('id_table', integer, 10, array('primary' => true));
  }
}

$q = Doctrine_Query::create()
   ->from('Table t')
   ->limit(10)
   ->getSqlQuery();

echo $q;

Я получаю результат:

SELECT t.id_table AS t__id_table FROM table t LIMIT 10

Может быть, в вашем коде происходит что-то еще?

person Matt Gibson    schedule 10.01.2011
comment
Мы только что узнали, что WHERE IN используется только при использовании JOIN. Извините за путаницу. - person Petr Peller; 11.01.2011