好的我需要根据一些用户输入构建一个查询来过滤结果。
查询基本上是这样的:
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;
所以我的问题是,我是否做了一些会对我的查询的性能产生负面影响的事情,或者以任何我应该远程担心的方式错误地提出任何其他问题?
答案 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
。
MySQL
中WHERE
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 = 5
和5 = 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)
我们在很久以前就做过类似的事情而且我们观察到了一些事情:
此外,如果您只有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 功能。