﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-纸飞机-文章分类-Database</title><link>http://www.blogjava.net/jxhkwhy/category/31489.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 14 May 2008 19:24:51 GMT</lastBuildDate><pubDate>Wed, 14 May 2008 19:24:51 GMT</pubDate><ttl>60</ttl><item><title>SQL语句-更改累计和中的值</title><link>http://www.blogjava.net/jxhkwhy/articles/200496.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:51:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200496.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200496.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200496.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200496.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200496.html</trackback:ping><description><![CDATA[<p>问题:根据另一列中的值修改累计和中的值。假设一个场景，要显示信用卡账号的事务处理历史以及每次事务处理洲改累计和中的值。假设一个场景，要显示信用卡账号的事务处理历史以及每次事务处理之后的当前余额。在这个例子中，将使用下面给出的视图V：</p>
<p>create view V (id,amt,trx)</p>
<p>as</p>
<p>select 1, 100, 'PR' from t1 union all</p>
<p>select 2, 100, 'PR' from t1 union all</p>
<p>select 3, 50,&nbsp;&nbsp; 'PY' from t1 union all</p>
<p>select 4, 100, 'PR' from t1 union all</p>
<p>select 5, 200, 'PY' from t1 union all</p>
<p>select 6, 50,&nbsp;&nbsp; 'PY' from t1 </p>
<p>select * from V </p>
<p>ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AMT TR</p>
<p>-- ---------- --</p>
<p>1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100 PR</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100 PR</p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50 PY</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100 PR</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 200 PY</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50 PY</p>
<p>ID列唯一标识每次事务处理。AMT列表示每次事务处理（取款或存款）涉及的金额。TRX列定义了事务处理的类型；取款是&#8220;PY&#8221;，存款是&#8220;PR&#8221;。如果TRX值是PY，则想要从累计和中减去AMT值代表的金额；如果TRX值是PR，则想要给累计和加上AMT值代表的金额。最后应该返回如下结果集：</p>
<p>TRX_TYPE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AMT&nbsp;&nbsp;&nbsp;&nbsp; BALANCE</p>
<p>-------- ---------- ----------</p>
<p>PURCHASE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100</p>
<p>PURCHASE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 200</p>
<p>PAYMENT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 150</p>
<p>PURCHASE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 250</p>
<p>PAYMENT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 200&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50</p>
<p>PAYMENT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0</p>
<p>解决方案</p>
<p>DB2和Oracle </p>
<p>使用窗口函数SUM OVER创建累计和，并使用CASE表达式判断事务处理的类型：</p>
<p>1&nbsp;&nbsp; select case when trx = 'PY'</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; then 'PAYMENT'</p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else 'PURCHASE'</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end trx_type,</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; amt,</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(</p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case when trx = 'PY'</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; then -amt else amt</p>
<p>9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end</p>
<p>10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) over (order by id,amt) as balance</p>
<p>11&nbsp;&nbsp;&nbsp;&nbsp; from V</p>
<p>MySQL、PostgreSQL和SQL Server</p>
<p>使用标量子查询创建累计和，并使用CASE表达式判断事务处理的类型：</p>
<p>1&nbsp;&nbsp; select case when v1.trx = 'PY'</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; then 'PAYMENT'</p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else 'PURCHASE'</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end as trx_type,</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v1.amt,</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (select sum(</p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case when v2.trx = 'PY'</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; then -v2.amt else v2.amt</p>
<p>9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end</p>
<p>10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from V v2</p>
<p>12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where v2.id &lt;= v1.id) as balance</p>
<p>13&nbsp;&nbsp;&nbsp;&nbsp; from V v1</p>
<p>讨论</p>
<p>CASE表达式判断是该给累计和加上当前的AMT值还是从中减去当前的AMT值 。如果事务处理是取款，则把AMT更改为负值，这样就减少了累计和。CASE表达式的结果如下所示：</p>
<p>select case when trx = 'PY'</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; then 'PAYMENT'</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else 'PURCHASE'</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end trx_type,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case when trx = 'PY'</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; then -amt else amt</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end as amt</p>
<p>&nbsp;&nbsp; from V </p>
<p>TRX_TYPE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AMT</p>
<p>-------- ---------</p>
<p>PURCHASE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100</p>
<p>PURCHASE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100</p>
<p>PAYMENT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -50</p>
<p>PURCHASE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100</p>
<p>PAYMENT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -200</p>
<p>PAYMENT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -50</p>
<p>在确定了事务处理类型之后，就可以从累计和中加上或者减去AMT值。有关窗口函数SUM OVER或标量子查询如何创建累计和的说明，请参阅&#8220;计算累计和&#8221;。</p>
<img src ="http://www.blogjava.net/jxhkwhy/aggbug/200496.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:51 <a href="http://www.blogjava.net/jxhkwhy/articles/200496.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-把字母数字串转换为数值</title><link>http://www.blogjava.net/jxhkwhy/articles/200495.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:50:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200495.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200495.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200495.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200495.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200495.html</trackback:ping><description><![CDATA[<p>问题:对于字母数字的数据，只返回数字值。从字符串&#8220;paul123f321&#8221;中返回123321。</p>
<p>解决方案</p>
<p>DB2</p>
<p>使用函数TRANSLATE和REPLACE，从字母数字串中提取数字字符：</p>
<p>1 select cast(</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replace(</p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translate( 'paul123f321',</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repeat('#',26),</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'abcdefghijklmnopqrstuvwxyz'),'#','') </p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; as integer ) as num</p>
<p>7&nbsp;&nbsp;&nbsp; from t1</p>
<p>Oracle和PostgreSQL</p>
<p>使用函数TRANSLATE和REPLACE，可以从包含字母数字的字符串中提取数字字符：</p>
<p>1 select cast(</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replace(</p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translate( 'paul123f321',</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'abcdefghijklmnopqrstuvwxyz',</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpad('#',26,'#')),'#','') </p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; as integer ) as num</p>
<p>7&nbsp;&nbsp; from t1&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p>MySQL和SQL Server</p>
<p>到本书编写时为止，这两个供应商都不支持TRANSLATE函数，因此这里不能给出解决方案了。</p>
<p>讨论</p>
<p>两种解决方案的唯一差别是语法，DB2使用函数REPEAT代替RPAD，而且TRANSLATE参数列表的顺序也不同。以下的解释采用了Oracle/PostgreSQL解决方案， DB2也类似。如果从里向外运行该查询（仅仅从TRANSLATE开始），就会发现这非常简单。首先，TRANSLATE把非数字字符转换为&#8220;#&#8221;：</p>
<p>select translate( 'paul123f321',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'abcdefghijklmnopqrstuvwxyz',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpad('#',26,'#')) as num</p>
<p>&nbsp;&nbsp; from t1 </p>
<p>NUM</p>
<p>-----------</p>
<p>####123#321</p>
<p>由于现在所有非数字字符都用&#8220;#&#8221;表示了，因此只需使用REPLACE去掉它们，然后把结果转换为数值。这个特殊的例子尤其简单，因为字符串中只有字母和数字。如果还有其他字符，那么用另一种方法会更容易：不是找出非数字字符并去掉它们，而是找出所有数字字符，并去掉不属于这些字符范围的其他字符。下面的例子会有助于理解这种技巧：</p>
<p>select replace(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translate('paul123f321',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replace(translate( 'paul123f321',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '0123456789',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpad('#',10,'#')),'#',''),</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpad('#',length('paul123f321'),'#')),'#','') as num</p>
<p>&nbsp;&nbsp; from t1 </p>
<p>NUM</p>
<p>------</p>
<p>123321</p>
<p>较之原始方案，该解决方案看起来有点儿费解，但如果把它分解开来就容易理解了。观察一下最内层的TRANSLATE调用： </p>
<p>select translate( 'paul123f321',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '0123456789',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpad('#',10,'#'))</p>
<p>&nbsp;&nbsp; from t1 </p>
<p>TRANSLATE('</p>
<p>-----------</p>
<p>paul###f###</p>
<p>与原来方案不同的是，它没有用&#8220;#&#8221;字符替换每个非数字字符，而是用&#8220;#&#8221;字符替换所有数字字符。接下来，去掉所有&#8220;#&#8221;，这样，只剩下非数字字符：</p>
<p>select replace(translate( 'paul123f321',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '0123456789',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpad('#',10,'#')),'#','')</p>
<p>&nbsp;&nbsp; from t1 </p>
<p>REPLA</p>
<p>-----</p>
<p>paulf</p>
<p>下一步，再次调用TRANSLATE，这次用&#8220;#&#8221;字符替换原始字符串中的所有非数字字符（前面查询的结果）：</p>
<p>select translate('paul123f321',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; replace(translate( 'paul123f321',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '0123456789',</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpad('#',10,'#')),'#',''),</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rpad('#',length('paul123f321'),'#'))</p>
<p>&nbsp;&nbsp; from t1 </p>
<p>TRANSLATE('</p>
<p>-----------</p>
<p>####123#321</p>
<p>到这里停一停，检验一下最外层的TRANSLATE调用。RPAD的第二个参数（DB2中REPEAT的第二个参数）是原始字符串的长度。这样做很方便，因为是没有任何字符出现的次数会比它所在的整个字符串长。现在，用&#8220;#&#8221;字符替换所有非数字字符；最后一步，使用REPLACE去掉所有&#8220;#&#8221;。至此，仅剩下数字。</p>
<img src ="http://www.blogjava.net/jxhkwhy/aggbug/200495.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:50 <a href="http://www.blogjava.net/jxhkwhy/articles/200495.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-计算不包含最大值和最小值的均值</title><link>http://www.blogjava.net/jxhkwhy/articles/200493.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:49:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200493.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200493.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200493.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200493.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200493.html</trackback:ping><description><![CDATA[<p>问题：计算平均数，但希望排除最大和最小值，以（希望能）减少数据畸偏造成的影响。例如，计算除最高和最低工资外的所有职员的平均工资。</p>
<p>解决方案</p>
<p>MySQL和PostgreSQL</p>
<p>使用子查询排除最高和最低值：</p>
<p>1&nbsp;&nbsp; select avg(sal)</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from emp</p>
<p>3&nbsp;&nbsp;&nbsp; where sal not in (</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (select min(sal) from emp),</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (select max(sal) from emp)</p>
<p>6&nbsp;&nbsp;&nbsp; )</p>
<p>DB2、Oracle和SQL Server</p>
<p>使用内联视图及窗口函数MAX OVER和MIN OVER，生成一个结果集，可以很容易地从中剔除最大和最小值：</p>
<p>1&nbsp;&nbsp; select avg(sal)</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from (</p>
<p>3&nbsp;&nbsp; select sal, min(sal)over() min_sal, max(sal)over() max_sal</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp; from emp</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x</p>
<p>6&nbsp;&nbsp;&nbsp; where sal not in (min_sal,max_sal) </p>
<p>讨论</p>
<p>MySQL和PostgreSQL</p>
<p>子查询返回表中的最高工资和最低工资。针对返回的值使用NOT IN，就可以从平均值中排除最高工资和最低工资。记住，如果存在重复（多个职员都是最高或最低工资），那么他们都会被排除在平均值之外。如果只想排除一个最高和最低值，只需从SUM中减去它们，再做除法：</p>
<p>select (sum(sal)-min(sal)-max(sal))/(count(*)-2) </p>
<p>&nbsp;&nbsp; from emp</p>
<p>DB2、Oracle和SQL Server</p>
<p>内联视图X将返回所有工资，其中包括最高工资和最低工资：</p>
<p>select sal, min(sal)over() min_sal, max(sal)over() max_sal</p>
<p>&nbsp;&nbsp; from emp </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL&nbsp;&nbsp;&nbsp; MIN_SAL&nbsp;&nbsp;&nbsp; MAX_SAL</p>
<p>--------- --------- ---------</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1600&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2850&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1500&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 950&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000</p>
<p>从每一行都可以访问最高工资和最低工资，因此，要找出哪些工资是最高工资的和/或最低工资的非常简单。外层查询会对内联视图X返回的行作筛选，这样，所有与MIN_SAL和MAX_SALAN相匹配的行都会从平均值中排除掉。</p>
<img src ="http://www.blogjava.net/jxhkwhy/aggbug/200493.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:49 <a href="http://www.blogjava.net/jxhkwhy/articles/200493.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-对可空列作聚集</title><link>http://www.blogjava.net/jxhkwhy/articles/200491.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:47:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200491.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200491.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200491.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200491.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200491.html</trackback:ping><description><![CDATA[<p>问题:对某列进行聚集运算，但该列的值可为空，由于函数会忽略NULL值，能否保持聚集运算的准确性令人担忧。例如，想要求DEPTNO 30中职员的平均佣金，但有些职员不挣佣金（这些职员的COMM值为NULL）。由于聚集运算会忽略NULL，因此输出结果的准确性没有保障。在进行聚集运算时有时可能需要以某种方式将NULL值包括进来。</p>
<p>解决方案</p>
<p>使用COALESCE函数把NULL转换为0，这样在进行聚集时可以把它们包括进来：</p>
<p>1&nbsp;&nbsp; select avg(coalesce(comm,0)) as avg_comm</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from emp</p>
<p>3&nbsp;&nbsp;&nbsp; where deptno=30</p>
<p>讨论</p>
<p>请务必记住，在使用聚集函数时会忽略NULL。不使用COALESCE函数时该解决方案的输出如下：</p>
<p>select avg(comm) </p>
<p>&nbsp;&nbsp; from emp </p>
<p>where deptno=30 </p>
<p>AVG(COMM)</p>
<p>---------</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 550</p>
<p>该查询表明，DEPTNO 30的平均佣金是550，快速检查这些行如下：</p>
<p>select ename, comm </p>
<p>&nbsp;&nbsp; from emp </p>
<p>where deptno=30 </p>
<p>order by comm desc </p>
<p>ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; COMM</p>
<p>---------- ---------</p>
<p>BLAKE</p>
<p>JAMES</p>
<p>MARTIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1400</p>
<p>WARD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 500</p>
<p>ALLEN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 300</p>
<p>TURNER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0</p>
<p>这表明六个职员中只有四个职员挣得佣金。DEPTNO 30中所有佣金的总和是2200，其平均值应该是2200/6，而不是2200/4。如果不用COALESCE函数，回答的是问题&#8220;DEPTNO 30中挣得佣金的职员的平均佣金是多少？&#8221;，而不是&#8220;DEPTNO 30中所有职员的平均佣金是多少？&#8221;。使用聚集时记住要相应处理NULL值。</p>
<img src ="http://www.blogjava.net/jxhkwhy/aggbug/200491.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:47 <a href="http://www.blogjava.net/jxhkwhy/articles/200491.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-求总和的百分比</title><link>http://www.blogjava.net/jxhkwhy/articles/200490.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:46:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200490.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200490.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200490.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200490.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200490.html</trackback:ping><description><![CDATA[<p>问题:求特定列中的值占总和的百分比。例如，确定所有DEPTNO 10工资占总工资的百分比（DEPTNO 10的工资在总工资中的百分比数）。</p>
<p>解决方案</p>
<p>总的来说，在SQL中计算占总数的百分比跟书面计算一样：先除后乘。这个例子要计算表EMP中DEPTNO 10工资所占的百分比。首先，算出DEPTNO 10的工资，然后除以表中的工资总和，最后一步，乘以100，则返回一个表示百分比的值。</p>
<p>MySQL和PostgreSQL </p>
<p>DEPTNO 10的工资总和除以所有工资总和：</p>
<p>1 select (sum(</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case when deptno = 10 then sal end)/sum(sal)</p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )*100 as pct</p>
<p>4&nbsp;&nbsp;&nbsp; from emp</p>
<p>DB2、Oracle和SQL Server</p>
<p>使用内联视图及窗口函数SUM OVER，计算出所有工资总和以及DEPTNO 10的工资和。然后，在外层查询中进行除法和乘法操作：</p>
<p>1&nbsp;&nbsp; select distinct (d10/total)*100 as pct</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from (</p>
<p>3&nbsp;&nbsp; select deptno,</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over() total,</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over(partition by deptno) d10</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp; from emp</p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x</p>
<p>8&nbsp;&nbsp;&nbsp; where deptno=10 </p>
<p>讨论</p>
<p>MySQL和PostgreSQL </p>
<p>用CASE语句能够轻松地得到DEPTNO 10的工资。然后将它们加起来，并除以所有工资总和。由于聚集时会忽略NULL值，所以CASE语句中不必加入ELSE子句。如果想看到确切的被除数和除数，则可以执行不做除法的查询：</p>
<p>select sum(case when deptno = 10 then sal end) as d10,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)</p>
<p>&nbsp;&nbsp; from emp </p>
<p>D10&nbsp;&nbsp; SUM(SAL)</p>
<p>---- ---------</p>
<p>8750&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025</p>
<p>依定义SAL的方式不同，在进行除法操作时可能需要做显式类型转换。例如，在DB2、SQL Server和PostgreSQL中，如果SAL定义为整数，则可以把它转换为小数，以便得到正确答案，如下所示：</p>
<p>select (cast( </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(case when deptno = 10 then sal end) </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; as decimal)/sum(sal)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )*100 as pct</p>
<p>&nbsp;&nbsp; from emp</p>
<p>DB2、Oracle和SQL Server</p>
<p>除传统解决方案外，该方案使用窗口函数计算相对于总数的百分数。对于DB2和SQL Server，如果SAL定义为整数类型，则在除法操作之前，需要进行类型转换：</p>
<p>select distinct </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cast(d10 as decimal)/total*100 as pct</p>
<p>&nbsp;&nbsp; from (</p>
<p>select deptno,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over() total,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over(partition by deptno) d10</p>
<p>&nbsp;&nbsp; from emp</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x</p>
<p>where deptno=10</p>
<p>必须记住，窗口函数在WHERE子句后执行。因此不能把针对DEPTNO的筛选放在内联视图X中。分别考虑一下内联视图X中包含及不包含DEPTNO筛选的结果。首先，看一下不包含DEPTNO筛选的结果：</p>
<p>select deptno,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over() total,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over(partition by deptno) d10</p>
<p>&nbsp;&nbsp; from emp </p>
<p>DEP </p>
<p>------- --------- ---------</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10875</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10875</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10875</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10875</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10875</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9400</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9400</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9400</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9400</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9400</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9400 </p>
<p>包含DEPTNO筛选的结果：</p>
<p>select deptno,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over() total,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over(partition by deptno) d10</p>
<p>&nbsp;&nbsp; from emp</p>
<p>where deptno=10 </p>
<p>DEPTNO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TOTAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D10</p>
<p>------ --------- ---------</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8750</p>
<p>由于窗口函数在WHERE子句后执行，因此TOTAL的值仅表示DEPTNO 10的工资之和，而实际上需要用TOTAL表示所有工资的总和。这就是必须把针对DEPTNO的筛选放在内联视图X外面的原因。</p>
<img src ="http://www.blogjava.net/jxhkwhy/aggbug/200490.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:46 <a href="http://www.blogjava.net/jxhkwhy/articles/200490.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-计算中间值</title><link>http://www.blogjava.net/jxhkwhy/articles/200489.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:45:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200489.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200489.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200489.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200489.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200489.html</trackback:ping><description><![CDATA[<p>问题:计算一列数字值的中间值（中间值就是一组有序元素中间成员的值）。例如，查找DEPTNO 20中工资的中间数。如下列工资：</p>
<p>select sal </p>
<p>&nbsp;&nbsp; from emp </p>
<p>where deptno = 20 </p>
<p>order by sal </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL</p>
<p>----------</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1100</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000</p>
<p>中间数为2975。</p>
<p>解决方案</p>
<p>除了Oracle解决方案（用函数计算中间数）之外，其他所有解决方案都是以Rozenshtein、Abramovich和Birger在Optimizing Transact-SQL: Advanced Programming Techniques (SQL Forum Press, 1997)中描述的方法为基础的。与传统的自联接相比，窗口函数的引入，使解决方案更为有效。</p>
<p>DB2 </p>
<p>使用窗口函数COUNT(*) OVER和ROW_NUMBER，查找中间数：</p>
<p>1&nbsp;&nbsp; select avg(sal)</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from (</p>
<p>3&nbsp;&nbsp; select sal,</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count(*) over() total,</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cast(count(*) over() as decimal)/2 mid,</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ceil(cast(count(*) over() as decimal)/2) next,</p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; row_number() over (order by sal) rn</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp; from emp</p>
<p>9&nbsp;&nbsp;&nbsp; where deptno = 20</p>
<p>10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x</p>
<p>11&nbsp;&nbsp;&nbsp; where ( mod(total,2) = 0</p>
<p>12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and rn in ( mid, mid+1 )</p>
<p>13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or ( mod(total,2) = 1</p>
<p>15&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and rn = next</p>
<p>16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>MySQL和PostgreSQL</p>
<p>使用自联接查找中间数：</p>
<p>1&nbsp;&nbsp; select avg(sal)</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from (</p>
<p>3&nbsp;&nbsp; select e.sal</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp; from emp e, emp d</p>
<p>5&nbsp;&nbsp;&nbsp; where e.deptno = d.deptno</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and e.deptno = 20</p>
<p>7&nbsp;&nbsp;&nbsp; group by e.sal</p>
<p>8&nbsp;&nbsp; having sum(case when e.sal = d.sal then 1 else 0 end) </p>
<p>9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;= abs(sum(sign(e.sal - d.sal)))</p>
<p>10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>Oracle</p>
<p>使用函数MEDIAN（Oracle Database 10g）或PERCENTILE_CONT（Oracle9i Database）：</p>
<p>1 select median (sal) </p>
<p>2&nbsp;&nbsp;&nbsp; from emp </p>
<p>3&nbsp;&nbsp; where deptno=20 </p>
<p>1 select percentile_cont(0.5)</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; within group(order by sal) </p>
<p>3&nbsp;&nbsp;&nbsp; from emp </p>
<p>4&nbsp;&nbsp; where deptno=20</p>
<p>对于Oracle8i Database，使用DB2解决方案。对于Oracle8i Database之前的版本，可以采用PostgreSQL/MySQL解决方案。</p>
<p>SQL Server</p>
<p>使用窗口函数COUNT(*) OVER和ROW_NUMBER，可得到中间数：</p>
<p>1&nbsp;&nbsp; select avg(sal)</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from (</p>
<p>3&nbsp;&nbsp; select sal,</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count(*)over() total,</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cast(count(*)over() as decimal)/2 mid,</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ceiling(cast(count(*)over() as decimal)/2) next,</p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; row_number()over(order by sal) rn</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp; from emp</p>
<p>9&nbsp;&nbsp;&nbsp; where deptno = 20</p>
<p>10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x</p>
<p>11&nbsp;&nbsp;&nbsp; where ( total%2 = 0</p>
<p>12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and rn in ( mid, mid+1 )</p>
<p>13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or ( total%2 = 1</p>
<p>15&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and rn = next</p>
<p>16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>讨论</p>
<p>DB2和SQL Server</p>
<p>DB2和SQL Server 解决方案的唯一差别是语法的稍许不同：SQL Server用&#8220;%&#8221;求模，而DB2使用MOD函数；其余的都相同。内联视图X返回三个不同的计数值，TOTAL、MID和NEXT，还用到由ROW_NUMBER生成的RN。这些附加列有助于求解中间数。检验内联视图X的结果集，就会看到这些列表示的意义：</p>
<p>select sal,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count(*)over() total,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cast(count(*)over() as decimal)/2 mid,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ceil(cast(count(*)over() as decimal)/2) next,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; row_number()over(order by sal) rn</p>
<p>&nbsp;&nbsp; from emp</p>
<p>where deptno = 20 </p>
<p>SAL TOTAL&nbsp;&nbsp; MID NEXT&nbsp;&nbsp;&nbsp; RN</p>
<p>---- ----- ---- ---- ----</p>
<p>800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp;&nbsp; 2.5&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 1</p>
<p>1100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp;&nbsp; 2.5&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 2</p>
<p>2975&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp;&nbsp; 2.5&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 3</p>
<p>3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp;&nbsp; 2.5&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 4</p>
<p>3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp;&nbsp; 2.5&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 5</p>
<p>要得到中间数，一定要把SAL值由低到高排序。由于DEPTNO 20中的职员数是奇数，因此它的中间数就是其RN与NEXT相等的SAL（即大于职员总数除以2的最小整数）。</p>
<p>如果结果集返回奇数行，WHERE子句的第一部分（第11～13行）条件不满足。如果能够确定结果集是奇数行，则可以简化为：</p>
<p>select avg(sal)</p>
<p>&nbsp;&nbsp; from (</p>
<p>select sal,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count(*)over() total,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ceil(cast(count(*)over() as decimal)/2) next,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; row_number()over(order by sal) rn</p>
<p>&nbsp;&nbsp; from emp</p>
<p>where deptno = 20</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x</p>
<p>where rn = next</p>
<p>令人遗憾的是，如果结果集包含偶数行，上述简化的解决方案就行不通。在最初的解决方案中，采用MID列中的值处理偶数行。想想DEPTNO 30的内联视图X的结果会怎样，该部门有6名职员：</p>
<p>select sal,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count(*)over() total,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cast(count(*)over() as decimal)/2 mid,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ceil(cast(count(*)over() as decimal)/2) next,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; row_number()over(order by sal) rn</p>
<p>&nbsp;&nbsp; from emp</p>
<p>where deptno = 30 </p>
<p>SAL TOTAL&nbsp;&nbsp; MID NEXT&nbsp;&nbsp;&nbsp; RN</p>
<p>---- ----- ---- ---- ----</p>
<p>950&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 1</p>
<p>1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 2</p>
<p>1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 3</p>
<p>1500&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 4</p>
<p>1600&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 5</p>
<p>2850&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp; 6</p>
<p>由于返回了偶数行，则采用下述方式计算中间数：计算RN分别等于MID和MID + 1两行的平均数。</p>
<p>MySQL和PostgreSQL</p>
<p>根据第一个自联接表EMP计算中间数，而该表返回了所有工资的笛卡儿积（GROUP BY&nbsp;&nbsp;&nbsp; E.SAL会去掉重复值）。HAVING子句使用函数SUM计算E.SAL等于D.SAL的次数；如果这个值大于等于E.SAL且大于D.SAL次数，那么该行就是中间数。在SELECT列表中加入SUM就可以观察到这种情况：</p>
<p>select e.sal,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(case when e.sal=d.sal </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; then 1 else 0 end) as cnt1,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; abs(sum(sign(e.sal - d.sal))) as cnt2</p>
<p>&nbsp;&nbsp; from emp e, emp d</p>
<p>where e.deptno = d.deptno</p>
<p>&nbsp;&nbsp;&nbsp; and e.deptno = 20</p>
<p>group by e.sal </p>
<p>SAL CNT1 CNT2</p>
<p>---- ---- ----</p>
<p>800&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; 4</p>
<p>1100&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; 2</p>
<p>2975&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp; 0</p>
<p>3000&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp; 6</p>
<p>Oracle</p>
<p>在Oracle Database 10g或Oracle9i Database中，可以使用Oracle提供的函数计算中间数；对于Oracle8i Database，可以采用DB2解决方案；其他版本必须采用PostgreSQL解决方案。显然可以用MEDIAN函数计算中间值，用PERCENTILE_CONT函数也可以计算中间值就不那么显而易见了。传递给PERCENTILE_CONT的值0.5是一个百分比值。子句WITHIN GROUP (ORDER BY SAL)确定PERCENTILE_CONT要搜索哪些有序行（记住，中间值就是一组已排序值的中间值）。返回的值就是搜索的有序行中符合给定百分比（在这个例子中是0.5，因为其两个边界值分别为0和1）的值。</p>
<img src ="http://www.blogjava.net/jxhkwhy/aggbug/200489.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:45 <a href="http://www.blogjava.net/jxhkwhy/articles/200489.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-计算模式</title><link>http://www.blogjava.net/jxhkwhy/articles/200486.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:44:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200486.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200486.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200486.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200486.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200486.html</trackback:ping><description><![CDATA[<p>问题:查找某个列中值的模式（数学中的模式概念就是对于给定的数据集出现最频繁的元素）。例如，查找DEPTNO 20中工资的模式。例如下列工资：</p>
<p>select sal </p>
<p>&nbsp;&nbsp; from emp </p>
<p>where deptno = 20 </p>
<p>order by sal </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL</p>
<p>----------</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1100</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000</p>
<p>the mode is 3000.</p>
<p>解决方案</p>
<p>DB2和SQL Server</p>
<p>使用窗口函数DENSE_RANK，把工资重复出现次数分等级，以便提取模式：</p>
<p>1&nbsp;&nbsp; select sal</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from (</p>
<p>3&nbsp;&nbsp; select sal,</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dense_rank()over(order by cnt desc) as rnk</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp; from (</p>
<p>6&nbsp;&nbsp; select sal, count(*) as cnt</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp; from emp</p>
<p>9&nbsp;&nbsp;&nbsp; where deptno = 20</p>
<p>10&nbsp;&nbsp;&nbsp; group by sal</p>
<p>11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x</p>
<p>12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) y</p>
<p>13&nbsp;&nbsp;&nbsp; where rnk = 1</p>
<p>Oracle</p>
<p>在Oracle8i Database中，可以使用DB2给出的解决方案。对于Oracle9i及更高版本，可以用聚集函数MAX的KEEP扩展，以得到SAL模式。特别要注意的是，如果存在绑带，也即多个行都是模式，则采用KEEP方案仅能得到一个，即其中工资最高的那个。如果想要看所有模式（如果存在多个模式），则必须修改该方案，或者简单地使用前面介绍的DB2解决方案。在这个例子中，由于3000是DEPTNO 20中SAL的模式，而且它也是最高的SAL，因此以下方案就可以了：</p>
<p>1&nbsp;&nbsp; select max(sal)</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; keep(dense_rank first order by cnt desc) sal</p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp; from (</p>
<p>4&nbsp;&nbsp; select sal, count(*) cnt</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp; from emp</p>
<p>6&nbsp;&nbsp;&nbsp; where deptno=20</p>
<p>7&nbsp;&nbsp;&nbsp; group by sal</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>MySQL和PostgreSQL</p>
<p>使用子查询查找模式：</p>
<p>1&nbsp;&nbsp; select sal </p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp; from emp </p>
<p>3&nbsp;&nbsp;&nbsp; where deptno = 20 </p>
<p>4&nbsp;&nbsp;&nbsp; group by sal </p>
<p>5&nbsp;&nbsp; having count(*) &gt;= all ( select count(*)&nbsp;&nbsp;</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from emp </p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where deptno = 20 </p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; group by sal )</p>
<p>讨论</p>
<p>DB2和SQL Server</p>
<p>内联视图X将返回每个SAL及它出现的次数。内联视图Y使用窗口函数DENSE_RANK（它允许绑带）给结果排序。结果按每个SAL出现的次数分等级，如下所示：</p>
<p>1 select sal,</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dense_rank()over(order by cnt desc) as rnk</p>
<p>3&nbsp;&nbsp;&nbsp; from (</p>
<p>4 select sal,count(*) as cnt</p>
<p>5&nbsp;&nbsp;&nbsp; from emp</p>
<p>6&nbsp;&nbsp; where deptno = 20</p>
<p>7&nbsp;&nbsp; group by sal</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x </p>
<p>&nbsp;&nbsp; SAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RNK</p>
<p>----- ----------</p>
<p>3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1</p>
<p>&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2</p>
<p>1100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2</p>
<p>2975&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2</p>
<p>最外层的查询只简单地保留RNK为1的行。</p>
<p>Oracle</p>
<p>内联视图将返回所有SAL及其出现的次数，如下所示：</p>
<p>select sal, count(*) cnt</p>
<p>&nbsp;&nbsp; from emp</p>
<p>where deptno=20</p>
<p>group by sal </p>
<p>&nbsp;&nbsp; SAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CNT</p>
<p>----- ----------</p>
<p>&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1</p>
<p>1100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1</p>
<p>2975&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1</p>
<p>3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2</p>
<p>下一步，使用聚集函数MAX的KEEP扩展查找模式。如果仔细分析下面给出的KEEP子句，会发现它又有三个子句，即DENSE_RANK、FIRST和ORDER BY CNT DESC：</p>
<p>keep(dense_rank first order by cnt desc)</p>
<p>这种做法对求模式极其方便。KEEP子句根据内联视图返回的CNT值来确定MAX返回SAL的哪个值。按从右向左的方向将CNT递减排序，然后保留下按DENSE_RANK次序返回的所有CNT值的第一个值。查看一下内联视图的结果集，就会看到3000具有最高的CNT值 —— 2。MAX(SAL) 返回的是拥有最高CNT值的最大SAL，在本例中是3000。</p>
<p>有关Oracle中集合函数的KEEP扩展的深入讨论，请参阅第11章第11.11节。有关Oracle中集合函数的KEEP扩展的深入讨论，请参阅第11章第11.11节。</p>
<p>MySQL和PostgreSQL</p>
<p>子查询将返回每个SAL出现的次数。外层查询将返回其的出现次数大于等于子查询所返回所有计数值的SAL（换句话说，外层查询会返回DEPTNO 20中出现最多的工资）。</p>
<img src ="http://www.blogjava.net/jxhkwhy/aggbug/200486.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:44 <a href="http://www.blogjava.net/jxhkwhy/articles/200486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-计算累计差</title><link>http://www.blogjava.net/jxhkwhy/articles/200484.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:42:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200484.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200484.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200484.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200484.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200484.html</trackback:ping><description><![CDATA[<p>问题：对于数字列中的值，计算其累计差。例如，计算DEPTNO 10中工资的累计差。要返回下列结果集：</p>
<p>ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL RUNNING_DIFF</p>
<p>---------- ---------- ------------</p>
<p>MILLER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300</p>
<p>CLARK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -1150</p>
<p>KING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -6150</p>
<p>解决方案</p>
<p>DB2和Oracle </p>
<p>使用窗口函数SUM OVER创建累计差：</p>
<p>1&nbsp;&nbsp; select ename,sal,</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(case when rn = 1 then sal else -sal end) </p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; over(order by sal,empno) as running_diff&lt;&gt;5&nbsp;&nbsp; select empno,ename,sal,</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; row_number()over(order by sal,empno) as rn</p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp; from emp </p>
<p>8&nbsp;&nbsp;&nbsp; where deptno = 10</p>
<p>9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) x</p>
<p>MySQL、PostgreSQL和SQL Server</p>
<p>使用标量子查询计算累计差：</p>
<p>1 select a.empno, a.ename, a.sal,</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (select case when a.empno = min(b.empno) then sum(b.sal) </p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else sum(-b.sal) </p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from emp b</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where b.empno &lt;= a.empno</p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and b.deptno = a.deptno ) as rnk</p>
<p>8&nbsp;&nbsp;&nbsp; from emp a</p>
<p>9&nbsp;&nbsp; where a.deptno = 10</p>
<p>讨论</p>
<p>该解决方案与&#8220;生成累计和&#8221;一节介绍的解决方案大致相同。唯一的差别是：SAL除了第一个值（因为要从DEPTNO 10的SAL开始）之外，其余所有值都返回负值。</p>
<img src ="http://www.blogjava.net/jxhkwhy/aggbug/200484.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:42 <a href="http://www.blogjava.net/jxhkwhy/articles/200484.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-计算累积值</title><link>http://www.blogjava.net/jxhkwhy/articles/200483.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:40:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200483.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200483.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200483.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200483.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200483.html</trackback:ping><description><![CDATA[<p>问题：计算某个数字列的累乘积。其操作方式与&#8220;计算累计和&#8221;相似，只是使用乘法而不是加法。</p>
<p>解决方案</p>
<p>作为例子，本解决方案中都计算职员工资的累乘积。虽然工资的累乘积没有多大用处，然而可以很容易地把该技巧用于其他更有用的领域。</p>
<p>DB2和Oracle </p>
<p>使用窗口函数SUM OVER，用对数相加来模拟乘法操作： </p>
<p>1 select empno,ename,sal,</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exp(sum(ln(sal))over(order by sal,empno)) as running_prod</p>
<p>3&nbsp;&nbsp;&nbsp; from emp</p>
<p>4&nbsp;&nbsp; where deptno = 10 </p>
<p>EMPNO ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RUNNING_PROD</p>
<p>----- ---------- ---- --------------------</p>
<p>7934 MILLER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300</p>
<p>7782 CLARK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3185000</p>
<p>7839 KING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15925000000</p>
<p>在SQL中，对小于等于0的值取对数是无效的。如果表中包含这样的值，一定要避免把这些无效的值传递给SQL的LN函数。为了增加可读性，该解决方案并没有对无效值和NULL值采取防范措施，但自己编写代码时，一定要考虑是否需要这种预防。如果一定要用到负值和0值，那么这种解决方案不合适。</p>
<p>Oracle独有的另一种解决方案是使用Oracle Database 10g新引入的MODEL子句。在下面的例子中，每个SAL都是负数，这表明累乘积允许出现负值：</p>
<p>1 select empno, ename, sal, tmp as running_prod</p>
<p>2&nbsp;&nbsp;&nbsp; from (</p>
<p>3 select empno,ename,-sal as sal</p>
<p>4&nbsp;&nbsp;&nbsp; from emp</p>
<p>5&nbsp;&nbsp; where deptno=10</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>7&nbsp;&nbsp; model</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp; dimension by(row_number()over(order by sal desc) rn )</p>
<p>9&nbsp;&nbsp;&nbsp;&nbsp; measures(sal, 0 tmp, empno, ename)</p>
<p>10&nbsp;&nbsp;&nbsp; rules (</p>
<p>11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tmp[any] = case when sal[cv()-1] is null then sal[cv()] </p>
<p>12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else tmp[cv()-1]*sal[cv()] </p>
<p>13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end</p>
<p>14&nbsp;&nbsp;&nbsp; ) </p>
<p>EMPNO ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RUNNING_PROD</p>
<p>----- ---------- ---- --------------------</p>
<p>7934 MILLER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -1300</p>
<p>7782 CLARK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3185000</p>
<p>7839 KING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -15925000000</p>
<p>MySQL、PostgreSQL和SQL Server</p>
<p>还可以使用对数相加的方法，但这些平台并不支持窗口函数，因此用标量子查询取而代之： </p>
<p>1 select e.empno,e.ename,e.sal,</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (select exp(sum(ln(d.sal))) </p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from emp d </p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where d.empno &lt;= e.empno </p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and e.deptno=d.deptno) as running_prod </p>
<p>6&nbsp;&nbsp; from emp e </p>
<p>7&nbsp;&nbsp; where e.deptno=10 </p>
<p>EMPNO ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RUNNING_PROD</p>
<p>----- ---------- ---- --------------------</p>
<p>7782 CLARK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2450</p>
<p>7839 KING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12250000</p>
<p>7934 MILLER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15925000000</p>
<p>SQL Server用户使用LOG代替LN。</p>
<p>讨论</p>
<p>除了MODEL子句方案（仅对Oracle Database 10g或更高版本可用）之外，所有解决方案都利用了乘法运算的特性，按下列步骤用加法进行计算：</p>
<p>1.&nbsp;&nbsp;&nbsp;&nbsp; 计算各自的自然对数</p>
<p>2.&nbsp;&nbsp;&nbsp;&nbsp; 计算这些对数的和</p>
<p>3.&nbsp;&nbsp;&nbsp;&nbsp; 对结果进行数学常量e的幂运算（使用EXP函数）</p>
<p>当采用这种方法时，需要注意，对于0值和负值，这种方法不可行，因为任何小于等于0的值都超出了SQL对数的定义域。</p>
<p>DB2和Oracle </p>
<p>有关窗口函数SUM OVER的功能，请参阅&#8220;生成累计和&#8221;一节。</p>
<p>对于Oracle Database 10g或更高版本，可以使用MODEL子句生成累乘积。同时使用MODEL子句及窗口函数ROW_NUMBER，很容易就能访问前面的行。可以像访问数组一样访问MEASURES列表中的每一项。然后，可以使用DIMENSIONS列表中的项（由ROW_NUMBER返回的值，别名RN）搜索该数组：</p>
<p>select empno, ename, sal, tmp as running_prod,rn</p>
<p>&nbsp;&nbsp; from (</p>
<p>select empno,ename,-sal as sal</p>
<p>&nbsp;&nbsp; from emp</p>
<p>where deptno=10</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )</p>
<p>model</p>
<p>&nbsp;&nbsp;&nbsp; dimension by(row_number()over(order by sal desc) rn )</p>
<p>&nbsp;&nbsp;&nbsp; measures(sal, 0 tmp, empno, ename)</p>
<p>&nbsp;&nbsp; rules () </p>
<p>EMPNO ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL RUNNING_PROD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RN</p>
<p>----- ---------- ---------- ------------ ----------</p>
<p>7934 MILLER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1</p>
<p>7782 CLARK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2</p>
<p>7839 KING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3</p>
<p>观察一下，会发现SAL[1]的值为－1300。由于数字逐一连续递增、没有间隙，所以可以通过减1来引用前一行。RULES子句如下：</p>
<p>rules (</p>
<p>&nbsp;&nbsp;&nbsp; tmp[any] = case when&nbsp;&nbsp; sal[cv()-1] is null then sal[cv()] </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else tmp[cv()-1]*sal[cv()] </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end</p>
<p>)</p>
<p>它使用内置操作符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]的乘积。所有行都进行上述操作。</p>
<p>MySQL、PostgreSQL和SQL Server</p>
<p>有关MySQL、PostgreSQL和SQL Server解决方案所采用的子查询方法的说明，请参阅本章第7.6节。</p>
<p>要注意，基于子查询解决方案的输出与Oracle和DB2解决方案的输出有少许差别，其原因来自EMPNO比较（它们按不同的顺序计算累乘积）。与累计和一样，其总数也是由标量子查询的谓词驱动的；在该解决方案中，行是按EMPNO排序的，而对于Oracle/DB2 解决方案，行是按SAL排序的。</p>
 <img src ="http://www.blogjava.net/jxhkwhy/aggbug/200483.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:40 <a href="http://www.blogjava.net/jxhkwhy/articles/200483.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL语句-计算累加值</title><link>http://www.blogjava.net/jxhkwhy/articles/200482.html</link><dc:creator>纸飞机</dc:creator><author>纸飞机</author><pubDate>Wed, 14 May 2008 13:37:00 GMT</pubDate><guid>http://www.blogjava.net/jxhkwhy/articles/200482.html</guid><wfw:comment>http://www.blogjava.net/jxhkwhy/comments/200482.html</wfw:comment><comments>http://www.blogjava.net/jxhkwhy/articles/200482.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jxhkwhy/comments/commentRss/200482.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jxhkwhy/services/trackbacks/200482.html</trackback:ping><description><![CDATA[<p>问题：计算某个列中所有值的累计和</p>
<p>解决方案</p>
<p>下面给出了一种解决方案，它展示了如何计算所有职员工资的累计和。为增加可读性，其结果是按SAL排序的，这样就能够很容易地观察到累计和变化的过程。</p>
<p>DB2和Oracle </p>
<p>使用窗口版本的SUM函数计算累计和：</p>
<p>1 select ename, sal, </p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal) over (order by sal,empno) as running_total </p>
<p>3&nbsp;&nbsp;&nbsp; from emp </p>
<p>4&nbsp;&nbsp;&nbsp; order by 2 </p>
<p>ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL RUNNING_TOTAL</p>
<p>---------- ---------- -------------</p>
<p>SMITH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800</p>
<p>JAMES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 950&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1750</p>
<p>ADAMS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2850</p>
<p>WARD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4100</p>
<p>MARTIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5350</p>
<p>MILLER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6650</p>
<p>TURNER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1500&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8150</p>
<p>ALLEN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1600&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9750</p>
<p>CLARK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12200</p>
<p>BLAKE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2850&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15050</p>
<p>JONES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 18025</p>
<p>SCOTT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 21025</p>
<p>FORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24025</p>
<p>KING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025</p>
<p>MySQL、PostgreSQL和SQL Server</p>
<p>使用标量子查询计算累计和（由于不使用SUM OVER这类窗口函数，因此就不能像在DB2和Oracle解决方案中那样容易地按SAL给结果排序）。不管怎么说，累计和是正确的（最终结果与上一节相同），但由于没有进行排序，其中间值有所不同：</p>
<p>1 select e.ename, e.sal,</p>
<p>2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (select sum(d.sal) from emp d </p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where d.empno &lt;= e.empno) as running_total</p>
<p>4&nbsp;&nbsp;&nbsp; from emp e</p>
<p>5&nbsp;&nbsp; order by 3 </p>
<p>ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL RUNNING_TOTAL</p>
<p>---------- ---------- -------------</p>
<p>SMITH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800</p>
<p>ALLEN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1600&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2400</p>
<p>WARD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3650</p>
<p>JONES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6625</p>
<p>MARTIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7875</p>
<p>BLAKE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2850&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10725</p>
<p>CLARK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 13175</p>
<p>SCOTT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 16175</p>
<p>KING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 21175</p>
<p>TURNER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1500&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 22675</p>
<p>ADAMS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 23775</p>
<p>JAMES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 950&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24725</p>
<p>FORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 27725</p>
<p>MILLER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025</p>
<p>讨论</p>
<p>生成累计和是因使用新的ANSI窗口函数而得以简化的任务之一。对于不支持这些窗口函数的DBMS，需要使用标量子查询（按取值唯一的字段联接）。</p>
<p>DB2和Oracle </p>
<p>窗口函数SUM OVER能够非常容易地生成累计和。该解决方案中的ORDER BY子句不仅包含SAL列，而且还包含EMPNO列（主键），以避免累计和中出现重复值。下面例子中的RUNNING_TOTAL2列示意了存在重复值时可能带来的问题：</p>
<p>select empno, sal, </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over(order by sal,empno) as running_total1,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum(sal)over(order by sal) as running_total2</p>
<p>&nbsp;&nbsp; from emp </p>
<p>order by 2 </p>
<p>ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL RUNNING_TOTAL1 RUNNING_TOTAL2</p>
<p>---------- ---------- -------------- --------------</p>
<p>SMITH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800</p>
<p>JAMES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 950&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1750&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1750</p>
<p>ADAMS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2850&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2850</p>
<p>WARD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5350</p>
<p>MARTIN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5350&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5350</p>
<p>MILLER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1300&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6650&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6650</p>
<p>TURNER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1500&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8150&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8150</p>
<p>ALLEN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1600&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9750&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9750</p>
<p>CLARK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2450&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12200&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12200</p>
<p>BLAKE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2850&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15050&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15050</p>
<p>JONES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 18025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 18025</p>
<p>SCOTT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 21025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24025</p>
<p>FORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24025</p>
<p>KING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 29025</p>
<p>对于WARD、MARTIN、SCOTT和FORD，RUNNING_TOTAL2中的值都不正确。他们的工资分别出现了多次，这些重复值都被加在一起计入累计和。这就是需要使用EMPNO（它是唯一的）才能生成与RUNNING_TOTAL1一样的（正确）结果的原因。大家想一想：对于ADAMS，RUNNING_TOTAL1的值为2850，RUNNING_TOTAL2把WARD的工资1250与2850相加，应该得到4100，然而，RUNNING_TOTAL2却返回了5350，这是为什么呢？因为WARD和MARTIN的SAL相同，他们两个的工资（1250）加在一起就等于2500，然后再加2850，就得到5350。如果指定按不会有重复值的列组合（例如，SAL和EMPNO的取值组合都是唯一的）排序，就能确保生成正确的累计和。</p>
<p>MySQL、PostgreSQL和SQL Server</p>
<p>在这些DBMS完全支持窗口函数之前，可以使用标量子查询计算累计和。一定要按取值唯一的列联接，否则一旦存在像工资重复这样的情况，就会产生不正确的累计和。本节解决方案的关键是把D.EMPNO与E.EMPNO联接起来，它会返回（求和）每个满足D.EMPNO小于或等于E.EMPNO D.SAL。为了更容易理解这些内容，可以重新编写标量子查询，把它写成职员之间的联接：</p>
<p>select e.ename as ename1, e.empno as empno1, e.sal as sal1,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d.ename as ename2, d.empno as empno2, d.sal as sal2</p>
<p>&nbsp;&nbsp; from emp e, emp d</p>
<p>where d.empno &lt;= e.empno</p>
<p>&nbsp;&nbsp;&nbsp; and e.empno = 7566 </p>
<p>ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EMPNO1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL1 ENAME&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EMPNO2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SAL2</p>
<p>---------- ---------- ---------- ---------- ---------- ----------</p>
<p>JONES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7566&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975 SMITH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7369&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 800</p>
<p>JONES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7566&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975 ALLEN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7499&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1600</p>
<p>JONES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7566&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975 WARD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7521&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1250</p>
<p>JONES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7566&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975 JONES&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7566&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2975</p>
<p>EMPNO2中的每个值与EMPNO1中的每个值相比较。对于EMPNO2值小于等于EMPNO1值的所有行，都会把SAL2值加入总和。在这个例子中，职员Smith、Allen、Ward和Jones的EMPNO值都与Jones的EMPNO值相比较。由于这四个职员的EMPNO都满足小于等于Jones的EMPNO的条件，所以会把这些工资加起来；而那些大于Jones的EMPNO的职员都不会计入SUM中。完整的查询的计算方法是：将所有EMPNO小于等于7934（Miller的EMPNO，这个表中的最大值）的所有职员的工资加起来。</p>
  <img src ="http://www.blogjava.net/jxhkwhy/aggbug/200482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jxhkwhy/" target="_blank">纸飞机</a> 2008-05-14 21:37 <a href="http://www.blogjava.net/jxhkwhy/articles/200482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>