sql查询|处理可能为空值的多个where条件

lqfhib0f  于 2021-06-21  发布在  Mysql
关注(0)|答案(3)|浏览(436)

假设下表:

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

用户可以选择使用两个属性或一个属性筛选结果。输入存储在变量中 $p1, $p2 如果这两个变量都从用户那里得到一个值,我可以像这样轻松地查询它们:

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

但是,如果其中一个没有从用户那里得到任何值,我该如何查询?因为在上面的查询中 propertyA=$p1 从那以后就变成假的了 $p1 可以为null,但查询结果应基于的值返回 $p2 .
例如,对于输入(null,low),查询应该返回第一条记录,但是对于我的查询,它不会返回。
这个例子只针对两个属性,我的表中有多个属性,所以实现多个if条件会很麻烦。
我想知道有没有什么简洁的方法来处理这个问题。
谢谢您!

nuypyhwy

nuypyhwy1#

好吧,查询是一个字符串毕竟,你需要先格式化字符串,根据条件,然后当最后一个字符串准备好,执行查询。

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);

希望这有帮助,谢谢

kkbh8khc

kkbh8khc2#

如果两个属性都是字符串,则可以使用通配符 '%' ```
select * from table where (propertyA like $p1 and propertyB like $p2)

c90pui9n

c90pui9n3#

解决这个问题有两种基本方法:1)动态生成sql语句,2)在静态sql语句中用表达式处理null
动态方法,如果查询是从应用程序执行的。。。
我们从一个静态字符串开始,每次查询都是这样的:

$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=1WHERE 子句不影响语句;优化器足够聪明,可以找出每一行的值都是真的,所以条件会被抛出。当我们附加到 WHERE 子句中,我们不再需要检查“这是where子句中的第一个条件吗?”这样我们就知道是否要附加 WHERE 或者 AND 对声明的回应。
第二种方法是在sql中使用static,使用一些表达式。
例如,假设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提供什么值。
注:以下为 <=> spaceship运算符是空安全比较。它保证返回true或false(不返回null),这与标准的相等比较不同( = )当被比较的值中的一个(或两个)为null时,返回null。
这是:

foo <=> bar

基本上是等价物的简写:

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

如果我们保证 propertyA 将永远不会为null(例如,通过表定义中的显式notnull约束),我们可以放弃spaceship操作符,只使用简单的相等比较。
这种方法的缺点是sql语句不够直观;不知情的人可能会为此挠头。所以我们要在代码中留下一个注解,解释匹配的条件是condition,if :p1 是空字符串,与 :p1 .
我们可以使用不同的语法来实现相同的结果,例如,使用更具可移植性的符合ansi标准的语法 COALESCE 功能代替 IFNULL ,和 CASE 表达式代替 NULLIF . (那就需要我们提供 $p1 一个额外的占位符,写它像我们做的,我们只需要提供 $p1 一次。)
但这是两种基本模式。摘下你的毒药。

相关问题