优化数据库搜索查询
Optimize Database Search Query
我有 table 有以下列
-> col1, col2, col3
我正在尝试使用这些列进行搜索。所以我正在接受用户的 3 个输入。
简单的搜索规则:
1) 如果用户未输入 col
中的任何一个,则它应该仅使用其他 2 列进行搜索。
select * from myTable where col1="abc" and col2="def"; // something like this. Any combination like col1-col2, col1-col3 or col2-col3
2) 如果输入所有 col
则:
select * from myTable where col1="abc" and col2="def" and col3="ghi"; // something like this
3) 如果用户输入 col
中的任何一个,则:
select * from myTable where col1="abc"; // something like this. It can be col1, col2 or col3.
我知道这可以通过对数据库使用不同的 select
语句并在 Java 代码中使用 if-else
来完成。
我想要针对这种情况的最优化解决方案(几乎没有code/explanation)。
编辑
注意: 所有 3 列都是 NULL-able !!我正在使用 Microsoft-SQL Server (MSSQL) 但我想同时解决 MySQL 和 MSSQL
假设您分别绑定名为 :col1
、:col2
和 :col3
的变量,这可以通过使用几个 or
在单个语句中完成状况。这里的想法是让数据库为 eahc 列执行一个短路逻辑——如果用户传递 null,那部分条件只是评估为 true
,而不访问 table。如果传递的是真实值,则将其与 table.
中的列进行比较
SELECT *
FROM myTable
WHERE (:col1 IS NULL OR :col1 = '' OR :col1 = col1) AND
(:col2 IS NULL OR :col2 = '' OR :col2 = col2) AND
(:col3 IS NULL OR :col3 = '' OR :col3 = col3)
您可以使用 PHP 执行以下操作,使用 Java 执行相同的方法:
$mapColVal = array( 1 => $first_post_value, 2 => $second_post_value, 3 => $third_post_value);
$whereCond = '';
for($i = 1; $i <= 3; $i++){
$whereCond .= "col".$i. "=". $mapColValue[$i]." AND ";
}
$whereCond = subStr($whereCond,0,-5);
然后按照下面的步骤做:
SELECT * FROM my_table WHERE $whereCond;
您还可以在查询中使用嵌套 CASE
。那么当部分变量为null
.
时查询条件会更简单
第一个命题:
SELECT *
FROM myTable
WHERE
CASE
WHEN @col1 is NULL OR @col1 = '' THEN
CASE
WHEN @col2 is NULL OR @col2 = '' THEN
CASE
WHEN @col3 is NULL OR @col3 = '' THEN 1=1
ELSE @col3 = col3
END
ELSE
CASE
WHEN @col3 is NULL OR @col3 = '' THEN @col2 = col2
ELSE @col2 = col2 AND @col3 = col3
END
END
ELSE
CASE
WHEN @col2 is NULL OR @col2 = '' THEN
CASE
WHEN @col3 is NULL OR @col3 = '' THEN @col1 = col1
ELSE @col1 = col1 AND @col3 = col3
END
ELSE
CASE
WHEN @col3 is NULL OR @col3 = '' THEN @col1 = col1 AND @col2 = col2
ELSE @col1 = col1 AND @col2 = col2 AND @col3 = col3
END
END
END;
第二个命题:
SELECT *
FROM myTable
WHERE
col1 =
CASE
WHEN @col1 IS NULL OR @col1 = '' THEN col1
ELSE @col1
END
AND
col2 =
CASE
WHEN @col2 IS NULL OR @col2= '' THEN col2
ELSE @col2
END
AND
col3 =
CASE
WHEN @col3 IS NULL OR @col3= '' THEN col3
ELSE @col3
END;
你可以在SQLFiddle
中看到结果
编辑:
所以有三个不同的查询。一个是 Mureinik 提出的,上面两个是我提出的。要确定其中哪一个是最佳的,我们必须了解 MySQL(和其他 DBMS)在执行前如何优化查询。我们可以看到详情here.
对我们来说最重要的短语是
Constant condition removal
这意味着我的一个查询中的条件 (1=1)
将被删除。这也意味着当 :col1
和 :col2
都是空值并且 :col3 = 'aaa'
那么 Mureinik 的查询:
WHERE (NULL IS NULL OR NULL = '' OR NULL = col1) AND
(NULL IS NULL OR NULL = '' OR NULL = col2) AND
('aaa' IS NULL OR 'aaa' = '' OR 'aaa' = col3)
将简化为:
WHERE 'aaa' = col3
如果我们以这种方式分析所有 3 个建议的查询,我们将看到对于每组变量 col1
、col2
和 col3
,所有这些查询都将被 DBMS 优化为相同的查询。所以他们三人的表现都一样。所以你可以选择你想要的(Mureinik的那个似乎是最清楚的)
确定这就是您所需要的?
Select *
from myTable
where (col1 like @col1 +'%' or @col1 is null)
and (col2 like @col2 +'%' or @col2 is null)
and (col3 like @col3 +'%' or @col3 is null)
您应该注意的一点是,在 WHERE 子句中,添加 "OR" 通常会给查询增加很多开销。 "AND" 通常要快得多,并且需要编译器进行更少的计算。所以我会尽可能尝试一些不会使用它的东西。
这是我关于如何最好地优化它的想法:
1) 在所有 3 列(col1、col2、col3)上放置索引。
2) 理想情况下,确定要使用的列应该在 Java 中计算,并基于此查询将被触发。这是我的想法(在PHP,但可以扩展到Java...抱歉,不够熟悉!):
<?php
if (isset($_GET['options'])) {
$options = explode(",",$_GET['options']); // assuming you feed the columns separated with columns
}
if (isset($_GET['col1Value'])) {
$col1Value = $_GET['col1Value'];
}
if (isset($_GET['col2Value'])) {
$col2Value = $_GET['col2Value'];
}
if (isset($_GET['col3Value'])) {
$col3Value = $_GET['col3Value'];
}
if (in_array("col1",$options)) { // check to see if 'col1' exists in array
$clause = ' and coalesce(col1,'') = $col1Value';
}
if (in_array("col2",$options)) { // check to see if 'col2' exists in array
$clause = $clause.' and coalesce(col2,'') = $col2Value';
}
if (in_array("col3",$options)) { // check to see if 'col3' exists in array
$clause = $clause.' and coalesce(col3,'') = $col3Value';
}
$sql = "
select *
from table
where 1=1
$clause
";
pg_execute($databaseConnection,$sql);
?>
这可能不是 PHP 中的最佳示例,但希望它能给您一些想法....
干杯!
这可以很容易地在 where 子句中使用 case 来完成:
SET @col1='someterm1';
SET @col2='someterm2';
SET @col3=NULL;
SELECT *
FROM table tbl1
WHERE
CASE WHEN @col1 IS NULL THEN 1=1 ELSE tbl1.col1=@col1 END
AND CASE WHEN @col2 IS NULL THEN 1=1 ELSE tbl1.col2=@col2 END
AND CASE WHEN @col3 IS NULL THEN 1=1 ELSE tbl1.col3=@col3 END;
where 子句将仅在您传递 NON-NULL 值时搜索值。因此,当 variables/paramaters 被替换时,上面的语句将如下所示:
SELECT *
FROM table tbl1
WHERE
CASE WHEN @col1 IS NULL THEN 1=1 ELSE tbl1.col1='someterm1' END
AND CASE WHEN @col2 IS NULL THEN 1=1 ELSE tbl1.col2='someterm2' END
/* THIS LINE AND CASE WHEN @col3 IS NULL THEN 1=1 ELSE tbl1.col3=@col3 END; changes because of the NULL*/
AND 1=1;
因此,您可以传递您拥有的任何字段组合,只有那些字段将是 searched.For 您没有的字段发送 NULL 值,CASE 语句将其转换为 1=1 和标准未应用。
该技术适用于任何数据库引擎。
我假设您的列名不完全是 col1、col2、col3,并且将来列数可能会增加,因此您想要的东西在发生这种情况时不需要完全返工.因此,您将需要一个包含列名的数组。用户输入同样应该来自与列名数组大小相同的字符串列表。
我还将假设您正在使用某种准备好的语句,但如果没有,请遵循基本大纲。
此外,我的假设是,如果所有输入均为空,我们将 return 整个 table。
private final String[] COLUMNS = new String[]{"col1", "col2", "col3"};
public static PreparedStatement getStatement(String queryString){
//you do this
}
public static PreparedStatement generateOptimizedStatement(List<String> input) {
String whereOrAnd = " where ";
StringBuilder sb = new StringBuilder("select * from myTable ");
int i = 0;
for(int i = 0; i < COLUMNS.length; i++){
if(input.get(i) != null){
sb.append(whereOrAnd).append(COLUMNS[i]).append(" = ? ");
whereOrAnd = " and ";
}
}
PreparedStatement ps = getStatement(sb);
for(int j = 0; j < COLUMNS.length; j++){
String s = input.get(j);
if(s != null){
ps.setString(j+ 1, s); //prepared statement starts with index 1
}
}
return ps;
}
我有 table 有以下列
-> col1, col2, col3
我正在尝试使用这些列进行搜索。所以我正在接受用户的 3 个输入。
简单的搜索规则:
1) 如果用户未输入 col
中的任何一个,则它应该仅使用其他 2 列进行搜索。
select * from myTable where col1="abc" and col2="def"; // something like this. Any combination like col1-col2, col1-col3 or col2-col3
2) 如果输入所有 col
则:
select * from myTable where col1="abc" and col2="def" and col3="ghi"; // something like this
3) 如果用户输入 col
中的任何一个,则:
select * from myTable where col1="abc"; // something like this. It can be col1, col2 or col3.
我知道这可以通过对数据库使用不同的 select
语句并在 Java 代码中使用 if-else
来完成。
我想要针对这种情况的最优化解决方案(几乎没有code/explanation)。
编辑
注意: 所有 3 列都是 NULL-able !!我正在使用 Microsoft-SQL Server (MSSQL) 但我想同时解决 MySQL 和 MSSQL
假设您分别绑定名为 :col1
、:col2
和 :col3
的变量,这可以通过使用几个 or
在单个语句中完成状况。这里的想法是让数据库为 eahc 列执行一个短路逻辑——如果用户传递 null,那部分条件只是评估为 true
,而不访问 table。如果传递的是真实值,则将其与 table.
SELECT *
FROM myTable
WHERE (:col1 IS NULL OR :col1 = '' OR :col1 = col1) AND
(:col2 IS NULL OR :col2 = '' OR :col2 = col2) AND
(:col3 IS NULL OR :col3 = '' OR :col3 = col3)
您可以使用 PHP 执行以下操作,使用 Java 执行相同的方法:
$mapColVal = array( 1 => $first_post_value, 2 => $second_post_value, 3 => $third_post_value);
$whereCond = '';
for($i = 1; $i <= 3; $i++){
$whereCond .= "col".$i. "=". $mapColValue[$i]." AND ";
}
$whereCond = subStr($whereCond,0,-5);
然后按照下面的步骤做:
SELECT * FROM my_table WHERE $whereCond;
您还可以在查询中使用嵌套 CASE
。那么当部分变量为null
.
第一个命题:
SELECT *
FROM myTable
WHERE
CASE
WHEN @col1 is NULL OR @col1 = '' THEN
CASE
WHEN @col2 is NULL OR @col2 = '' THEN
CASE
WHEN @col3 is NULL OR @col3 = '' THEN 1=1
ELSE @col3 = col3
END
ELSE
CASE
WHEN @col3 is NULL OR @col3 = '' THEN @col2 = col2
ELSE @col2 = col2 AND @col3 = col3
END
END
ELSE
CASE
WHEN @col2 is NULL OR @col2 = '' THEN
CASE
WHEN @col3 is NULL OR @col3 = '' THEN @col1 = col1
ELSE @col1 = col1 AND @col3 = col3
END
ELSE
CASE
WHEN @col3 is NULL OR @col3 = '' THEN @col1 = col1 AND @col2 = col2
ELSE @col1 = col1 AND @col2 = col2 AND @col3 = col3
END
END
END;
第二个命题:
SELECT *
FROM myTable
WHERE
col1 =
CASE
WHEN @col1 IS NULL OR @col1 = '' THEN col1
ELSE @col1
END
AND
col2 =
CASE
WHEN @col2 IS NULL OR @col2= '' THEN col2
ELSE @col2
END
AND
col3 =
CASE
WHEN @col3 IS NULL OR @col3= '' THEN col3
ELSE @col3
END;
你可以在SQLFiddle
中看到结果编辑:
所以有三个不同的查询。一个是 Mureinik 提出的,上面两个是我提出的。要确定其中哪一个是最佳的,我们必须了解 MySQL(和其他 DBMS)在执行前如何优化查询。我们可以看到详情here.
对我们来说最重要的短语是
Constant condition removal
这意味着我的一个查询中的条件 (1=1)
将被删除。这也意味着当 :col1
和 :col2
都是空值并且 :col3 = 'aaa'
那么 Mureinik 的查询:
WHERE (NULL IS NULL OR NULL = '' OR NULL = col1) AND
(NULL IS NULL OR NULL = '' OR NULL = col2) AND
('aaa' IS NULL OR 'aaa' = '' OR 'aaa' = col3)
将简化为:
WHERE 'aaa' = col3
如果我们以这种方式分析所有 3 个建议的查询,我们将看到对于每组变量 col1
、col2
和 col3
,所有这些查询都将被 DBMS 优化为相同的查询。所以他们三人的表现都一样。所以你可以选择你想要的(Mureinik的那个似乎是最清楚的)
确定这就是您所需要的?
Select *
from myTable
where (col1 like @col1 +'%' or @col1 is null)
and (col2 like @col2 +'%' or @col2 is null)
and (col3 like @col3 +'%' or @col3 is null)
您应该注意的一点是,在 WHERE 子句中,添加 "OR" 通常会给查询增加很多开销。 "AND" 通常要快得多,并且需要编译器进行更少的计算。所以我会尽可能尝试一些不会使用它的东西。
这是我关于如何最好地优化它的想法:
1) 在所有 3 列(col1、col2、col3)上放置索引。 2) 理想情况下,确定要使用的列应该在 Java 中计算,并基于此查询将被触发。这是我的想法(在PHP,但可以扩展到Java...抱歉,不够熟悉!):
<?php
if (isset($_GET['options'])) {
$options = explode(",",$_GET['options']); // assuming you feed the columns separated with columns
}
if (isset($_GET['col1Value'])) {
$col1Value = $_GET['col1Value'];
}
if (isset($_GET['col2Value'])) {
$col2Value = $_GET['col2Value'];
}
if (isset($_GET['col3Value'])) {
$col3Value = $_GET['col3Value'];
}
if (in_array("col1",$options)) { // check to see if 'col1' exists in array
$clause = ' and coalesce(col1,'') = $col1Value';
}
if (in_array("col2",$options)) { // check to see if 'col2' exists in array
$clause = $clause.' and coalesce(col2,'') = $col2Value';
}
if (in_array("col3",$options)) { // check to see if 'col3' exists in array
$clause = $clause.' and coalesce(col3,'') = $col3Value';
}
$sql = "
select *
from table
where 1=1
$clause
";
pg_execute($databaseConnection,$sql);
?>
这可能不是 PHP 中的最佳示例,但希望它能给您一些想法....
干杯!
这可以很容易地在 where 子句中使用 case 来完成:
SET @col1='someterm1';
SET @col2='someterm2';
SET @col3=NULL;
SELECT *
FROM table tbl1
WHERE
CASE WHEN @col1 IS NULL THEN 1=1 ELSE tbl1.col1=@col1 END
AND CASE WHEN @col2 IS NULL THEN 1=1 ELSE tbl1.col2=@col2 END
AND CASE WHEN @col3 IS NULL THEN 1=1 ELSE tbl1.col3=@col3 END;
where 子句将仅在您传递 NON-NULL 值时搜索值。因此,当 variables/paramaters 被替换时,上面的语句将如下所示:
SELECT *
FROM table tbl1
WHERE
CASE WHEN @col1 IS NULL THEN 1=1 ELSE tbl1.col1='someterm1' END
AND CASE WHEN @col2 IS NULL THEN 1=1 ELSE tbl1.col2='someterm2' END
/* THIS LINE AND CASE WHEN @col3 IS NULL THEN 1=1 ELSE tbl1.col3=@col3 END; changes because of the NULL*/
AND 1=1;
因此,您可以传递您拥有的任何字段组合,只有那些字段将是 searched.For 您没有的字段发送 NULL 值,CASE 语句将其转换为 1=1 和标准未应用。
该技术适用于任何数据库引擎。
我假设您的列名不完全是 col1、col2、col3,并且将来列数可能会增加,因此您想要的东西在发生这种情况时不需要完全返工.因此,您将需要一个包含列名的数组。用户输入同样应该来自与列名数组大小相同的字符串列表。
我还将假设您正在使用某种准备好的语句,但如果没有,请遵循基本大纲。
此外,我的假设是,如果所有输入均为空,我们将 return 整个 table。
private final String[] COLUMNS = new String[]{"col1", "col2", "col3"};
public static PreparedStatement getStatement(String queryString){
//you do this
}
public static PreparedStatement generateOptimizedStatement(List<String> input) {
String whereOrAnd = " where ";
StringBuilder sb = new StringBuilder("select * from myTable ");
int i = 0;
for(int i = 0; i < COLUMNS.length; i++){
if(input.get(i) != null){
sb.append(whereOrAnd).append(COLUMNS[i]).append(" = ? ");
whereOrAnd = " and ";
}
}
PreparedStatement ps = getStatement(sb);
for(int j = 0; j < COLUMNS.length; j++){
String s = input.get(j);
if(s != null){
ps.setString(j+ 1, s); //prepared statement starts with index 1
}
}
return ps;
}