ติดตามบทความล่าสุดของผู้เขียนได้ที่ phpinfo() Facebook Page
อธิบาย MySQL error message ในการเขียนโปรแกรม PHP ที่ตั้งกระทู้ถามกันบ่อยๆ
error message (ข้อความแสดงความผิดพลาด) เหล่านี้เป็นข้อความแสดงความผิดพลาดที่ MySQL Server จะส่งกลับมาให้เราอ่านได้ด้วยฟังก์ชั่น mysql_error() บทความนี้จะอธิบายความผิดพลาดที่พบเห็นกันบ่อยๆ
การเขียนโปรแกรมติดต่อกับฐานข้อมูลนั้นอาจจะดูเหมือนว่าไม่ยากลำบาก และเริ่มต้นเรียนรู้ได้ง่าย แต่สิ่งที่ทำให้มือใหม่หลายคนต้องหยุดชะงัก (บางคนชะงักเป็นเวลานาน บางคนเลิกเรียนรู้ไปเลยก็มี) คือความผิดพลาดที่ไม่รู้ว่าเกิดเพราะอะไร
เนื่องจากฟังก์ชั่น mysql_query() หรือ mysql_db_query() จะไม่แสดงความผิดพลาดที่เกิดจากการประมวลผล SQL ผ่าน PHP คือจะไม่มี Notice, Warning, Fatal error เกิดจากกระบวนการนี้
และจะกลายเป็นว่าในหลายๆ ครั้ง ผู้ที่พบเจอกับปัญหานี้ จะเจอกับ error message ของ PHP ในจุดอื่นแทน
และเข้าใจไปว่าน่าจะผิดตรงจุดนั้น
$result = mysql_query("SELECT * FROM order");
// จะเกิด error ณ จุดต่อจากนี้
while ($result = mysql_fetch_array($result)) {
// Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given
// เพราะ mysql_query() เกิด error จึงคืนค่ากลับมาให้ $result เป็น false
// และเมื่อนำไปใช้กับ mysql_fetch_array() ก็จะเกิด error
// ซึ่งหลายๆ คนคิดว่าผิดตรงนี้ แต่จริงๆ ไม่ใช่ ผิดก่อนหน้านั้นแล้ว
}
วิธีเดียวที่เราจะสามารถรับรู้ได้ว่าการประมวลผล SQL ล่าสุดที่เราได้สั่งไปนั้นมีความผิดพลาด
เราต้องตรวจจากค่าที่คืนมาจาก mysql_query()
ซึ่ง mysql_query() จะคืนค่ากลับมาเป็น false เสมอเมื่อเกิดความผิดพลาด
และเมื่อมีความผิดพลาดเกิดขึ้น เราจะสามารถอ่าน error message ได้จากฟังก์ชั่น mysql_error()
ซึ่งเราจะเขียนโปรแกรมได้ในลักษณะนี้
มาทำแบบนี้ให้เป็นนิสัยกันครับ
$result = mysql_query("SELECT * FROM order");
if (!$result) { // ตรวจว่า $result ให้ผลเป็นเท็จหรือไม่
echo mysql_error(); // แสดง error message
exit; // จบการทำงาน (ซึ่งไม่จำเป็นเสมอไป)
}
หรือจะแบบนี้
$sql = "SELECT * FROM order";
$result = mysql_query($sql);
if (!$result) { // ตรวจว่า $result ให้ผลเป็นเท็จหรือไม่
echo mysql_error() . "<br />\n$sql"; // แสดง error message และ SQL ปัจจุบัน
exit;
}
ต่อไปนี้จะเป็นการอธิบาย MySQL error message ที่ตั้งกันเป็นกระทู้บ่อยๆ ในเว็บบอร์ดของ thaicreate
โดยจะอธิบายทั้งสาเหตุ และวิธีการแก้ปัญหาครับ
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '???' at line ???
นี่คือ error ที่เจอบ่อยที่สุดสำหรับกรณีที่มีการเรียกใช้ mysql_query()
มีความหมายคร่าวๆ ว่า "SQL ที่คุณเขียนมานั้น มันผิดหลักไวยากรณ์"
ซึ่งผิดได้หลายแบบมากๆ
ที่พบเห็นบ่อยๆ ในกระทู้ต่างๆ ก็มีดังต่อไปนี้
ผิดไปเลย คำสั่งบ้าอะไรก็ไม่รู้
mysql_query("WHEN I WAS YOUNG I LISTEN TO THE RADIO");
กรณีแบบนี้ไม่ค่อยเกิดหรอกครับ
แต่อาจจะเกิดในกรณีแบบนี้ เวลาใช้การเชื่อมต่อสตริง แล้วลืมเครื่องหมาย .
$strSQL = "SELECT * FROM `table` ";
$strSQL .= "WHERE `id` = '" . $_POST['id'] . "'";
$strSQL = " AND `name` = '" . $_POST['name'] . "'";
$strSQL .= " AND `iq` > '" . $_POST['iq'] . "'";
$strSQL .= " ORDER BY `id` DESC";
ซึ่งจะทำให้ตัวแปร $strSQL เริ่มต้นการกำหนดค่าใหม่ ณ บรรทัดนั้น กลายเป็น SQL ที่ไม่มีความหมายไปเสีย
ลืมปิดสตริงด้วยเครื่องหมาย '
mysql_query("SELECT * FROM `table` WHERE `id` = '1 AND `name` = 'cookie'");
ที่ถูกต้องคือ
mysql_query("SELECT * FROM `table` WHERE `id` = '1' AND `name` = 'cookie'");
ใช้ค่าวันที่ที่ไม่ได้ครอบด้วยเครื่องหมาย '
mysql_query("SELECT * FROM `table` WHERE `created` = 2013-03-01");
ที่ถูกต้องคือ
mysql_query("SELECT * FROM `table` WHERE `created` = '2013-03-01'");
พิมพ์ตกเครื่องหมาย ,
mysql_query("SELECT id, username password FROM `table`");
ที่ถูกต้องคือ
mysql_query("SELECT id, username, password FROM `table`");
ซึ่งส่วนใหญ่สาเหตุจะเป็นเพราะนิยมใช้การเชื่อมต่อสตริง (อีกแล้ว) ทำให้อ่านยาก และผิดพลาดง่าย
และการพิมพ์ตกเครื่องหมาย ' ก็เช่นกัน
$strSQL = "UPDATE `members` SET ";
$strSQL .= "`username` = '" . $POST['username'] . "',";
$strSQL .= "`password` = '" . $POST['password'] . "'";
$strSQL .= "`email` = '" . $POST['email'] . "',";
$strSQL .= "`first_name` = '" . $POST['first_name'] . ",";
$strSQL .= "`last_name` = '" . $POST['last_name'] . "'";
$strSQL .= "WHERE id = '" . $_POST['id'] . "'";
mysql_query($strSQL) or die(mysql_error());
ซึ่งแนะนำให้ใช้ ตัวแปรในสตริง จะทำให้อ่านง่าย แก้ไขง่าย โอกาสเกิดความผิดพลาดน้อยกว่า
// เราสามารถขึ้นบรรทัดใหม่ในสตริงได้เลย สามารถจัดรูปแบบให้อ่านง่ายตามที่เราต้องการ
// ไม่จำเป็นต้องใช้การเชื่อมต่อสตริงให้ดูยุ่งเหยิง
$strSQL = "
UPDATE members SET
username = '$POST[username]',
password = '$POST[password]',
email = '$POST[email]',
first_name = '$POST[first_name]',
last_name = '$POST[last_name]'
WHERE id = '$_POST[id]'
";
mysql_query($strSQL) or die(mysql_error());
ผิดเพราะใช้ "คำสงวน" เป็นชื่อคอลัมน์หรือชื่อตาราง โดยไม่ได้เปิดและปิดด้วยเครื่องหมาย `
mysql_query("SELECT * FROM order");
mysql_query("SELECT force FROM jedi");
mysql_query("UPDATE characters SET limit = 'Final Heaven' WHERE name = 'Tifa'");
แต่แบบนี้ OK
mysql_query("SELECT * FROM `order`");
mysql_query("SELECT `force` FROM jedi");
mysql_query("UPDATE characters SET `limit` = 'Final Heaven' WHERE name = 'Tifa'");
ดังนั้นขอแนะนำให้ใช้เครื่องหมาย ` เสมอ สำหรับชื่อตาราง และชื่อคอลัมน์ ซึ่งจะทำให้เราสังเกตเห็นได้ชัดด้วยว่า อะไรเป็นอะไร
แต่เคยมีบางคนบอกผมว่า ` มันพิมพ์ยาก เพราะมันดันไปอยู่ที่เดียวกับแป้นเปลี่ยนภาษาไทย
เลยเป็นเหตุผลสำคัญที่เขาไม่ใช้มัน เวลาเจอคำสงวนที่เป็นชื่อตารางหรือคอลัมน์ เขาจึงขอเลือกที่จะเปลี่ยนชื่อมันไปเลยดีกว่า
อันนี้ต้องขอบอกว่าเป็นวิธีแก้ปัญหาที่ปลายเหตุครับ
ลองคิดดูว่า หากสักวันมาตรฐาน SQL อาจจะมีคำสงวนเพิ่มขึ้นมาใหม่ ซึ่งอาจจะซ้ำกับชื่อตารางหรือชื่อคอลัมน์ของเราในปัจจุบัน
โค้ด SQL ที่เราเขียนไว้จะกลายเป็นโค้ดที่ใช้งานไม่ได้ไปในทันที
ซึ้งถ้าหากเรายอมเหนื่อยนิดนึง ยอมใช้ ` ให้ติดเป็นนิสัย
เราก็จะไม่เจอกับปัญหานั้น และโค้ด SQL ของเราก็จะดูเป็นมืออาชีพมากขึ้นด้วยครับ
ลองสังเกต SQL ที่สร้างออกมาด้วย phpmyadmin หรือโปรแกรมยอดนิยมอื่นๆ ดูได้ครับ เขาใช้ ` ตลอด
รู้หรือไม่?
คุณสามารถพิมพ์ ` ได้ด้วยการกด Alt ค้างไว้ และตามด้วยแป้นตัวเลข 9 และ 6
จากนั้นก็ปล่อย Alt แค่นี้คุณก็จะได้ตัวอักษร ` ไว้เชยชมแล้วครับ ไม่เห็นจะพิมพ์ยากตรงไหนเลย
คำสงวนใน MySQL ได้แก่
ADD ALL ALTER ANALYZE
AND AS ASC ASENSITIVE
BEFORE BETWEEN BIGINT BINARY
BLOB BOTH BY CALL
CASCADE CASE CHANGE CHAR
CHARACTER CHECK COLLATE COLUMN
CONDITION CONNECTION CONSTRAINT CONTINUE
CONVERT CREATE CROSS CURRENT_DATE
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR
DATABASE DATABASES DAY_HOUR DAY_MICROSECOND
DAY_MINUTE DAY_SECOND DEC DECIMAL
DECLARE DEFAULT DELAYED DELETE
DESC DESCRIBE DETERMINISTIC DISTINCT
DISTINCTROW DIV DOUBLE DROP
DUAL EACH ELSE ELSEIF
ENCLOSED ESCAPED EXISTS EXIT
EXPLAIN FALSE FETCH FLOAT
FLOAT4 FLOAT8 FOR FORCE
FOREIGN FROM FULLTEXT GOTO
GRANT GROUP HAVING HIGH_PRIORITY
HOUR_MICROSECOND HOUR_MINUTE HOUR_SECOND IF
IGNORE IN INDEX INFILE
INNER INOUT INSENSITIVE INSERT
INT INT1 INT2 INT3
INT4 INT8 INTEGER INTERVAL
INTO IS ITERATE JOIN
KEY KEYS KILL LABEL
LEADING LEAVE LEFT LIKE
LIMIT LINES LOAD LOCALTIME
LOCALTIMESTAMP LOCK LONG LONGBLOB
LONGTEXT LOOP LOW_PRIORITY MATCH
MEDIUMBLOB MEDIUMINT MEDIUMTEXT MIDDLEINT
MINUTE_MICROSECOND MINUTE_SECOND MOD MODIFIES
NATURAL NOT NO_WRITE_TO_BINLOG NULL
NUMERIC ON OPTIMIZE OPTION
OPTIONALLY OR ORDER OUT
OUTER OUTFILE PRECISION PRIMARY
PROCEDURE PURGE READ READS
REAL REFERENCES REGEXP RELEASE
RENAME REPEAT REPLACE REQUIRE
RESTRICT RETURN REVOKE RIGHT
RLIKE SCHEMA SCHEMAS SECOND_MICROSECOND
SELECT SENSITIVE SEPARATOR SET
SHOW SMALLINT SONAME SPATIAL
SPECIFIC SQL SQLEXCEPTION SQLSTATE
SQLWARNING SQL_BIG_RESULT SQL_CALC_FOUND_ROWS SQL_SMALL_RESULT
SSL STARTING STRAIGHT_JOIN TABLE
TERMINATED THEN TINYBLOB TINYINT
TINYTEXT TO TRAILING TRIGGER
TRUE UNDO UNION UNIQUE
UNLOCK UNSIGNED UPDATE UPGRADE
USAGE USE USING UTC_DATE
UTC_TIME UTC_TIMESTAMP VALUES VARBINARY
VARCHAR VARCHARACTER VARYING WHEN
WHERE WHILE WITH WRITE
XOR YEAR_MONTH ZEROFILL
ใช้ expression ในที่ที่ใช้ไม่ได้
mysql_query("SELECT * FROM `table` LIMIT $limit * 2");
ใช้เครื่องหมายวงเล็บใน WHERE แต่ปิดผิดที่
mysql_query("SELECT * FROM `table` WHERE (`id` = 1 AND `name` = 'cookie' ORDER BY `name` DESC)");
ที่ถูกต้องคือ
mysql_query("SELECT * FROM `table` WHERE (`id` = 1 AND `name` = 'cookie') ORDER BY `name` DESC");
No database selected
หมายความว่า คุณใช้ query ที่ทำการเข้าถึงตาราง แต่ไม่ได้ระบุฐานข้อมูล
โดยไม่ได้ระบุใน query หรือไม่ได้เลือกผ่านฟังก์ชั่นจำพวก mysql_select_db()
// query แบบระบุชื่อฐานข้อมูลไปเลย ซึ่งวิธีนี้เป็นวิธีที่ไม่ค่อยจะปกติ ไม่ค่อยมีคนใช้กัน
mysql_query("SELECT * FROM `db`.`table`");
// query แบบไม่ระบุชื่อฐานข้อมูล ซึ่งถ้าหากว่าไม่ได้เลือกไว้ด้วย mysql_select_db() ก็จะเกิด error นี้
mysql_query("SELECT * FROM `db`.`table`");
หากเกิด error นี้ วิธีแก้ก็คือตรวจสอบดูว่ามีการเรียกใช้ mysql_select_db() อยู่ก่อนหน้านี้แล้วหรือยัง[/tt]
ขั้นตอนปกติของการเชื่อมต่อกับฐานข้อมูล MySQL
// ติดต่อฐานข้อมูล
if (!mysql_connect('localhost', 'root', 'password')) {
echo mysql_error();
exit;
}
// เลือกฐานข้อมูลหลักที่จะใช้ในทุกๆ คำสั่งที่เกี่ยวข้องกับตาราง
mysql_select_db('default_database_name');
Query was empty
หมายถึง "คำสั่ง SQL" ที่ส่งไปให้ mysql_query() มันว่างเปล่า
// แบบนี้ครับ
mysql_query('');
// หรือมีแต่ช่องว่าง หรือ whitespace
mysql_query(' ');
mysql_query('
');
// หรือค่าใดๆ แปลงเป็นสตริงแล้วได้สตริงว่าง ''
mysql_query(null);
mysql_query(false);
ดังนั้นถ้าหากเป็นการเรียกใช้ mysql_query() โดยที่ query เป็นตัวแปรแล้วเกิด error นี้
ก็ให้ตรวจสอบค่าของตัวแปรนั้นๆ ครับ
Duplicate entry '?' for key ?
อันนี้ก็เป็นอีก error หนึ่งที่เกิดขึ้นบ่อย
สาเหตุเกิดจากการพยายามให้ค่ากับคอลัมน์ที่เป็นชนิด PRIMARY หรือ UNIQUE
โดยที่ในตารางนั้นๆ มีแถวที่มีค่าดังกล่าวอยู่แล้ว
เช่น มีข้อมูลดังภาพ
แล้วพยายามที่จะ INSERT แถวใหม่เข้าไปโดยการเพิ่มแถวที่มี id เหมือนกับแถวที่มีอยู่ในตารางแล้ว
mysql_query("
INSERT INTO `players`
(`id`, `created`, `job`, `level`, `exp`, `hp`, `mp`)
VALUES
(1, NOW(), 'Mage', 1, 0, 10, 5)
");
ก็จะเกิด error ดังกล่าว
Incorrect integer value: '?' for column '?' at row ?
หมายถึงการที่เขียนข้อมูลลงในตาราง และให้ค่าที่ไม่ใช่ชนิดตัวเลข หรือแปลงเป็นตัวเลขไม่ได้
ให้แก่คอลัมน์ที่เป็นชนิดตัวเลข (INT, MEDIUMINT etc.)
mysql_query("UPDATE `players` SET `hp` = 'Hello World' WHERE `id` = 1");
แต่ตาราง players มีโครงสร้างเป็นแบบนี้
ดังนั้นการให้ค่า 'Hello World' ให้กับคอลัมน์ hp จึงทำให้เกิด error ดังกล่าวครับ
บทความที่เกี่ยวข้อง
ศึกษาเพิ่มเติม
ติดตามบทความล่าสุดของผู้เขียนได้ที่ phpinfo() Facebook Page