假设下表:
╔═══╦══════════════╦═════════════╗
║ ║Property A ║Property B ║
╠═══╬══════════════╬═════════════╣
║ 1 ║ slow ║low ║
║ 2 ║ fast ║high ║
╚═══╩══════════════╩═════════════╝
用户可以选择使用两个属性或仅使用一个属性来过滤结果。输入存储在变量$p1, $p2
如果两个变量都从用户获得值,我可以像这样轻松查询:
select * from table where (propertyA=$p1 and propertyB=$p2)
但是,如果其中一个没有得到用户的价值,我该如何查询?
由于上述查询propertyA=$p1
中的行会变为false,因为$p1
可能为null,但查询结果应根据$p2
的值返回。
例如对于输入(null,low),查询应该返回第一条记录,但是对于我的查询,它不会返回。
这个例子只适用于两个属性,我的表中有多个属性,因此实现多个if条件会很麻烦。
我想知道是否有任何简洁的方法可以解决这个问题。
谢谢!
答案 0 :(得分:2)
这个问题有两种基本方法: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=1
条款中包含条件WHERE
的原因并不会影响该陈述;优化器足够聪明,可以确定每个可能的行都是真的,这样条件就会被抛出。我们购买的是当我们附加到WHERE
子句时,我们不需要检查"这是WHERE子句中的第一个条件吗?"所以我们知道是否要将WHERE
或AND
附加到声明中。
第二种方法是使用静态SQL,使用一些表达式。
例如,假设propertyA和propertyB是字符类型列:
$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
进行比较。
我们可以使用不同的语法来实现相同的结果,例如,使用更加可移植的ANSI标准兼容COALESCE
函数代替IFNULL
,并使用CASE
表达式NULLIF
。 (这需要我们将$p1
提供给额外的占位符,像我们一样写,我们只需要提供$p1
一次。)
但这些是两种基本模式。选择你的毒药。
答案 1 :(得分:1)
如果两个属性都是字符串,则可以使用通配符'%'
select * from table where (propertyA like $p1 and propertyB like $p2)
答案 2 :(得分:0)
井查询是一个字符串,你需要先根据条件格式化字符串,然后在最终字符串准备就绪时,执行查询。
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);
希望这有帮助,谢谢