6.高级数据过滤
1.单字符匹配
进行单字符匹配的通配符为半角下划线“_”,它匹配单个出现的字符。比如通配符表达式“b_d”匹配第一个字符为b、第二个字符为任意字符、第三个字符为d的字符串,“bed”、“bad”都能匹配这个表达式。
单字符匹配在通配符表达式中可以出现多次:检索姓名长度为4、第三个字符为“n”、其它字符为任意字符的记录。
SELECT * FROM T_Employee WHERE FName LIKE '__n_'
2.多字符匹配
进行多字符匹配的通配符为半角百分号“%”,它匹配任意次数(零或多个)出现的任意字符。比如通配符表达式“k%”匹配以“k”开头、任意长度的字符串,“k”、“kerry”、“kb” 都能匹配这个表达式。
SELECT * FROM T_Employee WHERE FName LIKE '%n%'
3.集合匹配
进行集合匹配的通配符为“[]”,方括号中包含一个字符集,它匹配与字符集中任意一个字符相匹配的字符。比如通配符表达式“[bt]%”匹配第一个字符为b或者t、长度不限的
字符串,“bed”、“token”、“t”都能匹配这个表达式。
SELECT * FROM T_Employee WHERE FName LIKE '[SJ]%'
还可以使用否定符“^”来对集合取反,它匹配不与字符集中任意一个字符相匹配的字符。比如通配符表达式“[^bt]%”匹配第一个字符不为b或者t、长度不限的字符串。
SELECT * FROM T_Employee WHERE FName LIKE '[^SJ]%'
集合匹配可以通过变通手段来实现相同的效果:
SELECT * FROM T_Employee WHERE FName LIKE 'S%' OR FName LIKE 'J%'
SELECT * FROM T_Employee WHERE NOT(FName LIKE 'S%') AND NOT(FName LIKE 'J%')
4.空值检测
在SQL语句中对空值的处理有些特别,不能使用普通的等于运算符进行判断,而要使用IS NULL关键字。
SELECT * FROM T_Employee WHERE FNAME IS NULL
SELECT * FROM T_Employee WHERE FNAME IS NOT NULL
5.比较
相等:= 不等:<> 大于: > 小于:<
小于等于:<= 大于等于:>=
NOT运算符用来将一个表达式的值取反,也就是将值为“真”的表达式结果变为“假”、 将值为“假”的表达式结果变为“真”,使用方式也非常简单“NOT (表达式)”。
SELECT * FROM T_Employee WHERE NOT(FAge=22) AND NOT(FSALARY<2000)
6.多值检测
SQL提供了IN语句解决进行多个离散值的匹配问题,使用IN我们只要指定要匹配的数据集合就可以了,使用方法为“IN (值1,值2,值3……)”。
SELECT FAge,FNumber,FName FROM T_Employee WHERE FAge IN (23,25,28)
7.范围值检测
SELECT * FROM T_Employee WHERE FAGE BETWEEN 23 AND 27
数据库系统对“BETTWEEN AND”进行了查询优化,使用它进行范围值检测将会得到比其他方式更好的性能,因此在进行范围值检测的时候应该优先使用“BETTWEEN AND”。需要注意的就是“BETTWEEN AND”在进行检测的时候是包括了范围的边界值的(也就是闭区间)。
8.低效的“WHERE 1=1”
WHERE 1=1这样的SQL,是开发人员在使用动态组装的SQL。让我们想像如下的场景:用户要求提供一个灵活的查询界面来根据各种复杂的条件来查询员工信息,界面如下图:
当不选中年龄前的复选框的时候要使用下面的SQL语句:
SELECT * FROM T_Employee WHERE FNumber BETWEEN 'DEV001' AND 'DEV008' AND FName LIKE '%J%' AND FSalary BETWEEN 3000 AND 6000
而如果将所有的复选框都不选中的时候就要使用下面的SQL语句:
SELECT * FROM T_Employee
这里有一个问题就是当有复选框被选中的时候SQL语句是含有WHERE子句的,而当所有的复选框都没有被选中的时候就没有WHERE子句了,因此在添加每一个过滤条件判断的时候都要判断是否已经存在WHERE语句了,如果没有WHERE语句则添加WHERE语句。在判断每一个复选框的时候都要去判断,这使得用起来非常麻烦,“聪明的程序员是会偷懒的程序员”,因此开发人员想到了一个捷径:为SQL语句指定一个永远为真的条件语句(比如“1=1”),这样就不用考虑WHERE语句是否存在的问题了。
String sql = " SELECT * FROM T_Employee WHERE 1=1"; if(工号复选框选中) { sql+="AND FNumber BETWEEN '"+工号文本框1内容+"' AND '"+工号文本框2内容+"'"; } if(姓名复选框选中) { sql+="AND FName LIKE '%"+姓名文本框内容+"%'"; }
这样如果不选中姓名和年龄前的复选框的时候就会执行下面的SQL语句:
SELECT * FROM T_Employee WHERE 1=1 AND FNumber BETWEEN 'DEV001' AND 'DEV008' AND FSalary BETWEEN 3000 AND 6000
而如果将所有的复选框都不选中的时候就会执行下面的SQL语句:
SELECT * FROM T_Employee WHERE 1=1
这看似非常优美的解决了问题,殊不知这样很可能会造成非常大的性能损失,因为使用添加了“1=1”的过滤条件以后数据库系统就无法使用索引等查询优化策略,数据库系统将会被迫对每行数据进行扫描(也就是全表扫描)以比较此行是否满足过滤条件,当表中数据量比较大的时候查询速度会非常慢。因此如果数据检索对性能有比较高的要求就不要使用这种“简便”的方式。
private void doQuery() { bool hasWhere = false; string sql = " SELECT * FROM T_Employee"; if(工号复选框选中) { AppendWhereIfNeed(ref sql,ref hasWhere); sql+="FNumber BETWEEN '"+工号文本框1内容+"' AND '"+工号文本框2内容+"'"; } if(姓名复选框选中) { AppendWhereIfNeed(ref sql,ref hasWhere); sql+="FName LIKE '%"+姓名文本框内容+"%'"; } if(年龄复选框选中) { AppendWhereIfNeed(ref sql,ref hasWhere); sql+="FAge BETWEEN "+年龄文本框1内容+" AND "+年龄文本框2内容; } }
private void AppendWhereIfNeed(ref string sql,ref bool hasWhere) { if(hasWhere==false) { sql+=" WHERE ";
hasWhere=true; } else { sql+=" AND "; } }