oracle分頁查詢資料重複問題、分頁排序陷阱
select * from (select rownum as rn, ab.* from (SELECT t.id, e.ehr_id, nh_code, name, sex, id_card, (to_number(to_char(sysdate, 'yyyy')) - to_number(to_char(birthday, 'yyyy'))) age,home_address, telephone, t.enable_flag, t.create_user, t.create_date, t.modify_date, village_id, (SELECT answer FROM ehr_s_person_answer WHERE ehr_id = e.ehr_id AND question_id = 546) lxrdh, (SELECT count(1) FROM ehr_examination_record WHERE ehr_id = e.ehr_id AND enable_flag = 1) exam_count, (SELECT count(1) FROM ehr_ht_manage_visit_record WHERE ehr_id = e.ehr_id AND enable_flag = 1) visit_count FROM ehr_info e JOIN ehr_hypertension t ON e.ehr_id = t.ehr_id AND t.enable_flag != 3 WHERE e.region_id IN (23) AND e.hosp_id = 1 order by sex desc) ab where rownum <= 50) abc where rn > 40
以上分頁查出來的資料可能會引起分頁資料重複的現象
原因:
1.因為oracle是按塊進行讀取資料的,如果資料按順序儲存,則可能使讀取出來的資料是按順序的,給使用者誤解為預設排序。事實上,oracle沒有進行任何排序操作,如果sql沒有要求排序,oracle會順序的從資料塊中讀取符合條件的資料返回到客戶端。所以在沒有使用排序sql的時候,分頁返回的資料可能是按順序的,也可能是雜亂無章的,這都取決與資料的儲存位置。在oracle分頁查詢過程中,如果資料的物理位置發生了改變,就可能會引起分頁資料重複的現象。
2.對於以上SQL指令碼,優化器採用了“SORT (ORDER BY STOPKEY)”。“SORT (ORDER BY STOPKEY)”不需要對所有資料進行排序,而是隻要找出結果集中的按特定順序的最前N條記錄,一旦找出了這N條記錄,就無需再對剩下的資料進行排序,而直接返回結果。這種演算法我們可以視為是“快速排序”演算法的變種。快速排序演算法的基本思想是:先將資料分2組集合,保證第一集合中的每個資料都大於第二個集合中每個資料,然後再按這個原則對每個集合進行遞迴分組,直到集合的單位最小。在進行“SORT (ORDER BY STOPKEY)”時,首先找出N條資料(這些資料並沒有做排序)放在第一組,保證第一組的資料都大於第二組的資料,然後只對第一組資料進行遞迴。可以看到,基於這樣的演算法基礎上,如果N的數值不同,資料的分組也不同(如N=20時,第一次分組比例為12:8,然後繼續遞迴;當N=10時,第一次分組比例為3:7 … …),這樣,在資料的排序欄位值都相等時,輸出結果的順序就會因為N值不同而不同。
解決方法:
後記:select * from (select ab.*,rownum as rn from (SELECT id, e.ehr_id, nh_code, name, sex, id_card, to_number(to_char(sysdate, 'yyyy')) - to_number(to_char(e.birthday, 'yyyy')) age, home_address, telephone, t.enable_flag, t.create_user, t.create_date, t.modify_date, village_id, (SELECT answer FROM ehr_s_person_answer WHERE ehr_id = e.ehr_id AND question_id = 546) lxrdh, (SELECT count(1) FROM ehr_examination_record WHERE ehr_id = e.ehr_id AND enable_flag = 1) exam_count, (SELECT count(1) FROM ehr_diabetesMgvisitrecord WHERE ehr_id = e.ehr_id AND enable_flag = 1) visit_count FROM ehr_info e JOIN ehr_diabetes t ON e.ehr_id = t.ehr_id WHERE t.enable_flag != 3 AND e.region_id IN (23) AND e.hosp_id = 1 order by sex desc,id ) ab) abc where rn <= 10 and rn > 1
1.後面的方法相對來說對資料庫的效能會有一些影響
2.要正確使用oracle分頁查詢,sql語句中必須有排序條件。
3.排序條件如果沒有唯一性,那麼必須在後邊跟上一個唯一性的條件,比如主鍵
解決方法必須滿足以上2、3方法!!!!!!!!!!!!