ในโลกของการพัฒนาซอฟต์แวร์ที่ต้องมีการสร้างรายงาน (Reporting) หรือเอกสารที่มีโครงสร้างสูง การควบคุมองค์ประกอบด้าน Layout เช่น หัวกระดาษ ท้ายกระดาษ และเลขหน้า ให้ปรากฏอย่างถูกต้องและเป็นระบบ ถือเป็นความท้าทายทางสถาปัตยกรรมข้อมูลที่สำคัญยิ่ง แม้ว่า OpenXML SDK จะใช้ในการจัดการไฟล์ Word โดยตรง แต่ในมุมมองของ System Architect ที่ต้องสร้างแหล่งข้อมูล (Source of Truth) สำหรับเอกสารเหล่านั้น การเข้าใจหลักการทำงานเชิงโครงสร้างนี้ และจำลองมันด้วยพลังของฐานข้อมูลอย่าง PostgreSQL จึงเป็นสิ่งจำเป็น
แนวคิด: การจำลองโครงสร้างเอกสารในระดับ Database
โดยปกติแล้ว หัวกระดาษและท้ายกระดาษไม่ได้เป็นเพียงแค่ข้อความ แต่ถูกผูกติดกับ Section Properties ของเอกสาร ซึ่งหมายถึงการที่ข้อมูลต้องมีการทำซ้ำ (Repetition) และการนับลำดับ (Sequencing) ที่แม่นยำ การนำหลักการนี้มาใช้ใน PostgreSQL เราจะไม่สร้างไฟล์ Word โดยตรง แต่เราจะสร้าง ‘Metadata’ หรือ ‘Logic Flow’ ที่กำหนดว่าข้อมูลใดควรปรากฏเมื่อไหร่และอย่างไร
องค์ประกอบสำคัญที่ต้องจำลองด้วย SQL
- Sectioning Logic: การแบ่งเอกสารออกเป็นส่วนๆ (เช่น หน้าปก, บทนำ, เนื้อหาหลัก) ซึ่งใน PostgreSQL สามารถใช้ Common Table Expressions (CTEs) หรือการจัดกลุ่มข้อมูลตามลำดับเหตุการณ์ได้
- Sequential Numbering (Pagination): การสร้างเลขหน้าหรือหมายเลขบทที่ต้องเพิ่มขึ้นทีละ 1 อย่างต่อเนื่องและถูกต้อง แม้ว่าจะมีการข้ามส่วนใดส่วนส่วนหนึ่งไปก็ตาม ซึ่งเหมาะกับการใช้ฟังก์ชัน
generate_series() - Repeating Content (Header/Footer): การกำหนดชุดข้อมูลที่เป็นค่าคงที่หรือคำนวณจาก Context ปัจจุบัน เพื่อให้แสดงซ้ำในทุกๆ ‘หน้า’ หรือ ‘ส่วน’ ที่ถูกประมวลผล
การใช้ PostgreSQL ทำให้เราสามารถจัดการความสัมพันธ์เชิงลำดับ (Temporal and Sequential Relationship) ของข้อมูลเหล่านี้ได้อย่างมีประสิทธิภาพสูงกว่าการจัดเก็บเป็นเพียงตารางข้อความธรรมดา
Case Study: การสร้างเลขหน้าและส่วนหัวด้วย Recursive CTE
เราสามารถใช้เทคนิค Recursive Common Table Expression (CTE) เพื่อจำลองกระบวนการไหลของเอกสารที่ซับซ้อน ซึ่งรวมถึงการนับเลขหน้าและการกำหนดค่า Header/Footer ที่เปลี่ยนแปลงไปตาม Section ได้
SQL
WITH RECURSIVE DocumentFlow AS (
-- Anchor Member: เริ่มต้นที่ส่วนแรก (Page 1)
SELECT
1::integer AS page_number,
'Section A: Cover Page'::text AS section_name,
'Document Start'::text AS header_content,
NULL::jsonb AS footer_data, -- หน้าปกอาจไม่มีเลขหน้า
1 AS start_page
UNION ALL
-- Recursive Member: สร้างหน้าถัดไป (Page 2, 3, ...)
SELECT
df.page_number + 1,
CASE WHEN df.page_number < 5 THEN 'Section A' ELSE 'Section B: Main Body' END,
'Report Title - Page ' || (df.page_number + 1)::text,
jsonb_build_object('date', CURRENT_DATE, 'chapter', 2),
CASE WHEN df.page_number < 5 THEN 1 ELSE df.start_page END
FROM DocumentFlow df
WHERE df.page_number < 10 --จำลองถึงหน้า 10
)
SELECT * FROM DocumentFlow;
คำอธิบายโค้ด:
WITH RECURSIVE DocumentFlow AS (...): ใช้ CTE เพื่อสร้างชุดข้อมูลที่วนซ้ำ (Recursive) ซึ่งจำลองการไหลของหน้ากระดาษpage_number: ถูกเพิ่มค่าขึ้นเรื่อยๆ ในแต่ละรอบของการทำซ้ำ ทำให้เกิดลำดับเลขหน้าที่ถูกต้องheader_content: แสดงถึงเนื้อหาหัวกระดาษที่ถูกคำนวณแบบ Dynamic (เช่น ‘Report Title – Page X’) ซึ่งสามารถเปลี่ยนตามเงื่อนไขของ Section ได้jsonb: ใช้เก็บข้อมูล Metadata ของส่วนท้ายกระดาษ (Footer) ที่มีความยืดหยุ่นสูง เช่น วันที่หรือหมายเลขบท
สรุปและข้อได้เปรียบเชิงสถาปัตยกรรม
การใช้ PostgreSQL ในลักษณะนี้ ไม่เพียงแต่ช่วยให้เราสร้างลำดับตัวเลข (Pagination) ได้อย่างแม่นยำ แต่ยังเป็นการพิสูจน์ว่าโครงสร้างเอกสารที่ซับซ้อนสามารถถูกควบคุมด้วย Logic ของฐานข้อมูลได้ทั้งหมด การเปลี่ยนจากการเขียนโค้ดเพื่อจัดการ Layout ไปสู่การกำหนด Rule-Based Data Flow ทำให้ระบบของเรามีความทนทาน (Robustness) และง่ายต่อการบำรุงรักษาในระยะยาว นี่คือหัวใจสำคัญของการเป็น Senior Architect ที่มองเห็นโครงสร้างของข้อมูลที่อยู่เบื้องหลังหน้าตาของแอปพลิเคชัน