优化数据库搜索查询

时间:2015-01-27 06:05:51

标签: java mysql sql sql-server jdbc

我的桌子有以下栏目 - > 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来完成。

对于这种情况,我想要一个 MOST OPTIMIZED 解决方案(几乎没有代码/解释)。

修改

注意:所有3列 NULL -able !!我正在使用 Microsoft-SQL Server (MSSQL)但我想要解决方案,MySQL和MSSQL

8 个答案:

答案 0 :(得分:5)

假设您分别绑定了名为:col1:col2:col3的变量,可以使用一些or在单个语句中完成此操作条件。这里的想法是让数据库为eahc列执行短路逻辑 - 如果用户传递null,那么条件的一部分仅被评估为true,而不访问该表。如果传递了实际值,则将其与表中的列进行比较。

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)

答案 1 :(得分:3)

当然这就是你需要的一切吗?

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)

答案 2 :(得分:1)

您还可以在查询中使用嵌套的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

对我们来说最重要的一句是

  

恒定条件删除

这意味着我的一个查询中的条件(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个提议的查询,我们将看到对于每组变量col1col2col3,所有这些查询都将由DBMS优化到完全相同查询。因此,他们三个都具有同样的表现。因此,你可以选择你想要的任何一个(Mureinik的一个似乎是最清晰的)

答案 3 :(得分:0)

您可以使用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;

答案 4 :(得分:0)

使用where子句中的案例可以很容易地完成:

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;

如果传递NON-NULL值,where子句将仅搜索值。因此,当替换变量/参数时,上面的语句将如下所示:

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;

因此,您可以传递您拥有的任何字段组合,并且只搜索那些字段。对于您没有发送NULL值的字段,CASE语句将其转换为1 = 1,并且不应用条件。

该技术应适用于任何数据库引擎。

答案 5 :(得分:0)

我将假设您的列的名称不完全是col1,col2,col3,并且列的数量可能在将来扩展,因此您需要在发生这种情况时不需要完全重做的内容。因此,您需要一个包含列名的数组。用户输入同样应来自与列名称数组大小相同的字符串列表。

我还假设您正在使用某种准备好的声明,但如果没有,请按照基本大纲进行。

此外,我的假设是如果所有输入都为null,我们将返回整个表。

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

答案 6 :(得分:-1)

您应该注意的一点是,在WHERE子句中,添加&#34; OR&#34;通常会给查询增加很多开销。 &#34; AND&#34;通常要快得多,并且编译器需要的计算量更少。所以我会尝试尽可能不使用它的东西。

以下是我如何最好地优化这一点的想法:

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中最好的例子,但希望能给你一些想法......

干杯!

答案 7 :(得分:-1)

好告诉我哪种情况不起作用,

declare @col1 varchar(50) 
declare @col2 varchar(50) 
declare @col13 varchar(50) 
if(@col1='')
set @col1=null
if(@col2='')
set @col2=null
if(@col13='')
set @col13=null
select * from myTable
 where 
((col1=@col1) or (@col1 is null) )
and ((col2=@col2) or (@col2 is null) ) 
and ((@col13=@col3) or (@col13 is null)