Игнорировать определенные критерии WHERE

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

Вот воображаемый пример, чтобы проиллюстрировать, что я собираюсь сделать

$sql = 'SELECT * FROM people WHERE first_name = :first_name AND last_name = :last_name AND age = :age AND sex = :sex';
$query = $db->prepare($sql);
$query->execute(array(':first_name' => 'John', ':age' => '27');

Очевидно, что это не сработает, потому что количество предоставленных параметров не соответствует количеству ожидаемых параметров. Должен ли я каждый раз создавать запрос только с указанными параметрами, включенными в предложение WHERE, или есть способ заставить некоторые из этих параметров игнорироваться или всегда возвращать значение true при проверке?


person kotekzot    schedule 27.06.2012    source источник


Ответы (4)


SELECT * FROM people 
WHERE (first_name = :first_name or :first_name is null)
AND (last_name = :last_name or :last_name is null)
AND (age = :age or :age is null)
AND (sex = :sex or :sex is null)

При передаче параметров укажите null для тех, которые вам не нужны.

Обратите внимание, что для выполнения запроса таким образом, emulation mode для PDO необходимо повернуть ON

person juergen d    schedule 27.06.2012
comment
Я не думаю, что это то, чего он хочет. Это будет соответствовать, если first_name имеет совпадающее значение или если оно равно нулю. Но, насколько я понимаю, он хочет, чтобы first_name соответствовало чему угодно (как будто для этого столбца вообще не было предложения WHERE). - person netcoder; 27.06.2012
comment
@netcoder не проверяет, является ли first_name нулевым, он проверяет, является ли (first_name = @var or @var is null) - person rs.; 27.06.2012
comment
Это именно то, что я искал, спасибо! Довольно изобретательно. - person kotekzot; 27.06.2012
comment
@juergen d Это дает ряд связанных переменных PDOException, если ни один элемент массива не совпадает. См. мой комментарий ниже - person qais; 27.06.2012

Во-первых, начните с простого изменения строки $sql:

$sql = 'SELECT * FROM people WHERE 1 = 1';

WHERE 1 = 1 позволит вам не включать никаких дополнительных параметров...

Затем выборочно соедините с вашей строкой $sql любой дополнительный параметр, который имеет значимое значение:

$sql .= ' AND first_name = :first_name'
$sql .= ' AND age = :age'

Ваша строка $sql теперь содержит только те параметры, которые вы планируете предоставить, поэтому вы можете действовать, как и раньше:

$query = $db->prepare($sql);
$query->execute(array(':first_name' => 'John', ':age' => '27');
person Michael Fredrickson    schedule 27.06.2012

Если вы не можете решить проблему, изменив запрос... Существует несколько библиотек, которые помогают в сборке запросов. Я использовал Zend_Db_Select в прошлом, но, вероятно, каждый фреймворк имеет что-то подобное:

$select = new Zend_Db_Select;

$select->from('people');

if (!empty($lastName)) {
  $select->where('lastname = ?', $lastname);
}

$select->order('lastname desc')->limit(10);

echo $select; // SELECT * FROM people WHERE lastname = '...' ORDER BY lastname desc LIMIT 10
person Mike B    schedule 27.06.2012

Я протестировал решение, данное @juergen, но оно дает исключение PDOException, поскольку количество связанных переменных не совпадает. Следующий (не очень элегантный) код работает независимо от параметров:

function searchPeople( $inputArr )
{
  $allowed = array(':first_name'=>'first_name', ':last_name'=>'last_name', ':age'=>'age', ':sex'=>'sex');

  $sql  = 'SELECT * FROM sf_guard_user WHERE 1 = 1';

  foreach($allowed AS $key => $val)
  {
      if( array_key_exists( $key, $inputArr ) ){
          $sql .= ' AND '. $val .' = '. $key;
      }
  }

  $query = $db->prepare( $sql );
  $query->execute( $inputArr );
  return $query->fetchAll();
}

Использование:

$result = searchPeople(array(':first_name' => 'John', ':age' => '27'));
person qais    schedule 27.06.2012
comment
Используете ли вы именованные параметры и последнюю версию PHP+PDO? Он отлично работает для меня. - person kotekzot; 27.06.2012
comment
Да, я использую именованные параметры и PHP 5.3.5 с mysqlnd 5.0.7. Приведенный выше код работает нормально, но если я заменю только строку $sql на строку @juergen d, выдаст PDOException. Его метод, безусловно, более элегантен, и я бы предпочел использовать его. Есть идеи, что может быть не так? - person qais; 27.06.2012
comment
Вы должны предоставить null для нежелательных параметров. - person kotekzot; 27.06.2012
comment
Да. Что объясняет его. Я думал, вам нужен переменный размер массива в качестве входных данных. - person qais; 27.06.2012