Register Register Member Login Member Login Member Login Forgot Password ??
PHP , ASP , ASP.NET, VB.NET, C#, Java , jQuery , Android , iOS , Windows Phone
 

Registered : 109,027

HOME > PHP > PHP Forum > ปรึกษาเรื่องคำสั่ง SQL SELECT เพื่อลดการ query และ เพิ่มประสิทธิภาพมากที่สุดครับ



 

ปรึกษาเรื่องคำสั่ง SQL SELECT เพื่อลดการ query และ เพิ่มประสิทธิภาพมากที่สุดครับ

 



Topic : 072690



โพสกระทู้ ( 335 )
บทความ ( 0 )



สถานะออฟไลน์




ขอถามในห้องภาษา PHP นะครับ เพราะใช้ PHP ในการเชื่อมต่อ Database แต่เรื่องที่จะถามส่วนใหญ่เกี่ยวกับ SQL ครับ

คือผมทำ webboard
แล้วต้องการ query แล้ว loop โชว์

ชื่อกระทู้ , วัน-เวลาที่ตั้งกระทู้ , จำนวนคนดูกระทู้ , ชื่อผู้ตั้งกระทู้ , ชื่อผู้ที่คอมเม้นล่าสุด , วันเวลาที่คอมเม้นล่าสุด


การออกแบบ Database ของผมเป็นแบบนี้ครับ
มี
- tb_member
member_id
member_name

- tb_board_topic
topic_id
topic_title
topic_detail
topic_date
topic_view
topic_by :::: link กับ member_id

- tb_board_comment
comment_id
comment_detail
comment_date
comment_by :::: link กับ member_id
comment_topic :::: link กับ topic_id


โดยปกติแล้ว ถ้าวิธีที่ง่ายสุดในการ SELECT เลยคือ
ทำการ SELECT หา topic ก่อน แล้ว Loop ตาม Topic ที่ได้ จากนั้นใน Loop ถึง SELECT หา last comment ของ Topic นั้นอีกที
ประมาณนี้

Code (PHP)
$sql = "SELECT topic_title,topic_date,topic_view,member_name as topic_by FROM tb_board_topic INNER JOIN tb_member ON topic_by=member_id ORDER BY topic_date DESC";
$query_topic = mysql_query($sql);
while($result_topic=mysql_fetch_array($query_topic)){
    $sql = "SELECT comment_date , member_name as comment_by FROM tb_board_comment INNER JOIN tb_member ON comment_by=member_id WHERE comment_topic='".$result_topic['topic_id']."' ORDER BY topic_date DESC";
    $query_comment = mysql_query($sql);
    $result_comment = mysql_fetch_array($query_comment);

    #field ที่ได้คือ
    #topic_title , topic_date , topic_by (เป็นชื่อ member_name) , comment_date , comment_by (เป็นชื่อ member_name)

}


ซึ่งจากตัวอย่างข้างบน จำนวนครั้งที่ query จะเท่ากับ
1 + N
ซึ่ง N จะเป็นจำนวนของ topic ที่ได้ แล้วมา Loop แล้วใน Loop นั้นต้อง query เพื่อหา Last Comment อีกที
สมมุติได้จำนวน Topic ทั้งหมด 100 Topic ก็จะต้องมีการ Query ทั้งหมด 1 + 100 = 101 ครั้ง

แต่ที่อยากได้คือ ทำการ SELECT ครั้งเดียว แล้วได้ข้อมูลตามข้างบนทั้งหมด แล้ว ค่อย Loop แสดงผล
ซึ่งถ้าทำวิธีนี้ได้ ก็จะ ใช้การ Query แค่ 1 ครั้ง

ถ้าใช้ JOIN ระหว่าง topic กับ comment แบบนี้

Code
SELECT topic_title ,topic_date ,topic_view ,member_name AS topic_by_name ,comment_date FROM tb_board_topic
INNER JOIN tb_member ON member_id=topic_by
LEFT OUTER JOIN tb_board_comment ON comment_topic=topic_id
ORDER BY board_comment_date DESC, board_topic_date DESC


แบบนี้จะเกิดปัญหาดังนี้ครับ
- เราจะได้ member_name ของ tb_board_comment ได้อย่างไร เพราะไม่สามารถ INNER JOIN tb_member ON member_id=comment_by อีกได้
- ข้อมูลที่ SELECT มานั้น ในกรณีมีจำนวน N topic มีจำนวน M comment จะได้จำนวนแถว ที่ query เท่ากับ N topic คูณ M comment
ซึ่งจริงๆต้องการแค่ N topic กับ 1 comment ล่าสุด
อาจแก้ปัญหาโดยใช้ GROUP BY เข้ามาช่วยเป็นแบบนี้

Code
SELECT topic_title ,topic_date ,topic_view ,member_name AS topic_by_name ,comment_date FROM tb_board_topic
INNER JOIN tb_member ON member_id=topic_by
LEFT OUTER JOIN tb_board_comment ON comment_topic=topic_id
GROUP BY topic_id
ORDER BY board_comment_date DESC, board_topic_date DESC


ผลลัพธ์ ที่ได้จะเป็น N topic กับ 1 comment
แต่ comment ที่ได้จะไม่เรียงตาม comment_date DESC

ตอนนี้เท่าที่คิดได้คือ ใช้ SELECT ซ้อน SELECT จะเป็นแบบนี้ครับ

Code
SELECT
topic_title,
topic_date,
topic_view,
member_name AS topic_by_name,
(SELECT member_name FROM tb_comment INNER JOIN tb_member ON member_id=comment_by WHERE comment_topic=topic_id ORDER BY comment_date DESC LIMIT 0,1) AS comment_by_name ,
(SELECT comment_date FROM tb_comment INNER JOIN tb_member ON member_id=comment_by WHERE comment_topic=topic_id ORDER BY comment_date DESC LIMIT 0,1) AS comment_date
FROM tb_topic
INNER JOIN tb_member ON topic_by=member_id
ORDER BY topic_date DESC


เท่าที่สังเกตคือ การ SELECT ซ้อน ตรง Field นั้น จะเลือกได้แค่ 1 Field ไม่สามารถเลือกมา 2 Field ในการ SELECT ได้

Code
SELECT member_name,comment_date FROM tb_comment INNER JOIN tb_member ON member_id=comment_by WHERE comment_topic=topic_id ORDER BY comment_date DESC LIMIT 0,1


จะมีวิธีการ SELECT แบบอื่นอีกหรือไม่ครับ
หรือว่าการ SELECT -> Loop - > SELECT ตามแบบวิธีแรกสุดนั้นดีสุดแล้วครับ

เพราะที่คิดไว้คือ ถ้ายิ่ง query น้อยครั้ง ก็ยิ่งดีใช่หรือไม่
อยากให้ช่วยแชร์ความรู้กันทีครับ
พวกเว็บบอร์ดใหญ่ๆ เค้าทำกันอย่างไร
พวกบอร์ด สำเร็จรูป ผมก็ไม่เคยไปหาแกะโค้ด มาดู



Tag : PHP, MySQL







Move To Hilight (Stock) 
Send To Friend.Bookmark.
Date : 2012-01-26 11:22:40 By : Likito View : 2733 Reply : 5
 

 

No. 1



โพสกระทู้ ( 74,058 )
บทความ ( 838 )

สมาชิกที่ใส่เสื้อไทยครีเอท

สถานะออฟไลน์
Twitter Facebook

บอกเทคนิคแล้วกันครับ อันที่จริงบางครั้งหลักการจัดเก็บและการ Query ที่ถุกต้องก็ไม่สามารถแก้ไขปัญหาจากการใช้งานจริงได้ครับ จากประสบการณ์ เว็บพวก Board ใหญ ๆ หรือ Club ต่าง ๆ ที่ใช้พวก Board SMS / PHPBB หรืออื่น ๆ ที่มีคนใช้จำนวนมาก หลักหมื่นขึ้น จากประสบการณ์ที่ได้เจอมาเว็บพวกนี้จะใช้ Resource สูงมาก ส่วนมากเว็บใหญ่ ๆ พวกนี้จะตั้อง Server เอง หรือใช้ VPS รันเว็บเดียวไปเลย เพราะเมื่อ Data มีขนาดใหญ่ Process พวกนี้จะทำงานหนักมาก

แต่ถ้าคุณเขียนเว็บบอร์ดเอง ให้เขียนโดยการ Query ให้น้อยที่สุดเท่าที่เป็นไปได้ พวกจำนวนคนตอบ หรือ ตอบล่าสุด ให้จัดเก็บไว้ในตาราง tb_board_topic ไปเลยครับ โดยเมื่อมีคนตอบใหม่ ก็ไป update จำนวนคนตอบ หรือ คนตอบล่าสุดที่ table นี้เลย เพราะตอนที่ใช้งาน จะได้ไม่ต้องไป JOIN หรือ Query รวมกับ Table อื่น ๆ ครับ วิธีนี้ช่วยลดการทำงานได้เป็นอย่างดี

ซึ่งตอนนี้ผมก็ใช้วิธีนี้เช่นเดียวกันครับ

ต่อให้เขียน Query ดีและเร็ว แต่เมื่อ Process เยอะมาก มันจะเกิดการรอคิว และซึ่งเมื่อรอคิวเยอะขึ้น ๆ Server จะโหลดขึ้นมาทันที และเกิดการแฮ้งค์ในที่สุดครับ






แสดงความคิดเห็นโดยอ้างถึง ความคิดเห็นนี้
Date : 2012-01-26 11:37:11 By : webmaster
 


 

No. 2



โพสกระทู้ ( 335 )
บทความ ( 0 )



สถานะออฟไลน์


ขอบคุณครับ ^^

เหมือนเป็นวิชามาร อย่างหนึ่งเลยก็ได้

มัวแต่นึกถึงในตำราที่เรียน หลักการ normalization
ซึ่งเอาเข้าจริงการนำไปใช้นั้นก็ไม่ได้ดีไปซะทุกอย่าง



เคยทำเกี่ยวกับจัดเก็บผลคะแนนประเมิน
โดยที่ ข้อมูลนั้นมีเป็นหมื่นๆ เมื่อนำมาทำการ หารค่าเฉลี่ย ตามสูตรหลายๆสูตรแล้ว ถ้าไม่เก็บข้อมูลไว้ให้พร้อมใช้เลย
ระบบก็จะอืดเหมือนกัน
แสดงความคิดเห็นโดยอ้างถึง ความคิดเห็นนี้
Date : 2012-01-26 13:12:36 By : Likito
 

 

No. 3



โพสกระทู้ ( 74,058 )
บทความ ( 838 )

สมาชิกที่ใส่เสื้อไทยครีเอท

สถานะออฟไลน์
Twitter Facebook

ถ้า Data มีขนาดใหญ่มากขึ้น วิธีที่จะช่วยได้ก็น่าจะเป็นการ แยกออกหลาย ๆ Table ครับ ช่วยได้เยอะครับ
แสดงความคิดเห็นโดยอ้างถึง ความคิดเห็นนี้
Date : 2012-01-26 17:23:38 By : webmaster
 


 

No. 4



โพสกระทู้ ( 335 )
บทความ ( 0 )



สถานะออฟไลน์


ตอบความคิดเห็นที่ : 3 เขียนโดย : webmaster เมื่อวันที่ 2012-01-26 17:23:38
รายละเอียดของการตอบ ::
ถ้าแยกออก หลายๆ table นี่่คือแยกอย่างไรครับ
ทำเป็น table ที่มี field เหมือนกัน ประมาณนี้หรือเปล่าครับ
tb_board_topic_1
tb_board_topic_2
tb_board_topic_3
tb_board_topic_4
....
...
...

โดยที่ tb_board_topic_1 จะเก็บข้อมูลจาก id ที่ 1 ถึง 100000
ส่วน tb_board_topic_2 จะเก็บตั้งแต่ 100001 ถึง 200000
ประมาณนี้หรือเปล่า เก็บ แค่ table ละประมาณ 1 แสน row

ถ้าใช่ แล้วมีหลักการ อย่างไรตอนค้นหาข้อมูล เพื่อนำมาแสดงครับ
หรือแค่ใช้วิธี เขียน code เพิ่ม ถ้าค้นหาข้อมูลจาก id แล้วถ้า ช่วงของ id อยู่ในช่วงไหน ให้ไปดึง ข้อมูลจาก table นั้นๆ

แต่ถ้าเกิดว่า เป็นการ ค้นหา ข้อมูล จาก field อื่นๆ แบบนี้จะทำอย่างไร ถ้า ข้อมูลในลักษณะคล้ายๆกันอยู่ มันทุก table เลย

แสดงความคิดเห็นโดยอ้างถึง ความคิดเห็นนี้
Date : 2012-01-27 09:02:37 By : Likito
 


 

No. 5



โพสกระทู้ ( 74,058 )
บทความ ( 838 )

สมาชิกที่ใส่เสื้อไทยครีเอท

สถานะออฟไลน์
Twitter Facebook

ถ้าค้นหาก็ใช้การ UNION กันได้ครับ
แสดงความคิดเห็นโดยอ้างถึง ความคิดเห็นนี้
Date : 2012-01-27 09:14:13 By : webmaster
 

   

ค้นหาข้อมูล


   
 

แสดงความคิดเห็น
Re : ปรึกษาเรื่องคำสั่ง SQL SELECT เพื่อลดการ query และ เพิ่มประสิทธิภาพมากที่สุดครับ
 
 
รายละเอียด
 
ตัวหนา ตัวเอียง ตัวขีดเส้นใต้ ตัวมีขีดกลาง| ตัวเรืองแสง ตัวมีเงา ตัวอักษรวิ่ง| จัดย่อหน้าอิสระ จัดย่อหน้าชิดซ้าย จัดย่อหน้ากึ่งกลาง จัดย่อหน้าชิดขวา| เส้นขวาง| ขนาดตัวอักษร แบบตัวอักษร
ใส่แฟลช ใส่รูป ใส่ไฮเปอร์ลิ้งค์ ใส่อีเมล์ ใส่ลิ้งค์ FTP| ใส่แถวของตาราง ใส่คอลัมน์ตาราง| ตัวยก ตัวห้อย ตัวพิมพ์ดีด| ใส่โค้ด ใส่การอ้างถึงคำพูด| ใส่ลีสต์
smiley for :lol: smiley for :ken: smiley for :D smiley for :) smiley for ;) smiley for :eek: smiley for :geek: smiley for :roll: smiley for :erm: smiley for :cool: smiley for :blank: smiley for :idea: smiley for :ehh: smiley for :aargh: smiley for :evil:
Insert PHP Code
Insert ASP Code
Insert VB.NET Code Insert C#.NET Code Insert JavaScript Code Insert C#.NET Code
Insert Java Code
Insert Android Code
Insert Objective-C Code
Insert XML Code
Insert SQL Code
Insert Code
เพื่อความเรียบร้อยของข้อความ ควรจัดรูปแบบให้พอดีกับขนาดของหน้าจอ เพื่อง่ายต่อการอ่านและสบายตา และตรวจสอบภาษาไทยให้ถูกต้อง

อัพโหลดแทรกรูปภาพ

Notice

เพื่อความปลอดภัยของเว็บบอร์ด ไม่อนุญาติให้แทรก แท็ก [img]....[/img] โดยการอัพโหลดไฟล์รูปจากที่อื่น เช่นเว็บไซต์ ฟรีอัพโหลดต่าง ๆ
อัพโหลดแทรกรูปภาพ ให้ใช้บริการอัพโหลดไฟล์ของไทยครีเอท และตัดรูปภาพให้พอดีกับสกรีน เพื่อความโหลดเร็วและไฟล์ไม่ถูกลบทิ้ง

   
  เพื่อความปลอดภัยและการตรวจสอบ กระทู้ที่แทรกไฟล์อัพโหลดไฟล์จากที่อื่น อาจจะถูกลบทิ้ง
 
โดย
อีเมล์
บวกค่าให้ถูก
<= ตัวเลขฮินดูอารบิก เช่น 123 (หรือล็อกอินเข้าระบบสมาชิกเพื่อไม่ต้องกรอก)







Exchange: นำเข้าสินค้าจากจีน, Taobao, เฟอร์นิเจอร์, ของพรีเมี่ยม, ร่ม, ปากกา, power bank, แฟลชไดร์ฟ, กระบอกน้ำ

Load balance : Server 05
ThaiCreate.Com Logo
© www.ThaiCreate.Com. 2003-2024 All Rights Reserved.
ไทยครีเอทบริการ จัดทำดูแลแก้ไข Web Application ทุกรูปแบบ (PHP, .Net Application, VB.Net, C#)
[Conditions Privacy Statement] ติดต่อโฆษณา 081-987-6107 อัตราราคา คลิกที่นี่