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
表時,用的是該表的主鍵或者唯一鍵字段(否則將改變結果集的條數)