SQL數據庫編程大賽(第三期)

本期題目:
2011年度itpub數據庫技術大會將於4月15日隆重舉行。與會人員分佈於不同城市(A,B,C,D,....),每個城市及其會員人數保存在一張表cities(city_name, members)
這些城市及他們之間的通路構成了一張網絡交通圖。有直接通路的兩個城市及其距離保存在表routes之中,所有通路都是雙向的,但每條直接通路只用一行記錄來表示。
外地的會員將乘坐出租車參加大會,車費等於距離*價格(常數)。本地會員不用考慮交通費。

交通費定義:itpub要爲外地會員發放往返的交通補貼,每人每公里2元(雙程),在哪個城市舉辦能夠最節省,需要花費多少錢?

問題:選擇在哪個城市舉行,能夠使得會員的總交通費最低?列出這個城市及應發放給其他城市會員的總的交通補助。
輸出格式如下:
輸出3列數據,按城市名升序排列
舉辦城市  TOTAL   總費用
舉辦城市  來自城市   費用
....

例如,假設你求出在A城市最省,總的補助需要發放10000元,其中給B城市的會員要發8000, 給C城市的會員要發2000
輸出

CODE:
A       TOTAL   10000
A       B       8000
A       C       2000


如果存在在多個城市辦會交通費用相同,則都列出,按城市名升序排列
例如,假設你求出在A,B城市最省,需要發10000元,如果在A辦,B城市的會員8000, C城市的會員2000,如果在B辦,A城市的會員8000, C城市的會員2000,
輸出

CODE:
A       TOTAL   10000
A       B       8000
A       C       2000
B       TOTAL   10000
B       A       8000
B       C       2000


表結構和樣例數據如下,樣例數據供參考,評委將用多組數據驗證你的解答。表結構不能變動,不按此表結構答題的不得分

CODE:
CREATE TABLE cities (
       city_name  VARCHAR2(10) PRIMARY KEY
      ,members    NUMBER
      );
INSERT INTO cities
(
SELECT 'A' ,20 FROM dual union
SELECT 'B' ,31 FROM dual union
SELECT 'C' ,23 FROM dual union
SELECT 'D' ,42 FROM dual union
SELECT 'E' ,25 FROM dual union
SELECT 'F' ,29 FROM dual union
SELECT 'G' ,32 FROM dual union
SELECT 'H' ,45 FROM dual union
SELECT 'I' ,50 FROM dual union
SELECT 'J' ,32 FROM dual union
SELECT 'K' ,22 FROM dual
);
commit;

CREATE TABLE routes (
       city1      VARCHAR2(10)
      ,city2      VARCHAR2(10)
      ,distance   NUMBER
      ,PRIMARY KEY (city1,city2)
      ,CONSTRAINT check_cities CHECK (city1<city2) ---- 爲了防止同一條通路被插入兩遍
      );
INSERT INTO routes
(
SELECT 'A' ,'B' , 71 FROM dual union
SELECT 'A' ,'C' , 80 FROM dual union
SELECT 'A' ,'D' , 80 FROM dual union
SELECT 'A' ,'E' , 66 FROM dual union
SELECT 'A' ,'F' , 86 FROM dual union
SELECT 'C' ,'G' ,112 FROM dual union
SELECT 'D' ,'G' , 60 FROM dual union
SELECT 'G' ,'H' ,102 FROM dual union
SELECT 'G' ,'I' , 91 FROM dual union
SELECT 'G' ,'J' ,133 FROM dual union
SELECT 'G' ,'K' ,127 FROM dual union
SELECT 'C' ,'D' , 79 FROM dual union
SELECT 'C' ,'H' ,155 FROM dual union
SELECT 'C' ,'E' ,119 FROM dual union  
SELECT 'B' ,'D' , 52 FROM dual union
SELECT 'D' ,'I' , 44 FROM dual union  
SELECT 'B' ,'L' , 41 FROM dual union
SELECT 'J' ,'L' ,201 FROM dual union  
SELECT 'B' ,'F' , 79 FROM dual union
SELECT 'H' ,'K' , 38 FROM dual union
SELECT 'J' ,'K' , 81 FROM dual
);
commit;


數據庫平臺:適用Oracle、MS SQL Server,版本(Oracle推薦10gr2(包含)以上版本、MS SQL Sever推薦2008版本)

原文見:http://www.itpub.net/thread-1408182-1-1.html

參賽者答案:http://www.itpub.net/thread-1415335-1-1.html

我提交的答案:

/*
Oracle11gR2,使用遞歸with語法,運行時間0.05秒
allroutes 根據routes得出包括反方向的所有路徑組合,
s子句是生成各個城市間的不同路徑列表:
 rn的作用主要用於判斷循環,
 root表示出發城市,
 city2表示目標城市,
 sum_dis 表示距離,
 path表示路徑,裏面的內容如'A-B-C-D'這樣的

 where條件中instr(s.path,a.city2)<=0作用是不出現迴路,即當發現a.city2(新路徑的終點城市)已經在上一路徑列表裏則退出這一支路的遞歸
 s.sum_dis+a.DISTANCE<
     (select nvl(max(r.DISTANCE),999999) from routes r
      where (s.root=r.city1 and a.city2=r.city2) or (s.root=r.city2 and a.city2=r.city1)
     )
 上面這個條件是取消路徑小於routes中直線路徑長度的後續檢測,這裏沒有使用前面定義的allroutes是爲了使用索引進行性能優化
grouping sets(city_name,(city_name,city2)這個子句用於分組統計總成本及各城市間成本。
*/

 

/*
Oracle10gR2,使用connect by語法,運行時間0.3秒
allroutes 根據routes得出包括反方向的所有路徑組合,
CONNECT_BY_ROOT(city1) root表示出發城市,
city2表示目標城市,
sum_money_str表示路徑距離的一個字符串,裏面的內容如'000+080+082+091'這樣的,
nocycle 用於終止循環路徑,
CONNECT_BY_ROOT(city1) <> city2  優化用,當路徑回到根節點時退出,
+use_merge(a,b) 優化用
與子查詢select rownum rn from dual connect by rownum < (select count(*) from cities) 關聯,用於計算sum_money_str的值
sum(substr(a.sum_money_str, (b.rn) * 4 + 1, 3)) distance用於計算sum_money_str值
grouping sets(city_name,(city_name,city2)這個子句用於分組統計總成本及各城市間成本。
*/

 

以下是結果:
ROOT       NVL(CITY_NAME,'TOTAL') SUM(MONEY)
---------- ---------------------- ----------
D          TOTAL                       68356
D          A                            3200
D          B                            3224
D          C                            3634
D          E                            7300
D          F                            7598
D          G                            3840
D          H                           14580
D          I                            4400
D          J                           12352
D          K                            8228

 

解題思路:見上面的備註。

 

評委點評:where (s.root=r.city1 and a.city2=r.city2) or (s.root=r.city2 and a.city2=r.city1))
條件的優化思路很明確。性能很好。
但是此答案也存在瑕疵,11g版本答案能正確輸出多種選擇和多次轉乘,但一個城市的名稱包含另外一個城市名稱時無法輸出正確結果。另:10g版本答案在某些測試數據下輸出錯誤。

個人分析:

1、這道題主要是考一個圖路徑的算法,我算法不好,所以也不管理論的東西,按自己的理解去做。

2、因爲看到前兩期有許多人用11gR2新的with遞歸語法,所以也嘗試學習一下。感覺功能比以前強大,不過語法規則設計得太差,估計是ORACLE公司新來的人設計,只是實現了功能,沒考慮開發人員的思維習慣,所以不看參考手冊基本上用不來。

3、題目好像是說要滿足10gR2的版本,不清楚爲什麼用11gR2的新語法不扣分,反而加分,這不符合實際應用,畢竟實戰中通用性很重要。

4、如評委所說,未考慮到城市名稱中有字符名含的問題,原因是提供的測試數據不存在包含的現象,所以忽略了,實際當中還是存在的。

5、SQL整體性能還不錯,但是大數據量時估計性能不好。

 

總體來說,這期得分還算合理,個人感覺第4名(3-7)很精彩,根據圖論基礎算法使用Oracle10g的MODEL語法解題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章