tbwshc

          小議ROWNUM

          如何使用ROWNUM是個老生常談的問題了,本來沒有打算專門強調(diào)這個問題,但是最近在看Oracle的官方PL/SQL文檔時發(fā)現(xiàn)了一個嚴重的錯誤,借這個機會還是簡單說一下。

           

           

          首先來看Oracle文檔的描述,在10.2的PL/SQL文檔中,Oracle關(guān)于PL/SQL中直接使用SELECT的查詢描述為:

          Selecting At Most One Row: SELECT INTO Statement
          If you expect a query to only return one row, you can write a regular SQL SELECT statement with an additional INTO clause specifying the PL/SQL variable to hold the result.
          If the query might return more than one row, but you do not care about tb values after the first, you can restrict any result set to a single row by comparing the ROWNUM value. If the query might return no rows at all, use an exception handler to specify any actions to take when no data is found.

          這個描述是沒有問題的,但是到了11.2中,文檔的描述變成了:

          Single-Row Result Sets
          If you expect the query to return only one row, then use the SELECT INTO statement to store values from that row in either one or more scalar variables (see "Assigning Values to Variables with the SELECT INTO Statement") or one record variable (see "SELECT INTO Statement for Assigning Row to Record Variable").
          If the query might return multiple rows, but you care about only the nth row, then restrict the result set to that row with the clause WHERE ROWNUM=n. For more information about the ROWNUM pseudocolumn, see Oracle Database SQL Language Reference.

          第一個反應(yīng)是不是我看錯了,居然可以通過WHERE ROWNUM = N來限制只返回第N條記錄 ,再仔細看了一遍,并和10g的文檔對比,發(fā)現(xiàn)11.2和10.2中的不同。于是第二個反應(yīng)是Oracle在11.2中提供了新特性,使得PL/SQL語句中直接SELECT可以通過WHERE ROWNUM來直接控制游標,于是特意在11.2上進行了測試,發(fā)現(xiàn)結(jié)果和10.2上沒有區(qū)別,ROWNUM = N是行不通的,除非N等于1。

          SQL> select * from v$version;

          BANNER
          ------------------------------------------------------------------------------
          Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
          PL/SQL Release 11.2.0.1.0 - Production
          CORE   11.2.0.1.0     Production
          TNS for 32-bit Windows: Version 11.2.0.1.0 - Production
          NLSRTL Version 11.2.0.1.0 - Production

          SQL> select * from tab;

          TNAME                         TABTYPE CLUSTERID
          ------------------------------ ------- ----------
          COMPANIES                     TABLE
          MLOG$_T_F                     TABLE
          MLOG$_T_P                     TABLE
          MV_T_ORACLE                   TABLE
          SERVICES                      TABLE
          SERVICE_RATES                 TABLE
          SERVICE_USAGE                 TABLE
          SERVICE_USERS                 TABLE
          T                             TABLE
          T_DEFER                       TABLE
          T_F                           TABLE
          T_LOAD_LOB                    TABLE
          T_P                           TABLE
          T_PART                        TABLE

          已選擇14行。

          SQL> set serverout on
          SQL> declare
           2 v_name varchar2(30);
           3 begin
           4 select tname into v_name from tab where rownum = 5;
           5 dbms_output.put_line(v_name);
           6 exception
           7 when no_data_found then
           8 dbms_output.put_line('rownum equal the num bigger than 1 is incorrect!');
           9 end;
           10 /
          rownum equal the num bigger than 1 is incorrect!

          PL/SQL過程已成功完成。

          顯然Oracle文檔這里出現(xiàn)了嚴重的錯誤,如果要使用ROWNUM來控制返回第幾行結(jié)果,那么至少需要2層嵌套查詢才可以。

          最后簡單總結(jié)一下ROWNUM,很多人都知道ROWNUM只適用于小于或小于等于,如果進行等于判斷,那么只能等于1,不能進行大于的比較。但是卻并不了解造成這種限制條件的機制是什么。

          其實ROWNUM的返回很簡單,ROWNUM總是從1開始,不管當前的記錄是否滿足查詢結(jié)果,ROWNUM返回的值都是1,如果這條記錄的值最終滿足所有的條件,那么ROWNUM會遞加,下一條記錄的ROWNUM會返回2,否則下一條記錄的ROWNUM仍然返回1。

          理解了這一點,就清楚為什么一般的ROWNUM大于某個值或等于某個不為1的值是無法返回結(jié)果的,因此對于每條記錄的ROWNUM都是1,而ROWNUM為1不滿足查詢的結(jié)果,所以下一條記錄的ROWNUM不會遞增,仍然是1,因此所有的記錄都不滿足條件。

          了解了原理,就可以很容易的寫出ROWNUM大于某值的例子:

          SQL> select * from tab where rownum = 1 or rownum > 1;

          TNAME                         TABTYPE CLUSTERID
          ------------------------------ ------- ----------
          COMPANIES                     TABLE
          MLOG$_T_F                     TABLE
          MLOG$_T_P                     TABLE
          MV_T_ORACLE                   TABLE
          SERVICES                      TABLE
          SERVICE_RATES                 TABLE
          SERVICE_USAGE                 TABLE
          SERVICE_USERS                 TABLE
          T                             TABLE
          T_DEFER                       TABLE
          T_F                           TABLE
          T_LOAD_LOB                    TABLE
          T_P                           TABLE
          T_PART                        TABLE

          posted on 2012-08-17 14:53 chen11-1 閱讀(1300) 評論(0)  編輯  收藏

          主站蜘蛛池模板: 河西区| 浦城县| 瓮安县| 涞源县| 昌平区| 兖州市| 濉溪县| 剑阁县| 太原市| 邳州市| 措勤县| 岚皋县| 新营市| 常山县| 淮安市| 隆回县| 那坡县| 米泉市| 高雄县| 罗定市| 岳西县| 行唐县| 库尔勒市| 浮梁县| 西青区| 建始县| 怀仁县| 建阳市| 台江县| 黄浦区| 宁南县| 霍州市| 龙游县| 九江市| 电白县| 双桥区| 江孜县| 九龙城区| 黑河市| 泉州市| 临邑县|