紙飛機

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            22 隨筆 :: 28 文章 :: 30 評論 :: 0 Trackbacks

          問題:計算某個數字列的累乘積。其操作方式與“計算累計和”相似,只是使用乘法而不是加法。

          解決方案

          作為例子,本解決方案中都計算職員工資的累乘積。雖然工資的累乘積沒有多大用處,然而可以很容易地把該技巧用于其他更有用的領域。

          DB2和Oracle

          使用窗口函數SUM OVER,用對數相加來模擬乘法操作:

          1 select empno,ename,sal,

          2         exp(sum(ln(sal))over(order by sal,empno)) as running_prod

          3    from emp

          4   where deptno = 10

          EMPNO ENAME        SAL          RUNNING_PROD

          ----- ---------- ---- --------------------

          7934 MILLER      1300                  1300

          7782 CLARK       2450               3185000

          7839 KING        5000           15925000000

          在SQL中,對小于等于0的值取對數是無效的。如果表中包含這樣的值,一定要避免把這些無效的值傳遞給SQL的LN函數。為了增加可讀性,該解決方案并沒有對無效值和NULL值采取防范措施,但自己編寫代碼時,一定要考慮是否需要這種預防。如果一定要用到負值和0值,那么這種解決方案不合適。

          Oracle獨有的另一種解決方案是使用Oracle Database 10g新引入的MODEL子句。在下面的例子中,每個SAL都是負數,這表明累乘積允許出現負值:

          1 select empno, ename, sal, tmp as running_prod

          2    from (

          3 select empno,ename,-sal as sal

          4    from emp

          5   where deptno=10

          6         )

          7   model

          8     dimension by(row_number()over(order by sal desc) rn )

          9     measures(sal, 0 tmp, empno, ename)

          10    rules (

          11      tmp[any] = case when sal[cv()-1] is null then sal[cv()]

          12                      else tmp[cv()-1]*sal[cv()]

          13                 end

          14    )

          EMPNO ENAME        SAL          RUNNING_PROD

          ----- ---------- ---- --------------------

          7934 MILLER      -1300                -1300

          7782 CLARK       -2450              3185000

          7839 KING        -5000         -15925000000

          MySQL、PostgreSQL和SQL Server

          還可以使用對數相加的方法,但這些平臺并不支持窗口函數,因此用標量子查詢取而代之:

          1 select e.empno,e.ename,e.sal,

          2         (select exp(sum(ln(d.sal)))

          3            from emp d

          4           where d.empno <= e.empno

          5             and e.deptno=d.deptno) as running_prod

          6   from emp e

          7   where e.deptno=10

          EMPNO ENAME        SAL          RUNNING_PROD

          ----- ---------- ---- --------------------

          7782 CLARK       2450                  2450

          7839 KING        5000              12250000

          7934 MILLER      1300           15925000000

          SQL Server用戶使用LOG代替LN。

          討論

          除了MODEL子句方案(僅對Oracle Database 10g或更高版本可用)之外,所有解決方案都利用了乘法運算的特性,按下列步驟用加法進行計算:

          1.     計算各自的自然對數

          2.     計算這些對數的和

          3.     對結果進行數學常量e的冪運算(使用EXP函數)

          當采用這種方法時,需要注意,對于0值和負值,這種方法不可行,因為任何小于等于0的值都超出了SQL對數的定義域。

          DB2和Oracle

          有關窗口函數SUM OVER的功能,請參閱“生成累計和”一節。

          對于Oracle Database 10g或更高版本,可以使用MODEL子句生成累乘積。同時使用MODEL子句及窗口函數ROW_NUMBER,很容易就能訪問前面的行。可以像訪問數組一樣訪問MEASURES列表中的每一項。然后,可以使用DIMENSIONS列表中的項(由ROW_NUMBER返回的值,別名RN)搜索該數組:

          select empno, ename, sal, tmp as running_prod,rn

             from (

          select empno,ename,-sal as sal

             from emp

          where deptno=10

                  )

          model

              dimension by(row_number()over(order by sal desc) rn )

              measures(sal, 0 tmp, empno, ename)

             rules ()

          EMPNO ENAME              SAL RUNNING_PROD          RN

          ----- ---------- ---------- ------------ ----------

          7934 MILLER           -1300             0           1

          7782 CLARK            -2450             0           2

          7839 KING             -5000             0           3

          觀察一下,會發現SAL[1]的值為-1300。由于數字逐一連續遞增、沒有間隙,所以可以通過減1來引用前一行。RULES子句如下:

          rules (

              tmp[any] = case when   sal[cv()-1] is null then sal[cv()]

                              else tmp[cv()-1]*sal[cv()]

                         end

          )

          它使用內置操作符ANY處理每一行,而并未進行硬編碼。這個例子中ANY的值分別為1、2和3。把TMP[n]初始化為0。通過計算相應SAL行的當前值(函數CV返回當前值),可以給TMP[n]指定一個值。把TMP[1]初始化為0,把SAL[1]初始化為-1300。SAL[0]沒有值,所以把TMP[1]設置為SAL[1]。在設置了TMP[1]之后,下一行就是TMP[2]。計算第一個SAL[1](由于ANY的當前值是2,因此SAL[CV()-1]的值是SAL[1])。SAL[1]不為空,而且等于-1300,因此把TMP[2]設置為TMP[1]和SAL[2]的乘積。所有行都進行上述操作。

          MySQL、PostgreSQL和SQL Server

          有關MySQL、PostgreSQL和SQL Server解決方案所采用的子查詢方法的說明,請參閱本章第7.6節。

          要注意,基于子查詢解決方案的輸出與Oracle和DB2解決方案的輸出有少許差別,其原因來自EMPNO比較(它們按不同的順序計算累乘積)。與累計和一樣,其總數也是由標量子查詢的謂詞驅動的;在該解決方案中,行是按EMPNO排序的,而對于Oracle/DB2 解決方案,行是按SAL排序的。

          posted on 2008-05-14 21:40 紙飛機 閱讀(1423) 評論(0)  編輯  收藏 所屬分類: Database
          主站蜘蛛池模板: 乳源| 元江| 望都县| 夏邑县| 昌平区| 通城县| 通道| 昌黎县| 莆田市| 会理县| 宜兰县| 宽城| 林周县| 南木林县| 怀仁县| 宜春市| 德化县| 达拉特旗| 横峰县| 高邮市| 运城市| 道孚县| 富锦市| 南宫市| 华坪县| 星子县| 天气| 上林县| 九台市| 临高县| 白城市| 郧西县| 涟水县| 神农架林区| 老河口市| 神木县| 阿合奇县| 宜丰县| 福清市| 遂平县| 原阳县|