各種撈取 SQL 的方法

當使用 SQL 撈取資料的時候,如果需要撈取的是比較複雜的資料
同學通常會覺得「需要使用複雜的 SQL 語法」

但事實上,只要搭配適當的 PHP 程式
可以讓 SQL 變得很簡單!


YOYO!





資料表設計


我們的資料庫內有以下資料表

courses 課程資料表:
 

student 學生資料表


grade 每堂課的成績資料表



假設我們現在的目標是
「撈取某位學生,他修過的每一堂課的分數
以及該課程全班的平均分數」
我們可以有幾種不同的做法


1. 用一句很厲害的 SQL 來解決


如果要一口氣在一次 SQL 裡面撈取,這句 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 拆成兩個:

  1. 取得學生修過的課的列表
  2. 分別取得每一堂課的平均分數

程式可能會像這樣
(如果只抓學號 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. 先暫時把資料存起來,之後再拿出來用



上一個例子會對資料庫做多次的存取
要改善,我們可以試著這麼做:

  1. 先查詢所有課程的平均成績,並把這些資料存進陣列裡
  2. 查詢學生的修課紀錄,並到剛剛存的陣列裡找平均成績資料

說起來可能有點抽象,不如直接看看程式碼:


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