紙飛機(jī)

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            22 隨筆 :: 28 文章 :: 30 評(píng)論 :: 0 Trackbacks

          問(wèn)題:計(jì)算某個(gè)數(shù)字列的累乘積。其操作方式與“計(jì)算累計(jì)和”相似,只是使用乘法而不是加法。

          解決方案

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

          DB2和Oracle

          使用窗口函數(shù)SUM OVER,用對(duì)數(shù)相加來(lái)模擬乘法操作:

          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中,對(duì)小于等于0的值取對(duì)數(shù)是無(wú)效的。如果表中包含這樣的值,一定要避免把這些無(wú)效的值傳遞給SQL的LN函數(shù)。為了增加可讀性,該解決方案并沒(méi)有對(duì)無(wú)效值和NULL值采取防范措施,但自己編寫代碼時(shí),一定要考慮是否需要這種預(yù)防。如果一定要用到負(fù)值和0值,那么這種解決方案不合適。

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

          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

          還可以使用對(duì)數(shù)相加的方法,但這些平臺(tái)并不支持窗口函數(shù),因此用標(biāo)量子查詢?nèi)《?

          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子句方案(僅對(duì)Oracle Database 10g或更高版本可用)之外,所有解決方案都利用了乘法運(yùn)算的特性,按下列步驟用加法進(jìn)行計(jì)算:

          1.     計(jì)算各自的自然對(duì)數(shù)

          2.     計(jì)算這些對(duì)數(shù)的和

          3.     對(duì)結(jié)果進(jìn)行數(shù)學(xué)常量e的冪運(yùn)算(使用EXP函數(shù))

          當(dāng)采用這種方法時(shí),需要注意,對(duì)于0值和負(fù)值,這種方法不可行,因?yàn)槿魏涡∮诘扔?的值都超出了SQL對(duì)數(shù)的定義域。

          DB2和Oracle

          有關(guān)窗口函數(shù)SUM OVER的功能,請(qǐng)參閱“生成累計(jì)和”一節(jié)。

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

          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

          觀察一下,會(huì)發(fā)現(xiàn)SAL[1]的值為-1300。由于數(shù)字逐一連續(xù)遞增、沒(méi)有間隙,所以可以通過(guò)減1來(lái)引用前一行。RULES子句如下:

          rules (

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

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

                         end

          )

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

          MySQL、PostgreSQL和SQL Server

          有關(guān)MySQL、PostgreSQL和SQL Server解決方案所采用的子查詢方法的說(shuō)明,請(qǐng)參閱本章第7.6節(jié)。

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

          posted on 2008-05-14 21:40 紙飛機(jī) 閱讀(1423) 評(píng)論(0)  編輯  收藏 所屬分類: Database
          主站蜘蛛池模板: 武义县| 英吉沙县| 湖南省| 宣汉县| 溆浦县| 新巴尔虎右旗| 丰台区| 长岭县| 蒲江县| 西城区| 连云港市| 宁陵县| 东阿县| 贵溪市| 周至县| 玛纳斯县| 和田县| 邛崃市| 桐庐县| 潜山县| 桓台县| 南涧| 武隆县| 高州市| 嘉义市| 湘乡市| 晋中市| 麻栗坡县| 浦江县| 浦县| 西藏| 通化县| 广宁县| 祥云县| 出国| 宜兴市| 鄢陵县| 稻城县| 东方市| 秦安县| 长宁县|