使用选择(高级)过滤数据[重复]

This question already has an answer here:

I'm on a project and I have 3 possibilities of filters. Filter by name,cities and province. Right now what i'm doing are simple IFs trying to think of all possibilities.

Example : it can be [Name][City][All] or [All][City][province] or [All][All][province] and it goes on in total of 8 to 9 possibilities. That means if have to do about 8 condition in my PHP code trying to catch everything.

So my question is : Is there a way on MySql to do a SELECT data1.. FROM tableName WHERE ... but when I don't give for example the city it search only with the name and the province.

</div>

There's a couple of approaches. One way to do it is to dynamically build the SQL text. If the options are discrete, we can just handle each condition separately, and include or not. As an example of the pattern:

start the SQL text:

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

conditionally append search conditions

  if(  we need to add a condition on city ) {
       $sql .= " AND t.city LIKE :city ";
  }
  if(  we need to add a condition on province ) {
       $sql .= " AND t.province LIKE :province ";
  }
  if(  we need to add a condition on name  ) {
       $sql .= " AND t.name LIKE :name ";
  }

finish the SQL text

  $sql .= " ORDER BY ..."; 

prepare the SQL text (here is a good place to echo/var_dump/log the contents of $sql for debugging)

  $sth = $dbh->prepare($sql);

conditionally bind values for any of the search conditions we added

  if(  we need to add a condition on city ) {
      $sth->bindValue(':city',$city);
  }
  if(  we need to add a condition on province ) {
      $sth->bindValue(':province',$province);
  }
  if(  we need to add a condition on name  ) {
      $sth->bindValue(':name',$name);
  }

execute

  $sth->execute();

Another alternative is to use static SQL with expression in the WHERE clause, using a "special" reserved value to represent "no search".

In this example, we are using a zero-length string represent "no search condition" ...

  $sql = "SELECT ...
            FROM ... t
           WHERE ( :city1     = '' OR t.city     LIKE :city2     ) 
             AND ( :province1 = '' OR t.province LIKE :province2 )
             AND ( :name1     = '' OR t.name     LIKE :name2     )
           ORDER BY ...";

  $sth = $dbh->prepare($sql);

  $sth->bindValue(':city1'     , $city     ); 
  $sth->bindValue(':city2'     , $city     ); 
  $sth->bindValue(':province1' , $province ); 
  $sth->bindValue(':province2' , $province ); 
  $sth->bindValue(':name1'     , $name     ); 
  $sth->bindValue(':name2'     , $name     ); 

  $sth->execute();

This could be done in mysql or php - but imho the php solution looks cleaner. To do this, you just have to build the query on runtime.

$name = ...
$city = ...
$province = ...

$fields = [
    'name' => $name,
    'city' => $city,
    'province' => $province
];
$where = [];
$values = [];

foreach ($fields as $field => $value) {
    if ($value) {
        $where[] = "$field = :$field";
        $values[$field] = $value;
    }
}

$sql = 'SELECT * FROM table';
if (count($where) > 0) {
    $sql .= ' WHERE ' . join(' AND ', $where);
}

// using pdo
$query = $pdo->prepare($sql);
$query->execute($values);
// ...

You could do something like this, storing your filters in an array and checking for the existence of each. If the filter has a value, dynamically add it to the where clause by using an array of where statements.

$filters = ['name', 'city', 'province'];
$where   = [];

foreach($filters as $filter) {
  if(!empty($_POST[$filter])) {
    $param_name          = ":{$filter}";
    $where[]             = "{$filter} = {$param_name}";
    $params[$param_name] = $_POST[$filter];
  }
}

$where_str = !empty($where) ? 'WHERE ' . join(' AND ', $where) : '';

$sql = "SELECT * FROM table {$where_str}";

$db = new PDO(<connection_str>);

$stmt = $db->prepare($sql);

$results = $stmt->execute($params);