Насколько плохо мой запрос?
Ok Мне нужно создать запрос, основанный на некотором пользовательском входе для фильтрации результатов.
В основном запрос выглядит примерно так:
SELECT * FROM my_table ORDER BY ordering_fld;
Есть четыре текстовых поля, в которых пользователи могут фильтровать данные, что означает, что мне пришлось бы динамически строить в нем предложение "WHERE" для первого используемого фильтра, а затем предложения "И" для каждого последующего фильтра.
Поскольку я слишком ленив, чтобы сделать это, я только что сделал каждый фильтр предложением "И" и по умолчанию поставил предложение WHERE 1 в запросе.
Итак, теперь у меня есть:
SELECT * FROM my_table WHERE 1 {AND filters} ORDER BY ordering_fld;
Итак, мой вопрос: сделал ли я что-то, что отрицательно повлияет на производительность моего запроса или что-то еще не сработает, я должен быть удаленно обеспокоен?
Ответы
Ответ 1
MySQL
будет оптимизировать ваш 1
.
Я просто запустил этот запрос в своей тестовой базе данных:
EXPLAIN EXTENDED
SELECT *
FROM t_source
WHERE 1 AND id < 100
и он дал мне следующий description
:
select `test`.`t_source`.`id` AS `id`,`test`.`t_source`.`value` AS `value`,`test`.`t_source`.`val` AS `val`,`test`.`t_source`.`nid` AS `nid` from `test`.`t_source` where (`test`.`t_source`.`id` < 100)
Как вы можете видеть, нет 1
вообще.
В документации WHERE
оптимизация предложения в MySQL
упоминается следующее:
-
Постоянное складывание:
(a<b AND b=c) AND a=5
-> b>5 AND b=c AND a=5
-
Удаление константного состояния (необходимо из-за постоянного складывания):
(B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)
-> B=5 OR B=6
Примечание 5 = 5
и 5 = 6
части в приведенном выше примере.
Ответ 2
Вы можете ОБЪЯСНИТЬ ваш запрос:
http://dev.mysql.com/doc/refman/5.0/en/explain.html
и посмотрим, делает ли он что-то по-другому, что я сомневаюсь. Я бы использовал 1 = 1, так что это более понятно.
Возможно, вы захотите добавить LIMIT 1000 или что-то еще, когда параметры не используются и таблица становится большой, действительно ли вы хотите вернуть все?
Ответ 3
WHERE 1
- это постоянное детерминированное выражение, которое будет "оптимизировано" любым достойным механизмом БД.
Ответ 4
Если на вашем выбранном языке есть хороший способ избежать создания SQL самостоятельно, используйте это вместо этого. Мне нравятся Python и Django, а Django ORM позволяет легко фильтровать результаты на основе пользовательского ввода.
Если вы привержены построению SQL самостоятельно, не забудьте очистить входы пользователя от SQL-инъекции и попытайтесь инкапсулировать SQL-построение в отдельный модуль из вашей логики фильтра.
Кроме того, производительность запросов не должна быть вашей проблемой, пока это не станет проблемой, которая, вероятно, не будет, пока у вас не будет тысяч или миллионов строк. И когда пришло время оптимизировать, добавление нескольких индексов для столбцов, используемых для WHERE и JOIN, идет долгий путь.
Ответ 5
Чтобы повысить производительность, используйте индексы столбцов в полях, прослушиваемых в "ГДЕ"
Ответ 6
Отказ от стандартных инъекций SQL здесь...
Одна вещь, которую вы могли бы сделать, чтобы избежать инъекции SQL, поскольку вы знаете, что только четыре параметра используют хранимую процедуру, в которой вы передаете значения для полей или NULL. Я не уверен в синтаксисе хранимых профайлов mySQL, но запрос будет сжиматься до
SELECT *
FROM my_table
WHERE Field1 = ISNULL(@Field1, Field1)
AND Field2 = ISNULL(@Field2, Field2)
...
ORDRE BY ordering_fld
Ответ 7
Мы делали что-то похожее не так давно, и есть несколько вещей, которые мы наблюдали:
- Настройка индексов на столбцах, которые мы (возможно) фильтровали, улучшали производительность
- Часть WHERE 1 может быть полностью исключена, если фильтры не используются. (не уверен, относится ли это к вашему делу) Не имеет значения, но "чувствует" право.
- Не следует забывать SQL-инъекцию
Кроме того, если у вас есть только 4 фильтра, вы можете создать хранимую процедуру и передать нулевые значения и проверить их. (так же, как и n8wrl)
Ответ 8
Это сработает - некоторые соображения:
О динамически построенных SQL в целом, некоторые базы данных (по крайней мере, Oracle) будут кэшировать планы выполнения запросов, поэтому, если вы будете запускать один и тот же запрос много раз, ему не придется полностью начинать с нуля. Если вы используете динамически построенный SQL, вы каждый раз создаете другой запрос, поэтому в базу данных он будет выглядеть как 100 различных запросов вместо 100 запусков одного и того же запроса.
Вам, вероятно, просто нужно будет измерить производительность, чтобы узнать, хорошо ли это работает для вас.
Вам нужны все столбцы? Явное указание их, вероятно, лучше, чем использование * anyways, потому что:
- Вы можете визуально увидеть, какие столбцы возвращаются.
- Если вы добавите или удалите столбцы в таблицу позже, они не изменят ваш интерфейс
Ответ 9
Неплохо, я не знал этого фрагмента, чтобы избавиться от вопроса "это первый фильтр 3".
Вам должно быть стыдно за ваш код (^^), он ничего не делает для производительности, поскольку любой движок БД оптимизирует его.
Ответ 10
Единственная причина, по которой я использовал WHERE 1 = 1
, для динамического SQL; это взломать, чтобы упростить добавление предложений WHERE
, используя AND ...
. Это не то, что я хотел бы включить в мой SQL в противном случае - он не делает ничего, чтобы повлиять на общий запрос, потому что он всегда оценивается как истинный и не попадает в таблицу (-ы), поэтому нет никаких поисков индексов или табличных сканирований на основе он.
Я не могу говорить о том, как MySQL обрабатывает необязательные критерии, но я знаю, что используя следующее:
WHERE (@param IS NULL OR t.column = @param)
... является типичным способом обработки необязательных параметров. COALESCE и ISNULL не идеальны, потому что запрос все еще использует индексы (или, что хуже, сканирование таблиц) на основе контрольного значения. Представленный мной пример не попадет в таблицу, если не указано значение.
Таким образом, мой опыт работы с Oracle (9i, 10g) показал, что он не обрабатывает [ WHERE (@param IS NULL OR t.column = @param)
] очень хорошо. Я увидел огромный прирост производительности за счет преобразования SQL в динамический и использовал переменные CONTEXT, чтобы определить, что добавить. Мое впечатление от SQL Server 2005 заключается в том, что они обрабатываются лучше.
Ответ 11
Я обычно делал что-то вроде этого:
for(int i=0; i<numConditions; i++) {
sql += (i == 0 ? "WHERE " : "AND ");
sql += dbFieldNames[i] + " = " + safeVariableValues[i];
}
Делает сгенерированный запрос немного более чистым.
Ответ 12
Одна из альтернатив, которую я иногда использую, заключается в том, чтобы построить предложение where в массиве и затем объединить их:
my @wherefields;
foreach $c (@conditionfields) {
push @wherefields, "$c = ?",
}
my $sql = "select * from table";
if(@wherefields) { $sql.=" WHERE " . join (" AND ", @wherefields); }
Выше описано в perl, но большинство языков имеют какую-то функцию соединения.