我的查询有多糟糕?

时间:2009-09-02 15:09:52

标签: sql mysql performance optimization

好的我需要根据一些用户输入构建一个查询来过滤结果。

查询基本上是这样的:

SELECT * FROM my_table ORDER BY ordering_fld;

用户可以选择四个文本框来过滤数据,这意味着我必须为所使用的第一个过滤器动态构建一个“WHERE”子句,然后为输入的每个后续过滤器创建“AND”子句。

因为我懒得这样做,所以我只是将每个过滤器都设为“AND”子句,并在默认情况下在查询中放置一个“WHERE 1”子句。

所以现在我有:

SELECT * FROM my_table WHERE 1 {AND filters} ORDER BY ordering_fld;

所以我的问题是,我是否做了一些会对我的查询的性能产生负面影响的事情,或者以任何我应该远程担心的方式错误地提出任何其他问题?

12 个答案:

答案 0 :(得分:37)

MySQL会优化您的1

我刚刚在我的测试数据库上运行了这个查询:

EXPLAIN EXTENDED
SELECT  *
FROM    t_source
WHERE   1 AND id < 100

它给了我以下description

select `test`.`t_source`.`id` AS `id`,`test`.`t_source`.`value` AS `value`,`test`.`t_source`.`val` AS `val`,`test`.`t_source`.`nid` AS `nid` from `test`.`t_source` where (`test`.`t_source`.`id` < 100)

如您所见,根本没有1

MySQLWHERE clause optimization的文档提到了这一点:

  
      
  • 恒定折叠:

    (a<b AND b=c) AND a=5
    -> b>5 AND b=c AND a=5
    
  •   
  • 恒定条件移除(因常量折叠而需要):

    (B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)
    -> B=5 OR B=6
    
  •   

请注意上面示例中的5 = 55 = 6部分。

答案 1 :(得分:8)

您可以解释您的查询:
http://dev.mysql.com/doc/refman/5.0/en/explain.html

看看它有什么不同,我怀疑。我会使用1 = 1,这样就更清楚了。

您可能想要添加 LIMIT 1000 或其他东西,当没有使用参数且表格变大时,您真的想要归还所有内容吗?

答案 2 :(得分:5)

WHERE 1是一个不变的,确定性的表达式,它将被任何体面的数据库引擎“优化”。

答案 3 :(得分:4)

如果您选择的语言有一种好方法可以避免自己构建SQL,请使用它。我喜欢Python和Django,而Django ORM可以很容易地根据用户输入过滤结果。

如果您致力于自己构建SQL,请确保针对SQL注入清理用户输入,并尝试将SQL构建封装在与过滤器逻辑不同的模块中。

此外,在问题成为问题之前,查询性能不应该是你的问题,在你有数千或数百万行之前它可能不会。当确实需要优化时,在用于WHERE和JOIN的列上添加一些索引会有很长的路要走。

答案 4 :(得分:2)

要提高性能,请在字段中使用列索引侦听“WHERE”

答案 5 :(得分:2)

标准SQL注入免责声明......

你可以做的一件事是,为了避免SQL注入,因为你知道它只有四个参数是使用存储过程,你传递字段的值或NULL。我不确定mySQL存储过程语法,但查询将归结为

SELECT *
  FROM my_table
 WHERE Field1 = ISNULL(@Field1, Field1)
   AND Field2 = ISNULL(@Field2, Field2)
   ...
 ORDRE BY ordering_fld

答案 6 :(得分:2)

我们在很久以前就做过类似的事情而且我们观察到了一些事情:

  • 在我们(​​可能)过滤,提高性能的列上设置索引
  • 如果不使用过滤器,则可以完全省略WHERE 1部分。 (不确定它是否适用于您的情况)没有区别,但“感觉”正确。
  • 不应忘记SQL注入

此外,如果您只有4个过滤器,则可以构建存储过程并传入空值并检查它们。 (就像n8wrl在此期间建议的那样)

答案 7 :(得分:2)

这将有效 - 一些注意事项:

关于动态构建的SQL,一般来说,某些数据库(至少是Oracle)会缓存查询的执行计划,因此如果您最终多次运行相同的查询,则不必从头开始完全重新开始。如果您使用动态构建的SQL,则每次创建一个不同的查询,因此对于数据库,它将看起来像100个不同的查询,而不是相同查询的100次运行。

您可能只需要衡量效果,以确定它是否适合您。

你需要所有的栏目吗?明确指定它们可能比使用*更好,因为:

  • 您可以直观地看到要返回的列
  • 如果您稍后在表中添加或删除列,则不会更改您的界面

答案 8 :(得分:2)

还不错,我不知道这个片段可以摆脱'这是第一个过滤器3'的问题。

你应该为你的代码感到羞耻(^^),它对性能没有任何作用,因为任何数据库引擎都会优化它。

答案 9 :(得分:2)

我使用WHERE 1 = 1的唯一原因是动态SQL;使用WHERE可以更轻松地添加AND ...条款。这不是我将在SQL中包含的内容 - 它不会影响整个查询,因为它总是评估为true并且不会触及所涉及的表,因此没有基于的任何索引查找或表扫描它

我无法谈论MySQL如何处理可选标准,但我知道使用以下内容:

WHERE (@param IS NULL OR t.column = @param)

...是处理可选参数的典型方法。 COALESCE和ISNULL并不理想,因为查询仍在使用基于标记值的索引(或更糟糕的是,表扫描)。除非提供了值,否则我提供的示例不会触及表格。

也就是说,我使用Oracle(9i,10g)的经验表明它不能很好地处理[WHERE (@param IS NULL OR t.column = @param)]。通过将SQL转换为动态,我看到了巨大的性能提升,并使用CONTEXT变量来确定要添加的内容。我对SQL Server 2005的印象是这些处理得更好。

答案 10 :(得分:2)

我通常做过这样的事情:

for(int i=0; i<numConditions; i++) {
  sql += (i == 0 ? "WHERE " : "AND ");
  sql += dbFieldNames[i] + " = " + safeVariableValues[i];
}

使生成的查询更清晰。

答案 11 :(得分:2)

我有时使用的一种替代方法是将where子句构建为数组,然后将它们连接在一起:

my @wherefields;
foreach $c (@conditionfields) {
  push @wherefields, "$c = ?",
}

my $sql = "select * from table";
if(@wherefields) { $sql.=" WHERE " . join (" AND ", @wherefields); }

以上是用perl编写的,但大多数语言都有某种 join 功能。