|
名字叫Frank Zhou, 似乎是中国人:
http://oraqa.com/author/frank-zhou/
最近OO发的几个趣味问题,他早就做过了。他写的SQL很对我的胃口,我准备陆续把一些有趣的问题转贴过来,比如这个:
http://oraqa.com/2008/02/01/how- ... ions-puzzle-in-sql/
Assuming you have enough coins of 1, 5, 10, 25 and 50 cents, how many ways are there to make change for a dollar?
用1分,5分,10分,25分,50分硬币凑成一元,总共有几种组合办法?
最直接了当的写法:
SELECT '1*' ||c1 .cnt
||'+5*' ||c5 .cnt
||'+10*'||c10.cnt
||'+25*'||c25.cnt
||'+50*'||c50.cnt AS result
FROM (SELECT ROWNUM-1 cnt FROM DUAL CONNECT BY ROWNUM-1<=100) c1 ---- 利用CONNECT BY构造出0~100作为1分硬币的个数
,(SELECT ROWNUM-1 cnt FROM DUAL CONNECT BY ROWNUM-1<=20 ) c5
,(SELECT ROWNUM-1 cnt FROM DUAL CONNECT BY ROWNUM-1<=10 ) c10
,(SELECT ROWNUM-1 cnt FROM DUAL CONNECT BY ROWNUM-1<=4 ) c25
,(SELECT ROWNUM-1 cnt FROM DUAL CONNECT BY ROWNUM-1<=2 ) c50
WHERE 1 *c1 .cnt
+5 *c5 .cnt
+10*c10.cnt
+25*c25.cnt
+50*c50.cnt=100;
我用11GR2的写法:
WITH coins AS (
SELECT 1 cents FROM DUAL
UNION ALL SELECT 5 cents FROM DUAL
UNION ALL SELECT 10 cents FROM DUAL
UNION ALL SELECT 25 cents FROM DUAL
UNION ALL SELECT 50 cents FROM DUAL
)
,t(c1,c5,c10,c25,c50,cents,total_val) AS (
SELECT DECODE(c.cents,1,1,0)
,DECODE(c.cents,5,1,0)
,DECODE(c.cents,10,1,0)
,DECODE(c.cents,25,1,0)
,DECODE(c.cents,50,1,0)
,cents
,cents
FROM coins c
UNION ALL
SELECT c1 + DECODE(c.cents,1,1,0)
,c5 + DECODE(c.cents,5,1,0)
,c10+ DECODE(c.cents,10,1,0)
,c25+ DECODE(c.cents,25,1,0)
,c50+ DECODE(c.cents,50,1,0)
,c.cents
,t.total_val + c.cents
FROM t, coins c
WHERE t.total_val + c.cents<=100 AND t.cents<=c.cents
)
SELECT * FROM t WHERE total_val=100;
他的写法在11G通不过:
SELECT ltrim(max(str),',') combinations,
rtrim(ltrim(replace(max(str_num),',','+'),'+'),'+') Sum,
count(*) over () as counter
FROM
(SELECT sys_connect_by_path(str,', ') str, rownum rn,
translate(sys_connect_by_path(str_num,','),
chr(0)||'ABCDE',chr(0))||',' AS str_num
FROM
(SELECT str_num, str, to_number(substr(str_num,0,length(str_num)-1)) n,
substr(str_num,length(str_num)) coin_type
FROM
(SELECT LEVEL||'A' str_num , LEVEL||' Penny' AS str
FROM dual CONNECT BY LEVEL <=100
UNION ALL
SELECT LEVEL*5||'B' str_num, LEVEL||' Nickel'
FROM dual CONNECT BY LEVEL*5 <=100
UNION ALL
SELECT LEVEL*10||'C' str_num, LEVEL||' Dime'
FROM dual CONNECT BY LEVEL*10 <=100
UNION ALL
SELECT LEVEL*25||'D' str_num, LEVEL||' Quarter'
FROM dual CONNECT BY LEVEL*25 <=100
UNION ALL
SELECT LEVEL*50||'E' str_num, LEVEL||' Half D$'
FROM dual CONNECT BY LEVEL*50<=100
)
)
WHERE
CASE LEVEL
WHEN 1 THEN CASE WHEN n = 100 THEN 1 END
WHEN 2 THEN CASE WHEN n + PRIOR n =100 THEN 1 END
WHEN 3 THEN CASE WHEN CONNECT_BY_ROOT n + PRIOR n + n =100 THEN 1 END
ELSE 1 END =1
CONNECT BY PRIOR coin_type < coin_type
AND
CASE LEVEL
WHEN 2 THEN CASE WHEN n + PRIOR n <=100 THEN 1 END
WHEN 3 THEN CASE WHEN CONNECT_BY_ROOT n + PRIOR n + n <=100 THEN 1 END
WHEN 4 THEN CASE WHEN CONNECT_BY_ROOT n + PRIOR n + n <96 THEN 1 END
WHEN 5 THEN CASE WHEN CONNECT_BY_ROOT n + PRIOR n + n <=85 THEN 1 END
ELSE 1 END =1
)
CONNECT BY PRIOR rn = rn
AND INSTR(str_num,',', 1,LEVEL+1) > 0
AND PRIOR dbms_random.string('p',10) IS NOT NULL
GROUP BY rn
HAVING SUM(TO_NUMBER(SUBSTR(str_num,
INSTR(str_num,',', 1, LEVEL) + 1,
INSTR(str_num,',', 1, LEVEL+1) -
INSTR(str_num,',', 1, LEVEL) -1))) = 100 ;
WHEN 3 THEN CASE WHEN CONNECT_BY_ROOT n + PRIOR n + n <=100 THEN 1 END
*
ERROR at line 38:
ORA-30007: CONNECT BY ROOT operator is not supported in the START WITH or in the CONNECT BY condition
印象中只有某个版本允许在CONNECT BY条件中使用CONNECT_BY_ROOT。
[ 本帖最后由 newkid 于 2010-6-29 03:44 编辑 ] |
|