คำสั่ง MERGE

คำสั่ง MERGE
ผู้อ่านหลายคนอาจรู้จักคำสั่ง MERGE มาบ้าง แต่ยังมีผู้อ่านอีกมากที่ไม่ทราบว่าคำสั่ง MERGE คืออะไรอันที่จริงแล้วคำสั่ง MERGE เป็นคำสั่งที่มีอยู่ในมาตรฐาน ANSI มาตั้งแต่ปี 2003 และเพิ่มเติมส่วนขยายอีกครั้งในมาตรฐาน ANSI ปี 2008
สำหรับ Microsoft SQL Server นั้นมีใช้มาตั้งแต่ Microsoft SQL Server 2008
(ถึงแม้คำสั่งนี้จะเป็นมาตรฐาน และมีใช้ในหลายๆ ระบบจัดการฐานข้อมูลนอกเหนือจาก Microsoft SQL Server เช่น Oracle แต่ก็แปลกที่ปัจจุบันก็ยังไม่สามารถใช้ส่งข้อมูลข้ามกันไปมาได้ )
ผู้เขียนมีความชอบคำสั่งนี้เป็นพิเศษ โดยเฉพาะเมื่อนำไปใช้ในการ ETL ข้อมูล (Extract, Transform และ Load) แบบ Incremental Update จากแหล่งข้อมูลต้นทางไปยังปลายทาง
คำสั่ง MERGE เสมือนนำเอาคำสั่ง INSERT, UPDATE และ DELETE มารวมไว้ด้วยกัน โดยประกาศผ่านคำสั่ง MERGE เพียงคำสั่งเดียว
ก่อนหน้าที่จะมีคำสั่ง MERGE เวลาต้องการทำ Incremental Update จากแหล่งข้อมูลต้นทางไปยังปลายทาง เราสามารถทำได้โดยการ JOIN ตารางต้นทางกับตารางปลายทาง
ผู้เขียนจะแสดงวิธีดังกล่าวอย่างง่ายดังต่อไปนี้
ก่อนอื่นผู้เขียนได้สร้างฐานข้อมูลต้นทางชื่อ TestDB_SRC และ ฐานข้อมูลปลายทางชื่อ TestDB_DST
โดยทั้งสองฐานข้อมูลมีตาราง dbo.Employees อยู่

ผู้เขียนได้จำลองให้ตาราง dbo.Employees ในฐานข้อมูลต้นทางมีข้อมูลอยู่ 100 แถวข้อมูล
ส่วนตาราง dbo.Employees ในฐานข้อมูลปลายทางนั้นไม่มีข้อมูลอยู่เลย
เมื่อทำการ SELECT ข้อมูลจากทั้งสองตารางให้ผลลัพธ์ดังนี้
ส่วนตาราง dbo.Employees ในฐานข้อมูลปลายทางนั้นไม่มีข้อมูลอยู่เลย
เมื่อทำการ SELECT ข้อมูลจากทั้งสองตารางให้ผลลัพธ์ดังนี้

กรณี INSERT
สำหรับการ INSERT ข้อมูลจากตารางต้นทางไปยังตารางปลายทางสามารถทำได้โดยอาศัย LEFT OUTER JOIN ดังคำสั่งต่อไปนี้

เมื่อผู้เขียนประมวลผลคำสั่งข้างบนนี้ผลลัพธ์ที่ได้ก็คือการ INSERT ทุกแถวข้อมูลจากตารางต้นทางไปยังตารางปลายทาง
เมื่อมีข้อมูลทั้งในตารางต้นทางและตารางปลายทางเท่ากันแล้ว
ผู้เขียนทดลองประมวลผลคำสั่งข้างบนนี้อีกหลาย ๆ ครั้งก็พบว่าจะไม่สามารถ INSERT ข้อมูลเพิ่มได้อีก
ผู้เขียนจึงนับแถวข้อมูลจากตารางต้นทางและตารางปลายทางอีกครั้ง
เมื่อมีข้อมูลทั้งในตารางต้นทางและตารางปลายทางเท่ากันแล้ว
ผู้เขียนทดลองประมวลผลคำสั่งข้างบนนี้อีกหลาย ๆ ครั้งก็พบว่าจะไม่สามารถ INSERT ข้อมูลเพิ่มได้อีก
ผู้เขียนจึงนับแถวข้อมูลจากตารางต้นทางและตารางปลายทางอีกครั้ง

ผลลัพธ์ที่ได้คือ

จากนั้นผู้เขียนได้เพิ่มแถวข้อมูลลงไปในตารางต้นทางอีก 50 แถว
แล้วนับแถวข้อมูลของทั้งสองตารางอีกครั้งผลลัพธ์ที่ได้ คือ

เมื่อได้แถวข้อมูลในตารางต้นทางเพิ่มขึ้นมาอีก 50 แถว ผู้เขียนได้ประมวลผลคำสั่งข้างบนอีกครั้ง
พบว่าได้มีการปรับจำนวนแถวข้อมูลในตารางปลายทางดังแสดง

สรุปว่าการใช้ LEFT OUTER JOIN ระหว่างตารางต้นทาง กับ ตารางให้ผลลัพธ์ตามวัตถุประสงค์ในการเพิ่มแถวข้อมูลในตารางปลายทาง
หากมีแถวข้อมูลเพิ่มในตารางต้นทาง
กรณี UPDATE
ผู้เขียนได้จำลองว่ามีการปรับปรุงข้อมูลในตารางต้นทาง โดยเมื่อปรับปรุงข้อมูลในคอลัมน์ใดก็ตาม ให้ปรับปรุงคอลัมน์ ModifiedDate เพื่อทำเครื่องหมายว่าแถวข้อมูลถูกปรับปรุงในเวลาใด

จากคำสั่งข้างบนผู้เขียนได้ปรับปรุงแถวข้อมูล 3 แถวในตารางต้นทาง โดยการปรับปรุงคอลัมน์ MaritalStatus ให้มีค่าเป็น ‘M’
จากนั้นบันทึกเวลาที่ทำการปรับปรุงลงในคอลัมน์ MofifiedDate ของทุกแถวข้อมูลที่ทำการปรับปรุง
สำหรับกรณี UPDATE นั้น เราสามารถใช้ INNER JOIN ระหว่าง ตารางต้นทาง และ ตารางปลายทาง ในคำสั่ง UPDATE ดังคำสั่งต่อไปนี้

เมื่อผู้เขียนประมวลผลคำสั่งข้างบนนี้ผลลัพธ์ที่ได้ก็คือ การ UPDATE แถวข้อมูลที่มีการปรับปรุงในตารางต้นทางไปยังตารางปลายทาง
ซึ่งมีจำนวน 3 แถวข้อมูล ผลลัพธ์ที่ได้คือ

และ เมื่อผู้เขียนพยายามประมวลผลคำสั่งข้างบนอีกสักกี่ครั้งผลลัพธ์ที่ได้ คือ

ตราบใดที่ไม่มีการปรับปรุงตารางต้นทางเพิ่มอีก จะประมวลผลคำสั่งข้างบนกี่ครั้งก็จะไม่มีการปรับปรุงแถวข้อมูลในตารางปลายทาง
กรณี DELETE
ผู้เขียนได้จำลองว่ามีการลบแถวข้อมูลออกจากตารางต้นทาง ด้วยคำสั่งต่อไปนี้
เป็นการลบแถวข้อมูลในตารางต้นทางเฉพาะที่เป็นเพศหญิง ผลลัพธ์ที่ได้ คือ

สำหรับกรณี DELETE นั้นเราสามารถใช้ LEFT OUTER JOIN ระหว่างตารางต้นทาง และ ตารางปลายทาง
โดยตั้งให้ตารางปลายทางอยู่ด้านซ้ายมือ ดังคำสั่งต่อไปนี้

เมื่อผู้เขียนประมวลผลคำสั่งข้างบน ผลลัพธ์ที่ได้คือ

จำนวนแถวที่ถูกลบในตารางปลายทางเท่ากับที่ผู้เขียนลบในตารางต้นทาง และ เมื่อผู้เขียนประมวลผลคำสั่งข้างบนซ้ำอีกสักกี่ครั้ง
ก็จะไม่มีผลอะไร จนกว่าจะมีการลบแถวข้อมูลในตารางต้นทางเพิ่ม
คำสั่ง MERGE
จะเห็นว่าหากต้องการปรับปรุงข้อมูลในตารางปลายทางด้วยข้อมูลในตารางต้นทางโดยใช้คำสั่ง INSERT, UPDATE และ DELETE ทำได้ไม่ยากอะไร
แต่จะดีกว่าไหม หากมีคำสั่งที่ประกาศคำสั่งเดียวทำทั้งการเพิ่มข้อมูล ปรับปรุงข้อมูล และ ลบข้อมูลในตารางปลายทางในคราวเดียวเลย
ผู้เขียนได้จำลองตารางต้นทาง และตารางปลายทางเหมือนกับตัวอย่างก่อนหน้า

โดยให้ตาราง dbo.Employees ในฐานข้อมูลต้นทางมีข้อมูลอยู่ 100 แถวข้อมูล
ส่วนตาราง dbo.Employees ในฐานข้อมูลปลายทางนั้นไม่มีข้อมูลอยู่เลย
เมื่อทำการ SELECT ข้อมูลจากทั้งสองตารางให้ผลลัพธ์ดังนี้

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

หลังประโยค MERGE INTO จะระบุ ตารางปลายทาง ในที่นี้คือตาราง TestDB_DST.dbo.Employees
ส่วนหลังประโยค USING เป็นการระบุนิพจน์ตารางที่ทำหน้าที่เป็นตารางต้นทาง ในที่นี้คือตาราง TestDB_SRC.dbo.Employees
หลังประโยค ON จะเป็นเงื่อนไขในการระบุแถวข้อมูลของทั้งตารางต้นทางและตารางปลายทาง
หลังประโยค WHEN MATCHED หมายถึง หากมีแถวข้อมูลทั้งในตารางต้นทางและตารางปลายทาง ก็จะทำการ UPDATE หรือคำสั่งอื่นใดก็ได้
หลัง WHEN NOT MATCHED (ย่อมาจาก WHEN NOT MATCHED BY TARGET) หมายถึงมีแถวข้อมูลในตารางต้นทาง แต่ไม่มีแถวข้อมูลในตารางปลายทาง ก็จะทำการนำข้อมูลจากตารางต้นทางไป INSERT หรือ คำสั่งอื่นใดก็แล้วแต่
ในส่วนสุดท้าย WHEN NOT MATCHED BY SOURCE หมายถึง มีแถวข้อมูลในตารางปลายทาง แต่ไม่มีแถวข้อมูลในตารางต้นทาง อาจจะทำการ DELETE เพื่อลบข้อมูลจากตารางปลายทาง แต่ก็อีกเช่นเดียวกันอาจเป็นคำสั่งอื่นใดก็ได้
ผู้เขียนได้ทดลองประมวลผลคำสั่งข้างบน และ พบว่ามีการเพิ่มเติมแถวข้อมูลลงในตารางปลายทาง ตามจำนวนแถวข้อมูลที่เพิ่มเข้าไปในตารางต้นทาง
และ หากมีการปรับปรุงข้อมูลในแถวข้อมูลใดในตารางต้นทาง เมื่อประมวลผลคำสั่งข้างบนก็จะพบว่ามีการปรับปรุงในตารางปลายทางตามมา
สุดท้ายเมื่อทำการลบแถวข้อมูลในตารางต้นทาง เมื่อประมวลผลคำสั่งข้างบนก็จะทำการลบแถวข้อมูลในตารางปลายทางตามมา
หวังเป็นอย่างยิ่งว่าผู้อ่านจะได้ประโยชน์จากการนำคำสั่ง MERGE ไปใช้
แต่อย่างที่ผู้เขียนได้กล่าวไว้ตอนต้นว่าปัจจุบันยังไม่สามารถกำหนดให้ SOURCE และ TARGET อยู่คนละ Platform ได้
เช่น SOURCE เป็น Microsoft SQL Server และ TARGET เป็น Oracle ยังไม่สามารถทำได้ครั้บ
ส่วนหลังประโยค USING เป็นการระบุนิพจน์ตารางที่ทำหน้าที่เป็นตารางต้นทาง ในที่นี้คือตาราง TestDB_SRC.dbo.Employees
หลังประโยค ON จะเป็นเงื่อนไขในการระบุแถวข้อมูลของทั้งตารางต้นทางและตารางปลายทาง
หลังประโยค WHEN MATCHED หมายถึง หากมีแถวข้อมูลทั้งในตารางต้นทางและตารางปลายทาง ก็จะทำการ UPDATE หรือคำสั่งอื่นใดก็ได้
หลัง WHEN NOT MATCHED (ย่อมาจาก WHEN NOT MATCHED BY TARGET) หมายถึงมีแถวข้อมูลในตารางต้นทาง แต่ไม่มีแถวข้อมูลในตารางปลายทาง ก็จะทำการนำข้อมูลจากตารางต้นทางไป INSERT หรือ คำสั่งอื่นใดก็แล้วแต่
ในส่วนสุดท้าย WHEN NOT MATCHED BY SOURCE หมายถึง มีแถวข้อมูลในตารางปลายทาง แต่ไม่มีแถวข้อมูลในตารางต้นทาง อาจจะทำการ DELETE เพื่อลบข้อมูลจากตารางปลายทาง แต่ก็อีกเช่นเดียวกันอาจเป็นคำสั่งอื่นใดก็ได้
ผู้เขียนได้ทดลองประมวลผลคำสั่งข้างบน และ พบว่ามีการเพิ่มเติมแถวข้อมูลลงในตารางปลายทาง ตามจำนวนแถวข้อมูลที่เพิ่มเข้าไปในตารางต้นทาง
และ หากมีการปรับปรุงข้อมูลในแถวข้อมูลใดในตารางต้นทาง เมื่อประมวลผลคำสั่งข้างบนก็จะพบว่ามีการปรับปรุงในตารางปลายทางตามมา
สุดท้ายเมื่อทำการลบแถวข้อมูลในตารางต้นทาง เมื่อประมวลผลคำสั่งข้างบนก็จะทำการลบแถวข้อมูลในตารางปลายทางตามมา
หวังเป็นอย่างยิ่งว่าผู้อ่านจะได้ประโยชน์จากการนำคำสั่ง MERGE ไปใช้
แต่อย่างที่ผู้เขียนได้กล่าวไว้ตอนต้นว่าปัจจุบันยังไม่สามารถกำหนดให้ SOURCE และ TARGET อยู่คนละ Platform ได้
เช่น SOURCE เป็น Microsoft SQL Server และ TARGET เป็น Oracle ยังไม่สามารถทำได้ครั้บ
บทความโดย
อาจารย์ภัคพงศ์ กฤตวัฒน์
วิทยากรดูแลและออกแบบหลักสูตร
กลุ่มวิชา SQL Server/Window Server