您的位置:首页 > 数据库

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
表时,用的是该表的主键或者唯一键字段(否则将改变结果集的条数)


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: