PL/pgSQL คืออะไรและทำไมต้องรู้จัก
เวลาเราเขียนโปรแกรมติดต่อกับฐานข้อมูล ส่วนใหญ่เราจะสั่งงานผ่านการ Query (การส่งคำสั่งไปถามหรือดึงข้อมูล) ทีละคำสั่ง แต่ในงานจริงเรามักต้องทำขั้นตอนเดิมซ้ำ ๆ หลายบรรทัดต่อกัน การส่งคำสั่งเหล่านั้นจากคอมพิวเตอร์ของเราไปที่ฐานข้อมูลบ่อย ๆ จะทำให้เสียเวลาในการเชื่อมต่อ
PL/pgSQL (ภาษาที่ใช้เขียนโปรแกรมภายในฐานข้อมูล PostgreSQL) จึงเข้ามาแก้ปัญหานี้ โดยมันยอมให้เราเขียน "สคริปต์" (ชุดคำสั่งที่รันตามลำดับ) เก็บไว้ในฐานข้อมูลเลย เหมือนกับการสอนฐานข้อมูลให้รู้จักทำภารกิจเองโดยที่เราไม่ต้องคอยสั่ง
เปรียบเทียบง่าย ๆ เหมือนคุณสั่งอาหาร ถ้าคุณสั่งทีละอย่างแล้วรอพนักงานเดินไปบอกครัว คุณจะรอนานมาก แต่ถ้าคุณสั่ง "ชุดอาหาร" (Stored Procedure) ครั้งเดียว พนักงานก็จะส่งรายการทั้งหมดให้ครัวทีเดียว Stored Procedure (ชุดคำสั่งที่เก็บไว้ในฐานข้อมูล) จึงช่วยให้การทำงานเร็วและเป็นระเบียบขึ้นมาก
การสร้างฟังก์ชันเบื้องต้นด้วย CREATE FUNCTION
การ CREATE FUNCTION (คำสั่งสร้างฟังก์ชัน) คือการสอนให้ฐานข้อมูลทำงานบางอย่างแล้วส่งค่ากลับมาให้เราเหมือนการคำนวณเลข หรือการดึงค่าบางอย่างออกมาใช้ ฟังก์ชันใน PostgreSQL ต้องระบุประเภทของข้อมูลที่จะรับเข้ามาและประเภทข้อมูลที่จะส่งกลับไปเสมอ
เราต้องใช้คำสั่ง LANGUAGE plpgsql เพื่อบอกฐานข้อมูลว่าเรากำลังเขียนด้วยภาษานี้ และต้องครอบโค้ดด้วย $$ เพื่อบอกจุดเริ่มต้นและจุดจบของคำสั่งทั้งหมด Block (กลุ่มของคำสั่งที่ทำงานร่วมกัน) จะเริ่มต้นด้วย BEGIN และจบด้วย END เสมอ
มือใหม่มักลืมใส่ RETURN (คำสั่งส่งค่ากลับ) ทำให้ฐานข้อมูลแจ้งเตือนข้อผิดพลาด จำไว้ว่าทุกฟังก์ชันที่สร้างขึ้นต้องมีทางออกของข้อมูลเสมอ ไม่อย่างนั้นฐานข้อมูลจะไม่รู้ว่าจะให้ผลลัพธ์อะไรออกมา
-- สร้างฟังก์ชันบวกเลขง่ายๆ
CREATE FUNCTION add_numbers(a integer, b integer)
RETURNS integer AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql;
อธิบายโค้ด:
CREATE FUNCTION add_numbersคือการตั้งชื่อฟังก์ชันว่า add_numbers(a integer, b integer)คือการกำหนดตัวแปรที่จะรับเข้ามา 2 ตัว โดยต้องเป็นตัวเลขจำนวนเต็มทั้งคู่RETURNS integerคือการบอกว่าผลลัพธ์ที่ได้จะเป็นตัวเลขจำนวนเต็มBEGIN ... ENDคือส่วนที่บรรจุคำสั่งหลักของเราRETURN a + bคือการสั่งให้เอาค่า a มาบวกกับค่า b แล้วส่งผลลัพธ์ออกมา
ผลลัพธ์: เมื่อรันคำสั่ง SELECT add_numbers(5, 10); ฐานข้อมูลจะตอบกลับมาว่า 15
การใช้ตัวแปรและการกำหนดค่า
ใน PL/pgSQL เราสามารถประกาศ DECLARE (การจองพื้นที่ในหน่วยความจำเพื่อเก็บข้อมูล) เพื่อเอาไว้พักข้อมูลระหว่างคำนวณได้ ตัวแปรเหล่านี้ช่วยให้เราเขียนโค้ดที่ซับซ้อนขึ้นได้โดยไม่ต้องเขียนคำสั่งยาว ๆ ในบรรทัดเดียว
การกำหนดค่าให้ตัวแปรจะใช้เครื่องหมาย := แทนที่จะเป็นเครื่องหมายเท่ากับปกติ นี่เป็นจุดที่มือใหม่มักพลาดเพราะชินกับการเขียนภาษาอื่น Assignment (การกำหนดค่าให้ตัวแปร) ในภาษานี้ต้องเคร่งครัดเรื่องประเภทข้อมูล ถ้าตัวแปรถูกประกาศว่าเป็นตัวเลข คุณจะเอาข้อความไปใส่ไม่ได้
ลองนึกภาพว่าคุณมีกล่องหลายใบที่เขียนป้ายชื่อไว้หน้ากล่อง การใส่ข้อมูลคือการหยิบของใส่กล่องนั้น ถ้าคุณเอาของผิดประเภทใส่กล่อง ฐานข้อมูลก็จะเตือนคุณทันที การทำแบบนี้ช่วยป้องกันบั๊ก (ข้อผิดพลาดของโปรแกรม) ได้ดีมากในระยะยาว
-- การใช้ตัวแปรพักข้อมูล
CREATE FUNCTION get_discount(price integer)
RETURNS integer AS $$
DECLARE
discount integer := 10; -- ประกาศตัวแปรลดราคา
BEGIN
RETURN price - discount;
END;
$$ LANGUAGE plpgsql;
อธิบายโค้ด:
DECLAREคือส่วนที่ประกาศตัวแปรทั้งหมดที่จะใช้ในฟังก์ชันนี้discount integer := 10คือการสร้างกล่องชื่อ discount เก็บเลข 10 ไว้price - discountคือการนำค่าที่รับเข้ามาลบด้วยค่าในกล่อง discount
ผลลัพธ์: เมื่อรัน SELECT get_discount(100); ผลลัพธ์ที่ได้คือ 90
การควบคุม Loop เพื่อทำงานซ้ำ
บางครั้งเราต้องทำงานเดิมซ้ำหลายรอบ เช่น การเพิ่มข้อมูลหลายบรรทัดในครั้งเดียว LOOP (การวนซ้ำ) จึงเป็นเครื่องมือที่ทรงพลังมาก เราสามารถใช้ FOR หรือ WHILE เพื่อควบคุมจำนวนรอบของการทำงานได้ตามเงื่อนไขที่กำหนด
จุดที่ต้องระวังคือ Infinite Loop (การวนซ้ำที่ไม่รู้จบ) ซึ่งจะทำให้ฐานข้อมูลค้างและทำงานหนักเกินไป คุณต้องแน่ใจเสมอว่าเงื่อนไขในการหยุด Loop ของคุณจะเกิดขึ้นจริงในสักวันหนึ่ง
ให้คิดว่ามันเหมือนการตรวจการบ้านนักเรียน 10 คน ถ้าคุณตรวจทีละคนจนครบ 10 คนแล้วหยุด นี่คือการทำ Loop ถ้าคุณไม่กำหนดว่าจะหยุดที่ใคร คุณก็จะตรวจไปเรื่อย ๆ จนหมดแรง นี่คือสิ่งที่เกิดขึ้นถ้าคุณลืมกำหนดเงื่อนไขหยุดให้ดี
-- ตัวอย่างการวนซ้ำเพื่อแสดงข้อความ
CREATE FUNCTION show_numbers()
RETURNS void AS $$
BEGIN
FOR i IN 1..3 LOOP
RAISE NOTICE 'เลขที่: %', i;
END LOOP;
END;
$$ LANGUAGE plpgsql;
อธิบายโค้ด:
FOR i IN 1..3คือการบอกให้เริ่มนับตั้งแต่ 1 ถึง 3RAISE NOTICEคือคำสั่งให้ฐานข้อมูลแสดงข้อความออกมาที่หน้าจอ%คือสัญลักษณ์ที่จะนำค่าของตัวแปร i มาใส่แทนที่
ผลลัพธ์: ในหน้าต่างข้อความของโปรแกรมจัดการฐานข้อมูล จะเห็นข้อความขึ้นว่า เลขที่: 1, เลขที่: 2, และ เลขที่: 3
รู้จัก Stored Procedures ใน PostgreSQL
CREATE PROCEDURE (คำสั่งสร้างกระบวนการทำงาน) คล้ายกับฟังก์ชัน แต่มีความต่างสำคัญคือมันไม่จำเป็นต้องส่งค่ากลับ (ไม่ใช้ RETURNS) และมันสามารถควบคุม Transaction (การบันทึกข้อมูลแบบเป็นชุดที่ต้องสำเร็จทั้งหมดหรือล้มเหลวทั้งหมด) ได้ด้วยตัวเอง
ใช้ Procedure เมื่อคุณต้องการทำภารกิจที่ซับซ้อน เช่น การอัปเดตยอดเงินในบัญชีสองบัญชีพร้อมกัน ถ้าบัญชีหนึ่งตัดเงินสำเร็จแต่อีกบัญชีไม่ได้รับเงิน ฐานข้อมูลจะสั่งย้อนกลับ (Rollback) ให้เหมือนไม่เคยเกิดอะไรขึ้น เพื่อป้องกันข้อมูลผิดพลาด
มือใหม่มักใช้ฟังก์ชันกับทุกงาน แต่ถ้างานนั้นคือการ "เปลี่ยนค่าในฐานข้อมูล" (เช่น เพิ่ม ลบ แก้ไข) การใช้ Procedure จะเหมาะสมและปลอดภัยกว่ามาก เพราะมันออกแบบมาเพื่อจัดการเรื่องความถูกต้องของข้อมูลโดยเฉพาะ
-- ตัวอย่างการสร้าง Procedure
CREATE PROCEDURE update_data()
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE users SET status = 'active';
-- ทำการเปลี่ยนสถานะผู้ใช้ทุกคนให้เป็น active
END;
$$;
อธิบายโค้ด:
CREATE PROCEDUREคือการสร้างชุดคำสั่งที่เน้นการจัดการข้อมูลUPDATE usersคือคำสั่งแก้ไขข้อมูลในตาราง users- ไม่มี
RETURNSเพราะ Procedure เน้นการทำงาน ไม่เน้นการส่งค่ากลับ
ผลลัพธ์: เมื่อเรียกใช้ด้วย CALL update_data(); ข้อมูลในตาราง users ทั้งหมดจะถูกเปลี่ยนสถานะทันที
สรุป: การนำไปใช้จริงในงานโปรแกรมเมอร์
การเข้าใจ PL/pgSQL ช่วยให้คุณเป็นโปรแกรมเมอร์ที่ทำงานกับข้อมูลได้เก่งขึ้น เพราะคุณสามารถย้ายตรรกะที่ซับซ้อนไปไว้ใกล้กับข้อมูลที่สุด ทำให้แอปพลิเคชันของคุณทำงานได้รวดเร็วและลดภาระของฝั่งเซิร์ฟเวอร์ลงได้มหาศาล
อย่าพยายามเขียนทุกอย่างไว้ในฐานข้อมูล แต่ให้เลือกใช้เมื่อต้องการลดจำนวนการเชื่อมต่อ หรือต้องการความปลอดภัยของข้อมูลระดับสูง ลองฝึกเขียนฟังก์ชันง่าย ๆ เช่น การคำนวณภาษีจากราคาสินค้า หรือการตรวจสอบสถานะสมาชิกก่อนอนุญาตให้เข้าใช้งาน
เริ่มต้นจากการสร้างฟังก์ชันเล็ก ๆ ในโปรเจกต์ของคุณดู ถ้าคุณทำได้คล่อง คุณจะพบว่าฐานข้อมูลไม่ใช่แค่ที่เก็บข้อมูล แต่เป็นเครื่องมือช่วยเขียนโปรแกรมที่ทรงพลังมาก เมื่อคุณเริ่มเขียนโปรเจกต์จริง ให้ลองคิดว่า "ส่วนไหนที่ฐานข้อมูลจัดการเองได้บ้าง" แล้วคุณจะเห็นโอกาสใช้สิ่งเหล่านี้เพิ่มขึ้นเองครับ