各種撈取 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 內就撈完」
至於怎麼撈比較好、又能減少伺服器負擔
就完全看程式要怎麼寫囉!