2012-02-13 4 views
1

У меня есть стол Postgres с более чем 8 миллионами строк. Учитывая следующие два способа выполнения одного и того же запроса через DBD::Pg, я получаю совершенно разные результаты.тот же запрос, два разных способа, значительно отличная производительность

$q .= '%'; 

## query 1 
my $sql = qq{ 
    SELECT a, b, c 
    FROM t 
    WHERE Lower(a) LIKE '$q' 
}; 
my $sth1 = $dbh->prepare($sql); 
$sth1->execute(); 

## query 2 
my $sth2 = $dbh->prepare(qq{ 
    SELECT a, b, c 
    FROM t 
    WHERE Lower(a) LIKE ? 
}); 
$sth2->execute($q); 

запрос 2, по крайней мере, на порядок медленнее, чем запрос 1 ... кажется, что он не использует индексы, в то время как запрос 1 используется индекс.

Хотелось бы услышать, почему.

+0

Как вы измеряете скорость? – TLP

+0

изначально это было просто от наблюдения ... разница в том, что это очевидно.Затем я помещаю числа в наблюдение с помощью «Use Benchmark»; – punkish

+1

Вам следует попробовать переключить порядок тестов, чтобы исключить какие-либо эффекты кеширования. – TLP

ответ

5

С LIKE выражение, индексы б-дерево может быть использованы только тогда, когда схема поиска левого якоря, т.е. с концевым %. More details in the manual.
Благодаря @evil otto для связи. Эта ссылка на Текущая версия.

Ваш первый запрос предоставляет эту важную информацию на этапе подготовки, поэтому планировщик запросов может использовать соответствующий индекс.

Ваш второй запрос не предоставляет никакой информации о шаблоне во время подготовки, поэтому планировщик запросов не может использовать какие-либо индексы.

+0

Я согласен с вашим комментарием об использовании индекса с '%' в конце шаблона. Но я не думаю, что запрос запланирован на время подготовки заявления, поэтому я не согласен с последней частью вашего ответа. – zgpmax

+1

Erwin абсолютно корректен – punkish

+1

левый и постоянный. См. Http://www.postgresql.org/docs/8.4/interactive/indexes-types.html. –

1

Я подозреваю, что в первом случае компилятор/оптимизатор запросов обнаруживает, что предложение является константой и может построить оптимальный план запроса. Во втором он должен скомпилировать более общий запрос, потому что связанная переменная может быть чем-то во время выполнения.

+0

Почему я думаю, что я запрашиваю 'LIKE '%''? Я конкатенирую «%» с предоставленным «$ q» и ищем это. Например, в обоих случаях я ищу 'LIKE 'abcde%'', просто выполняя запросы по-разному. – punkish

+0

Я пропустил конкатенацию, извините. Я отредактировал свой ответ. Тем не менее, я все еще думаю, что основная предпосылка может быть действительной. В одном случае компилятор запроса получает константу, которую он может использовать при построении плана запроса. В другом случае компилятор должен построить план запроса для общего случая, когда связанная переменная может быть чем угодно. Вы должны проверить планы запросов, чтобы убедиться, что это так. Я никогда не работал с PG (просто Oracle и MySQL), поэтому я не знаю, как исследовать планы запросов. –

+0

Да, один интересный ответ в списке рассылки DBI заключается в том, что, возможно, планировщик запросов Pg не имеет столько информации в запросе со значением привязки, сколько в запросе с параметром inline. Я надеюсь услышать и от других, потому что, если это так, то это большой удар по связующим значениям в DBI, по крайней мере в этом случае. – punkish

-2

Я не знаю Postgres вообще, но я думаю, что в строке 7 (WHERE Lower (a) LIKE '$ q' ), $ q на самом деле является константой. Похоже, ваш редактор тоже так думает, так как он выделен красным цветом. Вам, вероятно, все еще нужно использовать? для переменной.

Чтобы проверить, сделайте COUNT (*) и убедитесь, что они совпадают - я мог бы быть вне базы.

+0

Нет, 'qq {...}' Perl действует как строка с двойными кавычками, поэтому '$ q' будет интерполировать. –

+0

Это не постоянный. Оператор завернут в 'qq {}', поэтому переменные интерполируются. – TLP

+0

или, может быть, попробуйте просто удалить одиночные кавычки вокруг $ q и посмотреть, скомпилируется ли он (так будет обрабатываться Oracle PL/SQL, но не уверен, как работает область действия в postgres). – klofton

0

Вы используете оба тестовых примера из того же файла, используя тот же объект $ dbh? Я думаю, что причина увеличения скорости во втором случае заключается в том, что вы используете подготовленный оператор, который уже разобран (но, может быть, я ошибаюсь :)).

+0

Подготовка заявления на самом деле очень быстро и является лишь проблемой, если вы делаете это очень много раз. В этом случае для многомиллионного запроса запроса готов только один запрос, поэтому эффект (даже если бы он был фактором) был бы незначительным. – zgpmax

-1

Ahh, я вижу - я выйду после этого комментария, так как я не знаю Perl. Но я бы верил, что редактор прав, выделив $ q как константу. Я предполагаю, что вам нужно объединить значение в строку, а не просто ссылаться на переменную. Таким образом, я думаю, что если + используется для объединения строк в Perl, а затем использовать что-то вроде:

мой $ SQL = кв.кв { ВЫБРАТЬ а, б, в ОТ т ГДЕ Lower (а) LIKE ' } + $ q + qq {'};

(Примечание: если язык не тесно интегрирован с базой данных, например Oracle/PLSQL, обычно перед созданием базы данных требуется создать полностью корректную строку SQL, вместо того чтобы ожидать, что компилятор «интерполирует»/Замените значение переменной.

Я бы еще раз предложил, чтобы вы получили инструкции COUNT(), чтобы убедиться, что вы сравниваете яблоко с яблоками.

+0

Извините, проигнорируйте это, должен был быть subpost ... – klofton

+0

Оператор конкатенации Perl - '.' и' qq {SELECT a, b, c FROM t WHERE Lower (a) LIKE '$ q'} 'и' qq {SELECT a, b, c FROM t WHERE Lower (a) LIKE '}. $ q. qq {'}; 'идентичны. – ikegami

+1

Если вы не знаете Perl, вы должны слушать тех, кто делает (mu, TLP). – ikegami

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