各種撈取 SQL 的方法
當使用 SQL 撈取資料的時候,如果需要撈取的是比較複雜的資料
同學通常會覺得「需要使用複雜的 SQL 語法」
但事實上,只要搭配適當的 PHP 程式
可以讓 SQL 變得很簡單!
YOYO!
我們的資料庫內有以下資料表
courses 課程資料表:
student 學生資料表
grade 每堂課的成績資料表
假設我們現在的目標是
「撈取某位學生,他修過的每一堂課的分數
以及該課程全班的平均分數」
我們可以有幾種不同的做法
如果要一口氣在一次 SQL 裡面撈取,這句 SQL 會像這樣:
假如我們要撈取學號為 101009332 這位同學的資料 整個 PHP 程式大概會像這樣
這樣子的做法看起來很厲害
不過這句 SQL 是不是有點太複雜了呢?
當然,如果你 SQL 語法非常厲害,你可以使用這樣的方法
但如果對 SQL 掌握度沒這麼高的話,你可以試試看其他方法
在上面的例子,只用一個 SQL 就找到答案
這次我們試試看把 SQL 拆成兩個:
程式可能會像這樣
(如果只抓學號 101009332 這位學生)
這次的 SQL 語言是不是好懂很多呢?
不過這個作法仍有個缺點,就是第二個 SQL 由於包在迴圈裡面
所以會被執行很多次
如果使用者不是很多的話,其實沒什麼差
但若是使用者一多(數千、數萬之類的),可能就會對伺服器造成負擔
因此,可考慮第三種的方法
上一個例子會對資料庫做多次的存取
要改善,我們可以試著這麼做:
如此一來,就能同時兼顧讓 SQL 變簡單
並且也不會做太多次資料庫查詢囉!
總而言之
這個教學想要闡述的概念
「撈資料,並不是一定要在一個 SQL 內就撈完」
至於怎麼撈比較好、又能減少伺服器負擔
就完全看程式要怎麼寫囉!
同學通常會覺得「需要使用複雜的 SQL 語法」
但事實上,只要搭配適當的 PHP 程式
可以讓 SQL 變得很簡單!
YOYO!
資料表設計
courses 課程資料表:
student 學生資料表
grade 每堂課的成績資料表
假設我們現在的目標是
「撈取某位學生,他修過的每一堂課的分數
以及該課程全班的平均分數」
我們可以有幾種不同的做法
1. 用一句很厲害的 SQL 來解決
SELECT *, (SELECT AVG(grade) FROM grade WHERE course_id = c.course_id ) AVG_SCORE FROM student s, courses c, grade g WHERE s.student_id = g.student_id AND c.course_id = g.course_id GROUP BY s.student_id, c.course_id
假如我們要撈取學號為 101009332 這位同學的資料 整個 PHP 程式大概會像這樣
<?php // 資料庫連線的東西 mysql_connect("localhost", "root", "abc123"); mysql_select_db("dbms_school"); mysql_query("SET NAMES 'utf8'"); $sql = "SELECT *, (SELECT AVG(grade) FROM grade WHERE course_id = c.course_id ) AVG_SCORE FROM student s, courses c, grade g WHERE s.student_id = g.student_id AND c.course_id = g.course_id AND s.student_id = 101009332 GROUP BY s.student_id, c.course_id"; $result = mysql_query($sql); while( $row = mysql_fetch_array($result) ) { // 顯示 echo "課程名稱:".$row['course_name']; echo "學生得分:".$row['grade']; echo "全班得分:".$row['AVG_SCORE']; // p.s 如果不確定 $row[''] 中間的 '' 裡面要輸入什麼 // 可以嘗試執行 print_r( $row ) 這一句試試看 } ?>
這樣子的做法看起來很厲害
不過這句 SQL 是不是有點太複雜了呢?
當然,如果你 SQL 語法非常厲害,你可以使用這樣的方法
但如果對 SQL 掌握度沒這麼高的話,你可以試試看其他方法
2. 跑很多很多次 SQL
在上面的例子,只用一個 SQL 就找到答案
這次我們試試看把 SQL 拆成兩個:
- 取得學生修過的課的列表
- 分別取得每一堂課的平均分數
程式可能會像這樣
(如果只抓學號 101009332 這位學生)
<?php // 資料庫連線的東西 mysql_connect("localhost", "root", "abc123"); mysql_select_db("dbms_school"); mysql_query("SET NAMES 'utf8'"); // 撈取學生的修課名稱、ID $sql = "SELECT c.course_id, c.course_name, g.grade FROM grade g, courses c WHERE student_id = 101009332 AND g.course_id = c.course_id"; $result = mysql_query($sql); while( $row = mysql_fetch_array($result) ) { $courseId = $row['course_id']; // 再做一次 SQL,撈取這堂課的平均分數 $sql2 = "SELECT AVG( grade ) AVG_SCORE FROM grade WHERE course_id = $courseId GROUP BY course_id"; $result2 = mysql_query( $sql2 ); $row2 = mysql_fetch_array($result2); $avg = $row2['AVG_SCORE']; // 顯示 echo "課程名稱:".$row['course_name']; echo "學生得分:".$row['grade']; echo "全班得分:".$avg; // p.s 如果不確定 $row[''] 中間的 '' 裡面要輸入什麼 // 可以嘗試執行 print_r( $row ) 這一句試試看 } ?>
這次的 SQL 語言是不是好懂很多呢?
不過這個作法仍有個缺點,就是第二個 SQL 由於包在迴圈裡面
所以會被執行很多次
如果使用者不是很多的話,其實沒什麼差
但若是使用者一多(數千、數萬之類的),可能就會對伺服器造成負擔
因此,可考慮第三種的方法
3. 先暫時把資料存起來,之後再拿出來用
上一個例子會對資料庫做多次的存取
要改善,我們可以試著這麼做:
- 先查詢所有課程的平均成績,並把這些資料存進陣列裡
- 查詢學生的修課紀錄,並到剛剛存的陣列裡找平均成績資料
說起來可能有點抽象,不如直接看看程式碼:
<?php // 資料庫連線的東西 mysql_connect("localhost", "root", "abc123"); mysql_select_db("dbms_school"); mysql_query("SET NAMES 'utf8'"); // 撈取每一堂課的平均成績 $sql = "SELECT course_id, AVG( grade ) AVG_SCORE FROM grade GROUP BY course_id"; $result = mysql_query($sql); // 建立一個空的陣列,準備用來存放撈出來的資料 $courseAvgScores = array(); while( $row = mysql_fetch_array($result) ) { $courseId = $row['course_id']; $avgScore = $row['AVG_SCORE']; // 把 courseID 當作陣列的索引,並存入平均分數資料 $courseAvgScores[ $courseId ] = $avgScore; } // 再來撈取學生的修課紀錄 $sql2 = "SELECT c.course_id, c.course_name, g.grade FROM grade g, courses c WHERE student_id = 101009332 AND g.course_id = c.course_id"; $result2 = mysql_query($sql2); while( $row = mysql_fetch_array($result2) ) { $c_id = $row['course_id']; // 從陣列中抓到平均成績資料 $avg = $courseAvgScores[ $c_id ]; // 顯示 echo "課程名稱:".$row['course_name']; echo "學生得分:".$row['grade']; echo "全班得分:".$avg; // p.s 如果不確定 $row[''] 中間的 '' 裡面要輸入什麼 // 可以嘗試執行 print_r( $row ) 這一句試試看 } ?>
如此一來,就能同時兼顧讓 SQL 變簡單
並且也不會做太多次資料庫查詢囉!
總而言之
這個教學想要闡述的概念
「撈資料,並不是一定要在一個 SQL 內就撈完」
至於怎麼撈比較好、又能減少伺服器負擔
就完全看程式要怎麼寫囉!