SQL-запрос | обработка нескольких условий где с потенциальным нулевым значением - PullRequest
0 голосов
/ 01 мая 2018

Допустим, следующая таблица:

╔═══╦══════════════╦═════════════╗
║   ║Property A    ║Property B   ║
╠═══╬══════════════╬═════════════╣
║ 1 ║ slow         ║low          ║
║ 2 ║ fast         ║high         ║
╚═══╩══════════════╩═════════════╝

Пользователь имеет возможность фильтровать результат, используя либо оба свойства, либо только одно. Входные данные хранятся в переменных $p1, $p2

Если обе переменные получают значение от пользователя, я могу легко запросить их следующим образом:

select * from table where (propertyA=$p1 and propertyB=$p2)

Однако, если один из них не получает значения от пользователя, как мне сделать запрос? Поскольку строка в приведенном выше запросе propertyA=$p1 станет ложной, поскольку $p1 может быть нулевым, однако результат запроса должен возвращаться на основе значения $p2.

Например, для ввода (null, low) запрос должен вернуть первую запись, но с моим запросом он не будет.

Этот пример только для двух свойств, у меня в таблице несколько свойств, поэтому реализация нескольких, если условия будут хлопотными.

Мне было интересно, есть ли какой-нибудь краткий способ сделать это.

Спасибо!

Ответы [ 3 ]

0 голосов
/ 01 мая 2018

Ну, а запрос - это строка, которую нужно сначала отформатировать, исходя из условий, а затем, когда готовая строка готова, выполнить запрос.

function chechForProperty($variable, $colName)
{
      if(isset($variable) || $variable != '')
      {
           return $colName."=".$variable." and ";
      } 
      return "";
}


$query = "select * from table where (";
$query .= checkForProperty($p1,'propertyA');
$query .= checkForProperty($p2,'propertyB');
$query .= checkForProperty($p3,'propertyC');
$query = substr($query,0,-5);
$query .= ");";

// now execute the query, I am just printing
print_r($query); 

Надеюсь, это поможет, спасибо

0 голосов
/ 01 мая 2018

Существует два основных подхода к этой проблеме: 1) динамическая генерация оператора SQL, 2) обработка NULL с выражениями в статической инструкции SQL

Динамический подход, если запрос выполняется из приложения ...

мы начинаем со статической строки, как будет выглядеть запрос каждый раз:

 $sql = 'SELECT t.id
           FROM mytable t
         WHERE 1=1';

и затем решить, собираемся ли мы добавить еще одно условие в предложение WHERE

 if( $p1 !== '' ) {
    $sql .= ' AND t.propertyA = :p1';
 }
 if( $p2 !== '' ) {
    $sql .= ' AND t.propertyB = :p2';
 }

 // prepare the SQL statement
 $sth=$pdo->prepare($sql);

 // conditionally bind values to the placeholders
 if( $p1 !== '' ) {
    $sth->bindValue(':p1',$p1);
 }
 if( $p2 !== '' ) {
    $sth->bindValue(':p2',$p2);
 }

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

Другим недостатком является большое разнообразие SQL-операторов, которые мы можем в конечном итоге создать, и обеспечение того, что у каждого варианта будет подходящий план выполнения, становится сложным. Имея только два необязательных условия в предложении WHERE, мы получаем очень легко управляемые 4 варианта ...

О, причина включения условия 1=1 в предложение WHERE не влияет на утверждение; оптимизатор достаточно умен, чтобы понять, что это верно для каждой возможной строки, так что условие сбрасывается. Что нас покупает, так это когда мы добавляем к предложению WHERE, мы избавляемся от необходимости проверять «это первое условие в предложении WHERE?» поэтому мы знаем, добавлять ли WHERE или AND к утверждению.


Второй подход - использовать статический с SQL, используя некоторые выражения.

В качестве примера предположим, что propertyA и PropertB являются столбцами символьного типа:

$sql = "SELECT t.id 
          FROM mytable t
         WHERE t.propertyA <=> IFNULL(NULLIF(:p1,''),t.propertyA) 
           AND t.propertyB <=> IFNULL(NULLIF(:p2,''),t.propertyB)"; 

$sth = $pdo->prepare($sql);
$sth->bindValue(':p1',$p1);
$sth->bindValue(':p2',$p2);

Если мы предоставим ненулевую строку для :p1, то функция NULLIF вернет :p1, а функция IFNULL вернет :p1. Это будет, как если бы мы только что написали:

 t.propertyA <=> :p1  

Если мы предоставим строку нулевой длины для $p1 (для заполнителя :p1), то функция SQL NULLIF вернет NULL. И, в свою очередь, функция IFNULL вернет t.propertyA, поэтому оператор будет сравнивать propertyA с самим собой, поэтому чистый результат будет таким, как мы написали

AND t.propertyA <=> t.propertyA  

или просто

AND 1=1

(Разница в том, что оптимизатор не откажется от нашего условия, так как оптимизатор не знает, какое значение мы собираемся предоставить: p1, когда подготовлен план выполнения.

ПРИМЕЧАНИЕ. Оператор космического корабля <=> является NULL-безопасным сравнением. Он гарантированно вернет TRUE или FALSE (а не вернет NULL), в отличие от стандартного сравнения на равенство (=), которое возвращает NULL, когда любое (или оба) из сравниваемых значений равно NULL.

Это:

foo <=> bar

по существу означает сокращение для эквивалента:

foo = bar OR ( foo IS NULL AND bar IS NULL ) 

Если мы гарантируем, что propertyA никогда не будет NULL (например, с помощью явного ограничения NOT NULL в определении таблицы), мы можем отказаться от оператора космического корабля и просто использовать сравнение с простым равенством.

Недостатком этого подхода является менее интуитивный оператор SQL; непосвященные могут царапать голову над ним. Поэтому мы хотим оставить комментарий в коде, объясняющий, что условием сопоставления является условие, если :p1 - пустая строка, сравнение с :p1.

невозможно.

Мы могли бы использовать другой синтаксис для достижения того же результата, например, используя более портативную функцию COALESCE, соответствующую стандартам ANSI, вместо IFNULL и выражение CASE вместо NULLIF. (Это потребовало бы, чтобы мы поставили $p1 дополнительному заполнителю, написав это так, как мы, мы должны предоставить $p1 только один раз.)


Но это две основные модели. Выбери свой яд.

0 голосов
/ 01 мая 2018

Если оба свойства являются строками, вы можете использовать подстановочный знак '%'

select * from table where (propertyA like $p1 and propertyB like $p2)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...