Dynami c необязательные параметры в предложении WHERE - PullRequest
0 голосов
/ 28 мая 2020

Рассмотрим SQL запрос, все параметры которого являются необязательными

SELECT ...
FROM Table
WHERE
 (@Col1 IS NULL OR Col1 = @Col1)
AND
 (@Col2 IS NULL OR Col2 = @Col2)
...
 (@ColN IS NULL OR ColN = @ColN)

Есть около 8 параметров, но в будущем их может быть больше.

Рецензент попросил никогда не писать такие WHERE конструкция (проверьте NULL или равенство) и вместо этого используйте динамические c SQL (без указания причины).

Мне это кажется понятным и понятным. Я еще не запускал план выполнения, но теоретически СУБД обнаружит любой параметр, который имеет значение NULL, и не рассмотрит второй член в скобках. Таким образом, я ожидаю, что будут сравниваться только указанные параметры.

  1. Может ли кто-нибудь поддержать или опровергнуть, почему это плохо?
  2. Индексирование. Можем ли мы вообще проиндексировать такой запрос (и как), если параметры поиска могут поступать в любой комбинации?

Ответы [ 2 ]

2 голосов
/ 28 мая 2020

Это сложно. Наличие нединамического запроса c имеет ряд преимуществ:

  • Это ясно.
  • Легче отлаживать и поддерживать.
  • Проверено при создании хранимой процедуры.
  • SQL Сервер будет поддерживать зависимости для вас, если код находится в хранимой процедуре.

С точки зрения кодирования, я думаю, что компиляция- проверка времени - большая победа. Это может предотвратить неожиданные сбои во время выполнения.

Запрос Dynami c в этом случае имеет одно преимущество: он, вероятно, будет перекомпилироваться каждый раз при выполнении.

Когда это произойдет Сделать разницу? Если у вас есть индекс для каждого из используемых столбцов, то всегда поможет перекомпиляция.

Однако вы можете получить лучшее из обоих миров, используя option (recompile). Это перекомпилирует код на основе текущих значений параметров. Вероятно, это лучший вариант для того, чем вы хотите заниматься.

1 голос
/ 28 мая 2020

Я склонен согласиться с вашим рецензентом. Вот почему:

Риск того, что у вас есть, составляет SQL Сервер скомпилирует этот запрос один раз при первом выполнении. Он будет делать оценки количества элементов на основе значений параметров, предоставленных в то время, и основывать на этом план выполнения. Этот план выполнения будет оставаться в кеше в течение некоторого времени, независимо от того, что на самом деле происходит при последующих запусках. Это может привести к очень плохим планам выполнения для некоторых значений параметров.

Как предлагает г-н Линофф (а он действительно знает свое дело), ​​вы можете учесть это с помощью option(recompile). Однако это означает, что SQL серверу потребуется перекомпилировать этот запрос при каждом выполнении . Если эти запросы выполняются часто, это может привести к тому, что SQL сервер со временем потратит много дополнительных усилий на перекомпиляцию.

Более того, мой опыт работы с этим типом запросов заключается в том, что пользователи склонны предоставлять определенные столбцы гораздо больше. чаще, чем другие. Если вы создаете запрос динамически, SQL Server может кэшировать планы для общих перестановок и сохранить эту дополнительную работу по перекомпиляции. С точки зрения администратора баз данных, теперь это также отдельные записи в кэше планов, которые вы можете проверить, чтобы понять, какие индексы могут быть более ценными для поддержки этой функции поиска с течением времени.

Конечно, зная, будут ли или нет, это будет выигрыш, значит знать свои данные, свою систему и своих пользователей. Вы должны выполнить работу, чтобы go вернуться и проверить, действительно ли пользователи выполняют поиск в одних и тех же наборах столбцов, или же они более случайны. создайте этот динамический c SQL в клиентском коде, что немного похоже на то, что вы ожидаете сделать это как часть хранимой процедуры.

Например, с псевдокодом C#:

sql = "SELECT ... FROM... WHERE 1=1";

if (txtCol1.Text != "")
{
   sql += " AND Col1 = @Col1"
   cmd.Parameters.AddWithValue("@Col1", txtCol1.Text);
}
if (txtCol1.Text != "")
{
    sql += " AND Col2 = @Col2"
    cmd.Parameters.AddWithValue("@Col2", txtCol2.Text);
}
//...
// Note this is just pseudocode. I'm not a fan of AddWithValue() in actual practice.

cmd.ExecuteReader();

Я знаю, что 1=1 кажется странным, но это не повредит SQL серверу, и это простой способ убедиться, что синтаксис все еще действителен, независимо от того, какой (если есть) из дальнейшие условия - первое, которое удерживает значение.

...