SQLMAP编写规范以及一些常见的SQL问题
2011-03-29 16:00
393 查看
1.select语句中的*号问题
多表连接必须杜绝使用select *单表连接,一般情况不建议使用select *,但是如果出现以下情况,必须禁用:
1)表中包含lob字段(BLOB,CLOB,LONG,RAW等)
2)表中包含长度较大的字段,如varchar2(1000)以上的字段,但该SQL实际并不需要取出该字段的值
3)字段数量较多,但实际使用的字段很少,比如表有50个字段,而实际需要使用的只有5个,并且该SQL没有被重用
2.严格要求使用正确类型的变量,杜绝oracle做隐式类型转换的情况
1)推荐在sqlmap的变量中制定变量的数据类型,如:select * from tablename where id = #id:VARCHAR#
2)对于时间类型的字段,必须使用TO_DATE进行赋值(当前时间可直接用sysdate表示)
错误的写法(使用date类型的变量):
select * from tablename where id = #id:varchar#
and dt >= #dateBegin:date#
and dt < #dateEnd:date#
错误的写法(使用包含sysdate的表达式):
select * from tablename where id= #id:varchar#
and dt >= trunc(sysdate - 1)
and dt < sysdate + 1
错误的写法(将to_date函数和数字进行算术运算):
select * from tablename where id = #id:varchar#
and dt >= to_date(#dateBegin:varchar#, 'yyyy-mm-dd hh24:mi:ss')
and dt < to_date(#dateBegin:varchar#, 'yyyy-mm-dd hh24:mi:ss') + 1
正确的写法:
select * from tablename where id = #id:varchar#
and dt >= to_date(#dateBegin:varchar#, 'yyyy-mm-dd hh24:mi:ss')
and dt < to_date(#dateEnd:varchar#, 'yyyy-mm-dd hh24:mi:ss') /*或 dt < sysdate */
3.杜绝循环调用
例如:在迭代的过程中,使用同一SQL反复查询DB,如:while (listObj.hasnext()) { SELECT * FROM process_table WHERE (no = ?) ...... }
这样不仅效率不高,还造成交互过于频繁,严重情况会导致服务器LOAD增加
解决方式:
如果该查询是使用唯一键(如上例),参考后面的STR2VARLIST或STR2NUMLIST的用法
4.绑定变量和替代变量
在Ibatis中:绑定变量用 #变量名# 表示
替代变量用 $变量名$ 表示
注意几点:
1)通常,应使用绑定变量,尤其是具体取值变化范围较大的变量,如id =
#id#。
2)取值范围很小(比如枚举字段),并且通常取值会比较固定,在DBA预先同意的情况下使用替代变量,或者干脆使用常量。
3)当一个绑定变量在实际使用中实际取值总是为某一固定常量时,应当直接使用常量而不是变量
4)在order
by子句中,通常使用替代变量而不是绑定变量。
5)IN子句,使用"iterate + 数组类型变量"的方式实现绑定变量,例如:
<isNotEmpty prepend="and" property="userIds" > <iterate property="Ids" open="t.creator in (" close=")" conjunction="," > #Ids[]# </iterate> </isNotEmpty>
将生成 t.creator in (:1, :2, :3, :4, :5 ...) 的语句
5.在字段上加函数的问题
1)通常,不允许在字段上添加函数或者表达式,如:错误的写法:
select * from tableName where to_char ( dt, 'yyyy-mm-dd') = '2011-03-04'; select qty from tableName where id + 12 = 168;
正确的写法:
select * from tableName where dt >= to_date ( '2007-04-04', 'yyyy-mm-dd') and dt < to_date ( '2007-04-05', 'yyyy-mm-dd'); select qty from tableName where id = 168 - 12;
2)特别注意,当表连接时,用于连接的两个表的字段如果数据类型不一致,则必须在一边加上类型转换的函数,如
错误的写法(a.id是number类型,而b.operator_number是char类型):
select count(*) from tableName1 a, tableName2 b where a.id = b.operator_number and a.username = '小钗';
正确的写法:
select count(*) from tableName1 a, tableName2 b where to_char(a.id) = b.operator_number and a.username = '小钗'; select count(*) from tableName1 a, tableName2 b where a.id = to_number(b.operator_number) and a.username = '小钗';
6.表连接
不使用ANSI连接,如inner join、left join、right join、full outer join,而使用(+)来表示外连接错误的写法:
select a.*, b.goods_title from tableName1 a left join tableName2 b on a.NO = b.no where a.code = '1234' and a.name = #name:varchar# and a.dt > to_date(...)
正确的写法:
select a.*, b.goods_title from tableName a, tableName b where a.NO = b.no(+) and a.code = '1234' and a.name = #name:varchar# and a.dt > to_date(...)
7.SQLMAP的其它编写规范
1)对表的记录进行更新的时候,必须包含对gmt_modified字段的更新,并且不要使用dynamic标记,如:错误的写法:
update tableName <dynamic prepend="set" > ...... <isNotNull prepend="," property="gmtModified" > MODIFIED = #modified:TIMESTAMP# </isNotNull> </dynamic> where ID = #id#
正确的写法(当然,这里更推荐直接更新为sysdate):
update tableName set MODIFIED = #modified:TIMESTAMP# <dynamic> ...... </dynamic> where ID = #id#
2)不允许在where后添加1=1这样的无用条件,where可以写在prepend属性里,如:
错误的写法:
select count(*) from tableName t where 1=1 <dynamic> ...... </dynamic>
正确的写法:
select count(*) from tableName t <dynamic prepend="where" > ...... </dynamic>
3)对大表进行查询时,在SQLMAP中需要加上对空条件的判断语句,如:
性能上不保险的写法:
select count(*) from tableName a <dynamic prepend="where" > <isNotEmpty prepend="AND" property="id" > a.id = #id:varchar# </isNotEmpty> <isNotEmpty prepend="AND" property="email" > a.email = #email:varchar# </isNotEmpty> <isNotEmpty prepend="AND" property="type" > a.type = #type:varchar# </isNotEmpty> <isNotEmpty prepend="AND" property="no" > a.no = #no:varchar# </isNotEmpty> </dynamic>
性能上较保险的写法(防止那些能保证查询性能的关键条件都为空):
select count(*) from tableName a <dynamic prepend="where" > <isNotEmpty prepend="AND" property="id" > a.id = #id:varchar# </isNotEmpty> <isNotEmpty prepend="AND" property="email" > a.email = #email:varchar# </isNotEmpty> <isNotEmpty prepend="AND" property="type" > a.type = #type:varchar# </isNotEmpty> <isNotEmpty prepend="AND" property="no" > a.no = #no:varchar# </isNotEmpty> <isEmpty property="id" > <isEmpty property="email" > <isEmpty property="no" > query not allowed </isEmpty> </isEmpty> </isEmpty> </dynamic>
8.聚合函数常见问题
1)不要使用count(1)代替count(*)2)count(column_name)计算该列不为NULL的记录条数
3)count(distinct
column_name)计算该列不为NULL的不重复值数量
4)count()函数不会返回NULL,但sum()函数可能返回NULL,可以使用nvl(sum(qty),0)来避免返回NULL
9. NULL的使用
1)理解NULL的含义,是"不确定",而不是"空"2)查询时,使用is null或者is not
null
3)更新时,使用等于号,如:update tablename set column_name = null
10.STR2NUMLIST、STR2VARLIST函数的使用
1)适用情况:使用唯一值(或者接近唯一值)批量取数据时,能够大大减少和数据库的交互次数
2)编写规范:a表必须放在from
list的第一位,并且必须在select后加上下面的hint
注意一:参数是由各个交易号拼成的字符串,以逗号间隔,不要有空格
注意二:函数的参数是一个字符串,长度不能超过4000个字节
注意三:函数生成的表只有一个字段,名字是column_value
注意四:生成表的column_value字段是varchar2类型,如果希望是number类型,请使用str2numlist函数,并将vartabletype替换为numtabletype
错误的写法(缺少hint):
select a.column_value, b.goods_title from TABLE(CAST(str2varlist(:1) as vartabletype)) a, tableName b where a.column_value = b.no;
错误的写法(函数生成的表必须放在from list的第一位):
select /*+ ordered use_nl(a,b) */ a.column_value, b.goods_title from tableName b, TABLE(CAST(str2varlist(:1) as vartabletype)) a where a.column_value = b.no;
正确的写法:
select /*+ ordered use_nl(a,b) */ a.column_value, b.goods_title from TABLE(CAST(str2varlist(:1) as vartabletype)) a, tableName b where a.column_value = b.no;
如果要求返回的结果记录条数和参数中交易号的个数一致,可使用外连接:
select /*+ ordered use_nl(a,b) */ a.column_value, b.goods_title from TABLE(CAST(str2varlist(:1) as vartabletype)) a, tableName b where a.column_value = b.no (+);
如果参数内的值不唯一,可能返回多行,而并不需要返回多行,可考虑使用聚合函数:
select /*+ ordered use_nl(a,b) */ a.column_value, min(b.goods_title) goods_title from TABLE(CAST(str2varlist(:1) as vartabletype)) a, tableName b where a.column_value = b.no (+) group by a.column_value;
使用该函数与使用IN的区别:
(1)IN返回的结果是无序的,而该函数返回的结果是以参数中各值的顺序为顺序的
(2)IN返回的结果是不重复的,而该函数返回的结果可重复,取决于输入的参数
11.分页查询的使用
1)分页通常是先执行COUNT语句然后执行分页语句,当COUNT返回值为0的时候,应当避免执行后面的分页语句
2
)有时,只须执行分页语句而无须执行COUNT语句,就不要执行COUNT语句,例如,用户下载excel格式的账户明细
3
)有时,在分页前除了要统计COUNT还需要统计SUM,这些WHERE子句一致的统计应该在一条SQL中查出,而不是分多次统计
4)包含排序逻辑的分页查询写法,必须是三层select嵌套:
错误的写法:
SELECT t1.* FROM (SELECT t.*, ROWNUM rnum FROM tableName t WHERE no = :1 AND gmt_create >= TO_DATE (:2, 'yyyy-mm-dd') AND gmt_create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY create DESC) t1 WHERE rnum >= :4 AND rnum < :5
正确的写法:
SELECT t2.* FROM (SELECT t1.*, ROWNUM rnum FROM (SELECT t.* FROM tableName t WHERE no = :1 AND create >= TO_DATE (:2, 'yyyy-mm-dd') AND create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY create DESC) t1 WHERE ROWNUM <= :4) t2 WHERE rnum >= :5
5)不包含排序逻辑的分页查询写法,则是两层select嵌套,但对rownum的范围指定仍然必须在不同的查询层次指定:
错误的写法:
SELECT t1.* FROM (SELECT t.*, ROWNUM rnum FROM tableName t WHERE no = :1 AND create >= TO_DATE (:2, 'yyyy-mm-dd') AND create < TO_DATE (:3, 'yyyy-mm-dd')) t1 WHERE rnum >= :4 AND rnum <= :5
正确的写法:
SELECT t1.* FROM (SELECT t.*, ROWNUM rnum FROM tableName t WHERE seller_account = :1 AND create >= TO_DATE (:2, 'yyyy-mm-dd') AND create < TO_DATE (:3, 'yyyy-mm-dd') AND ROWNUM <= :4) t1 WHERE rnum >= :5
6)注意下面两种写法的逻辑含义是不同的:
按交易创建时间排序(倒序),然后再取前10条:
SELECT t2.* FROM (SELECT t1.*, ROWNUM rnum FROM (SELECT t.* FROM tableName t WHERE no = :1 AND create >= TO_DATE (:2, 'yyyy-mm-dd') AND create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY create DESC) t1 WHERE ROWNUM <= 10) t2 WHERE rnum >= 1
随机取10条,然后在这10条中按照交易创建时间排序(倒序):
SELECT t1.* FROM (SELECT t.*, ROWNUM rnum FROM tableName t WHERE no = :1 AND create >= TO_DATE (:2, 'yyyy-mm-dd') AND create < TO_DATE (:3, 'yyyy-mm-dd') AND ROWNUM <= 10 ORDER BY create DESC) t1 WHERE rnum >= 1
7)先连接后分页与先分页后连接
性能较差:
SELECT t2.* FROM (SELECT t1.*, ROWNUM rnum FROM (SELECT a.*, b.fee FROM tableName1 a, tableName2 b WHERE a.no = b.no(+) AND a.seller = :1 AND a.create >= TO_DATE (:2, 'yyyy-mm-dd') AND a.create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY a.create DESC) t1 WHERE ROWNUM <= :4) t2 WHERE rnum >= :5
性能较好:
SELECT /*+ ordered use_nl(a,b) */
a.*, b.fee
FROM (SELECT t2.* FROM (SELECT t1.*, ROWNUM rnum FROM (SELECT t.* FROM tableName t WHERE no = :1 AND create >= TO_DATE (:2, 'yyyy-mm-dd') AND create < TO_DATE (:3, 'yyyy-mm-dd') ORDER BY create DESC) t1 WHERE ROWNUM <= :4) t2 WHERE rnum >= :5) a,
tableName1 b
WHERE a.no = b.no(+)
后面这种写法的适用情况:
a、where子句中的查询条件都是针对
tableName
表的(否则得到的结果将不相同)
b、关联
tableName1
表时,用的是该表的主键或者唯一键字段(否则将改变结果集的条数)
相关文章推荐
- ASP.NET AJAX(Atlas)现存的一些常见问题以及解决方案[持续更新]
- Java代码规范和一些常见问题
- powerDesigner生成sql一些常见问题解决
- 如何配置java环境以及常见的一些问题处理
- document.body的一些用法以及js中的常见问题
- Java代码规范和一些常见问题
- linux下的wireshark最新版安装(源码安装)以及一些常见问题
- 转:SQL SERVER中一些常见性能问题的总结
- Java代码规范和一些常见问题
- ASP.NET AJAX 1.0资源连接以及对一些常见问题的答复
- 编写第一个javascript的XPCOM,以及一些小问题的解决。
- 【20180523】ProxySQL+MHA的配置以及一些问题描述
- ubuntu下Qt之android环境配置以及一些常见问题解决
- 初学Python以及安装的一些常见问题
- SQL执行的原理以及一些常见的关键字
- PowerDesigner16.5快速入门显示,注释comment配置方法,以及创建sql文件过程中需要注意的一些问题
- document.body的一些用法以及js中的常见问题
- 单链表基本操作以及一些常见的面试问题
- nagios安装配置以及一些常见问题处理
- 对 SQL编写规范 的一些认识