Oracle คอลเลกชัน PL/SQL: วาร์เรย์ ซ้อนกัน และจัดทำดัชนีตามตาราง

⚡ สรุปอย่างชาญฉลาด

คอลเลกชันใน PL/SQL คือกลุ่มขององค์ประกอบที่มีชนิดข้อมูลเดียวกันเรียงลำดับ โดยแต่ละองค์ประกอบสามารถเข้าถึงได้ด้วยดัชนี คอลเลกชันมีสามประเภท ได้แก่ Varray, ตารางซ้อน และตารางดัชนี ซึ่งแตกต่างกันในเรื่องขนาดที่คงที่ วิธีการทำงานของดัชนี และความสามารถในการจัดเก็บในฐานข้อมูล

  • 🧺 ความหมาย: ชุดสะสมประกอบด้วยองค์ประกอบประเภทเดียวกันจำนวนมาก โดยแต่ละองค์ประกอบจะถูกระบุด้วยตัวเลขกำกับที่ไม่ซ้ำกัน
  • 📐 วาร์เรย์: ขนาดสูงสุดคงที่ มีความหนาแน่นสูง มีดัชนีเป็นตัวเลข ต้องกำหนดค่าเริ่มต้นก่อนใช้งาน
  • 📚 ตารางซ้อน: ไม่มีข้อจำกัดด้านขนาด สามารถจัดเก็บแบบหนาแน่นหรือแบบเบาบางก็ได้ เก็บไว้ในตารางระบบ และสามารถขยายเพิ่มเติมได้ด้วยคำสั่ง EXTEND
  • 🔑 ตารางดัชนี: ไม่มีข้อจำกัดด้านขนาด ดัชนีสามารถเป็นสตริงหรือจำนวนเต็มลบได้ และจะไม่ถูกจัดเก็บในฐานข้อมูล
  • 🏗️ ผู้สร้าง: Varray และตารางซ้อนจำเป็นต้องมีตัวสร้างที่ระบุอย่างชัดเจนเพื่อเริ่มต้นก่อนที่จะอ้างอิงถึง
  • 🛠️ วิธีการ: คำสั่ง COUNT, EXISTS, FIRST, LAST, EXTEND, TRIM และ DELETE ใช้สำหรับจัดการคอลเลกชัน
  • เป็นกลุ่ม: BULK COLLECT จะรวบรวมข้อมูลลงในคอลเลกชันในขั้นตอนเดียวเพื่อการประมวลผลที่รวดเร็ว

Oracle คอลเลกชัน PL/SQL

คอลเลกชันคืออะไร?

คอลเลกชันคือกลุ่มขององค์ประกอบที่มีลำดับของชนิดข้อมูลเฉพาะอย่างหนึ่ง อาจเป็นคอลเลกชันของชนิดข้อมูลแบบง่ายหรือชนิดข้อมูลที่ซับซ้อน เช่น ชนิดข้อมูลที่ผู้ใช้กำหนดเองหรือชนิดข้อมูลแบบระเบียน

ในชุดข้อมูล แต่ละองค์ประกอบจะถูกระบุด้วยคำที่เรียกว่า a “ตัวห้อย” แต่ละรายการจะได้รับดัชนีเฉพาะที่ไม่ซ้ำกัน และสามารถจัดการหรือดึงข้อมูลได้โดยอ้างอิงจากดัชนีเฉพาะนั้น

คอลเลกชันมีประโยชน์มากที่สุดเมื่อต้องการประมวลผลหรือจัดการข้อมูลจำนวนมากที่มีประเภทเดียวกัน คอลเลกชันสามารถเติมข้อมูลและจัดการได้ทั้งหมดโดยใช้ตัวเลือก 'BULK' ใน Oracle.

ชุดข้อมูลจะถูกจัดประเภทตามโครงสร้าง ดัชนี และวิธีการจัดเก็บ ดังแสดงด้านล่าง:

  • ตารางดัชนี (หรือที่เรียกว่าอาร์เรย์แบบเชื่อมโยง)
  • ตารางที่ซ้อนกัน
  • วาร์เรย์

ในทุกจุด ข้อมูลในชุดข้อมูลสามารถอ้างอิงได้ด้วยคำสามคำ ได้แก่ ชื่อชุดข้อมูล ดัชนี และชื่อฟิลด์หรือคอลัมน์ เช่น “ ( ) คุณจะได้เรียนรู้เกี่ยวกับหมวดหมู่ของสะสมเหล่านี้ในหัวข้อด้านล่าง

ภาพรวมประเภทของคอลเลกชัน

ประเภทการรวบรวมข้อมูลทั้งสามแบบมีข้อดีข้อเสียแตกต่างกัน ตารางด้านล่างแสดงการเปรียบเทียบประเภทการรวบรวมข้อมูลทั้งสามแบบก่อนที่จะอธิบายรายละเอียดในแต่ละแบบ

แง่มุม วาร์เรย์ ตารางซ้อน ดัชนีตามตาราง
ขนาด กำหนดขีดจำกัดสูงสุด ไม่มีขีด จำกัด ไม่มีขีด จำกัด
ห้อย เป็นตัวเลข เป็นตัวเลข จำนวนเต็มหรือสตริง
ความหนาแน่น หนาแน่นเสมอ หนาแน่นหรือเบาบาง มักจะน้อย
บันทึกไว้ในฐานข้อมูล มี (ใบกำกับภาษีเต็มรูปแบบ) มี (ใบกำกับภาษีเต็มรูปแบบ) ไม่
จำเป็นต้องเริ่มต้นใช้งาน มี (ใบกำกับภาษีเต็มรูปแบบ) มี (ใบกำกับภาษีเต็มรูปแบบ) ไม่

วาร์เรย์

Varray คือคอลเลกชันที่มีขนาดคงที่และไม่สามารถเปลี่ยนแปลงได้ ดัชนีของ Varray เป็นค่าตัวเลข คุณสมบัติของ Varray มีดังนี้:

  • ขนาดสูงสุดถูกกำหนดไว้แล้ว
  • เรียงลำดับตามจำนวนสมาชิก โดยเริ่มจากเลขห้อย '1'
  • ประเภทคอลเลกชันนี้มีความหนาแน่นสูงเสมอ เราไม่สามารถลบองค์ประกอบแต่ละรายการในอาร์เรย์ได้ สามารถลบ Varray ทั้งหมดหรือตัดส่วนท้ายออกได้
  • เนื่องจากมีความหนาแน่นสูง จึงมีความยืดหยุ่นน้อยมาก
  • วิธีนี้จะเหมาะสมกว่าเมื่อทราบขนาดของอาร์เรย์และมีการดำเนินการที่คล้ายคลึงกันกับทุกองค์ประกอบ
  • ดัชนีและจำนวนของชุดข้อมูลจะคงที่เสมอ
  • ต้องทำการกำหนดค่าเริ่มต้นก่อนใช้งาน การดำเนินการใดๆ ยกเว้น EXISTS บนคอลเลกชันที่ยังไม่ได้กำหนดค่าเริ่มต้น จะทำให้เกิดข้อผิดพลาด
  • สามารถสร้างเป็นอ็อบเจ็กต์ในฐานข้อมูลที่มองเห็นได้ทั่วทั้งฐานข้อมูล หรือสร้างไว้ภายในโปรแกรมย่อยเพื่อใช้งานเฉพาะในโปรแกรมย่อยนั้นก็ได้

ภาพด้านล่างนี้อธิบายการจัดสรรหน่วยความจำของ Varray (แบบหนาแน่น)

ห้อย 1 2 3 4 5 6 7
ความคุ้มค่า xyz ดฟฟ สเด Cxs วบีซี นู Qwe

ไวยากรณ์สำหรับ VARRAY:

TYPE <type_name> IS VARRAY (<SIZE>) OF <DATA_TYPE>;
  • ในไวยากรณ์ข้างต้น type_name ถูกประกาศเป็น VARRAY ของชนิด 'DATA_TYPE' สำหรับขนาดที่กำหนดไว้ ชนิดข้อมูลอาจเป็นแบบง่ายหรือแบบซับซ้อนก็ได้

ตารางที่ซ้อนกัน

ตารางซ้อน (Nested Table) คือชุดข้อมูลที่ขนาดของอาร์เรย์ไม่คงที่ มีชนิดข้อมูลเป็นตัวเลข (Numeric Subscript Type) ข้อมูลเพิ่มเติมเกี่ยวกับชนิดข้อมูลตารางซ้อน:

  • ตารางซ้อนไม่มีข้อจำกัดขนาดสูงสุด
  • เนื่องจากขีดจำกัดสูงสุดไม่คงที่ จึงจำเป็นต้องขยายหน่วยความจำทุกครั้งก่อนใช้งาน โดยใช้คำสั่ง 'EXTEND'
  • เรียงลำดับตามจำนวนสมาชิก โดยเริ่มจากเลขห้อย '1'
  • ประเภทคอลเลกชันนี้สามารถเป็นได้ทั้งสองแบบ หนาแน่นและเบาบางเราสามารถสร้างมันให้มีความหนาแน่นสูง และยังสามารถลบองค์ประกอบแต่ละส่วนแบบสุ่ม ซึ่งจะทำให้มันมีความหนาแน่นต่ำลงได้
  • ทำให้มีความยืดหยุ่นมากขึ้นในการลบองค์ประกอบในอาร์เรย์
  • ข้อมูลจะถูกจัดเก็บไว้ในตารางฐานข้อมูลที่ระบบสร้างขึ้น และสามารถใช้ในคำสั่งเลือกข้อมูลเพื่อดึงค่าได้
  • ตัวเลขดัชนีและจำนวนนับอาจแตกต่างกันได้
  • ต้องทำการกำหนดค่าเริ่มต้นก่อนใช้งาน การดำเนินการใดๆ ยกเว้น EXISTS บนคอลเลกชันที่ยังไม่ได้กำหนดค่าเริ่มต้น จะทำให้เกิดข้อผิดพลาด
  • สามารถสร้างเป็นอ็อบเจ็กต์ในฐานข้อมูลที่มองเห็นได้ทั่วทั้งฐานข้อมูล หรือสร้างไว้ภายในโปรแกรมย่อยเพื่อใช้งานเฉพาะในโปรแกรมย่อยนั้นก็ได้

ภาพด้านล่างอธิบายการจัดสรรหน่วยความจำของตารางซ้อน (แบบหนาแน่นและแบบเบาบาง) พื้นที่ว่างสำหรับองค์ประกอบแสดงถึงองค์ประกอบแบบเบาบาง

ห้อย 1 2 3 4 5 6 7
ค่า (หนาแน่น) xyz ดฟฟ สเด Cxs วบีซี นู Qwe
ค่า (กระจัดกระจาย) Qwe asd อัฟกานิสถาน asd wer

ไวยากรณ์สำหรับตารางที่ซ้อนกัน:

TYPE <type_name> IS TABLE OF <DATA_TYPE>;
  • ในไวยากรณ์ข้างต้น type_name ถูกประกาศเป็นคอลเลกชันตารางซ้อนของประเภท 'DATA_TYPE' โดยชนิดข้อมูลอาจเป็นแบบง่ายหรือแบบซับซ้อนก็ได้

ดัชนีตามตาราง

ตารางดัชนี (Index-by Table) คือชุดข้อมูลที่ขนาดของอาร์เรย์ไม่คงที่ แตกต่างจากชุดข้อมูลประเภทอื่น ๆ ตรงที่ดัชนีของตารางดัชนีสามารถกำหนดได้โดยผู้ใช้ คุณลักษณะของตารางดัชนีมีดังนี้:

  • ดัชนีสามารถเป็นจำนวนเต็มหรือสตริงก็ได้ ควรระบุประเภทของดัชนีเมื่อสร้างคอลเลกชัน
  • คอลเลกชันเหล่านี้ไม่ได้จัดเก็บตามลำดับ
  • พวกมันจะเบาบางในธรรมชาติอยู่เสมอ
  • ขนาดอาร์เรย์ไม่คงที่
  • ไม่สามารถจัดเก็บไว้ในคอลัมน์ของฐานข้อมูลได้ เนื่องจากถูกสร้างและใช้งานภายในเซสชันนั้นๆ เท่านั้น
  • วิธีนี้ช่วยให้มีความยืดหยุ่นมากขึ้นในการรักษามูลค่าดัชนี
  • ตัวเลขดัชนีอาจเป็นลำดับติดลบได้
  • วิธีการนี้เหมาะสมกว่าสำหรับการใช้งานกับชุดข้อมูลที่มีค่าค่อนข้างน้อยภายในโปรแกรมย่อยเดียวกัน
  • ไม่จำเป็นต้องกำหนดค่าเริ่มต้นก่อนใช้งาน
  • ไม่สามารถสร้างเป็นอ็อบเจ็กต์ในฐานข้อมูลได้ แต่จะสร้างขึ้นภายในโปรแกรมย่อยเท่านั้น
  • ไม่สามารถใช้ BULK COLLECT กับประเภทการรวบรวมข้อมูลนี้ได้ เนื่องจากต้องระบุตัวเลขดัชนีสำหรับแต่ละรายการอย่างชัดเจน

ภาพด้านล่างอธิบายการจัดสรรหน่วยความจำของตารางดัชนี (แบบสปาร์ส) พื้นที่ว่างสำหรับองค์ประกอบแสดงถึงองค์ประกอบแบบสปาร์ส

ตัวห้อย (varchar) FIRST นาปรัง ที่สาม FOURTH ที่ห้า หก เจ็ด
ค่า (กระจัดกระจาย) Qwe asd อัฟกานิสถาน asd wer

ไวยากรณ์สำหรับการสร้างดัชนีตามตาราง:

TYPE <type_name> IS TABLE OF <DATA_TYPE> INDEX BY VARCHAR2 (10);
  • ในไวยากรณ์ข้างต้น type_name ถูกประกาศเป็นคอลเลกชันตารางแบบดัชนีของชนิด 'DATA_TYPE' ตัวแปรดัชนีถูกกำหนดให้เป็นชนิด VARCHAR2 โดยมีขนาดสูงสุด 10

คอนสตรัคเตอร์และแนวคิดการเริ่มต้นในคอลเลกชัน

คอนสตรัคเตอร์คือฟังก์ชันในตัวที่จัดเตรียมโดย Oracle เมธอดเหล่านี้จะมีชื่อเดียวกันกับอ็อบเจ็กต์หรือคอลเลกชัน และจะถูกเรียกใช้งานก่อนเป็นอันดับแรกทุกครั้งที่มีการอ้างอิงถึงอ็อบเจ็กต์หรือคอลเลกชันเป็นครั้งแรกในเซสชัน รายละเอียดสำคัญของคอนสตรัคเตอร์ในบริบทของคอลเลกชันมีดังนี้:

  • สำหรับคอลเลกชัน จำเป็นต้องเรียกใช้คอนสตรัคเตอร์เหล่านี้อย่างชัดเจนเพื่อเริ่มต้นคอลเลกชัน
  • ทั้ง Varray และตารางซ้อนต้องได้รับการเริ่มต้นใช้งานผ่านตัวสร้างเหล่านี้ก่อนที่จะนำไปอ้างอิงในโปรแกรม
  • คอนสตรัคเตอร์จะขยายการจัดสรรหน่วยความจำสำหรับคอลเลกชันโดยปริยาย (ยกเว้น Varray) ดังนั้นจึงสามารถกำหนดค่าตัวแปรให้กับคอลเลกชันได้ด้วย
  • การกำหนดค่าผ่านคอนสตรัคเตอร์จะไม่ทำให้คอลเลกชันนั้นกระจัดกระจายแต่อย่างใด

วิธีการเก็บรวบรวม

Oracle มีฟังก์ชันมากมายที่ใช้ในการจัดการและทำงานกับคอลเลกชัน ฟังก์ชันเหล่านี้จะกำหนดและแก้ไขคุณลักษณะต่างๆ ของคอลเลกชัน ตารางด้านล่างแสดงฟังก์ชันต่างๆ และคำอธิบายของแต่ละฟังก์ชัน

วิธี Descriptไอออน วากยสัมพันธ์
มีอยู่ (n) ส่งคืนค่าบูลีน โดยจะคืนค่า TRUE หากองค์ประกอบที่ n มีอยู่ และคืนค่า FALSE หากไม่พบ สามารถใช้ EXISTS ได้เฉพาะกับคอลเลกชันที่ยังไม่ได้กำหนดค่าเริ่มต้นเท่านั้น .มีอยู่(element_position)
COUNT แสดงจำนวนรวมขององค์ประกอบที่มีอยู่ในคอลเลกชัน .นับ
LIMIT ส่งคืนขนาดสูงสุดของคอลเลกชัน สำหรับ Varray จะส่งคืนขนาดคงที่ สำหรับตารางแบบซ้อนและตารางแบบใช้ดัชนี จะส่งคืนค่า NULL .จำกัด
FIRST ส่งคืนค่าของดัชนีแรกของคอลเลกชัน .อันดับแรก
LAST ส่งคืนค่าดัชนีสุดท้ายของคอลเลกชัน .ล่าสุด
ก่อน (n) ส่งคืนดัชนีก่อนหน้าขององค์ประกอบที่ n หากไม่มี จะส่งคืนค่า NULL .ก่อน(n)
ถัดไป (n) ส่งคืนดัชนีถัดไปขององค์ประกอบที่ n หากไม่มี จะส่งคืนค่า NULL .ถัดไป(n)
ขยาย เพิ่มองค์ประกอบหนึ่งรายการต่อท้ายชุดข้อมูล .ขยาย
ขยาย (n) เพิ่มองค์ประกอบ n ตัวที่ส่วนท้ายของคอลเลกชัน .ขยาย(n)
ขยาย (n,i) เพิ่มสำเนา n ชุดขององค์ประกอบที่ i ต่อท้ายชุดข้อมูล .ขยาย(n,i)
TRIM ลบองค์ประกอบหนึ่งรายการออกจากส่วนท้ายของชุดข้อมูล .ทริม
ตัดแต่ง (n) ลบองค์ประกอบจำนวน n รายการออกจากส่วนท้ายของคอลเลกชัน .ทริม (n)
ลบ ลบองค์ประกอบทั้งหมดออกจากคอลเลกชัน ทำให้คอลเลกชันว่างเปล่า .ลบ
ลบ (n) ลบองค์ประกอบที่ n หากองค์ประกอบที่ n เป็น NULL จะไม่ทำอะไรเลย .ลบ(n)
ลบ (นาที,n) ลบองค์ประกอบในช่วงลำดับที่ m ถึง n ในคอลเลกชัน .DELETE(ม,n)

ตัวอย่างที่ 1: ประเภทเรคอร์ดในระดับซับโปรแกรม

ในตัวอย่างนี้ เราจะเห็นวิธีการเติมข้อมูลลงในคอลเลกชันโดยใช้ 'รวบรวมจำนวนมากและวิธีการอ้างอิงข้อมูลที่รวบรวมไว้

ตัวอย่างการเติมข้อมูลใน PL/SQL ด้วย BULK COLLECT

DECLARE
TYPE emp_det IS RECORD
(
EMP_NO NUMBER,
EMP_NAME VARCHAR2(150),
MANAGER NUMBER,
SALARY NUMBER
);
TYPE emp_det_tbl IS TABLE OF emp_det;
guru99_emp_rec emp_det_tbl:= emp_det_tbl();
BEGIN
INSERT INTO emp (emp_no,emp_name, salary, manager) VALUES (1000,'AAA',25000,1000);
INSERT INTO emp (emp_no,emp_name, salary, manager) VALUES (1001,'XXX',10000,1000);
INSERT INTO emp (emp_no, emp_name, salary, manager) VALUES (1002,'YYY',15000,1000);
INSERT INTO emp (emp_no,emp_name,salary, manager) VALUES (1003,'ZZZ',7500,1000);
COMMIT;
SELECT emp_no,emp_name,manager,salary BULK COLLECT INTO guru99_emp_rec
FROM emp;
dbms_output.put_line ('Employee Detail');
FOR i IN guru99_emp_rec.FIRST..guru99_emp_rec.LAST
LOOP
dbms_output.put_line ('Employee Number: '||guru99_emp_rec(i).emp_no);
dbms_output.put_line ('Employee Name: '||guru99_emp_rec(i).emp_name);
dbms_output.put_line ('Employee Salary:'|| guru99_emp_rec(i).salary);
dbms_output.put_line('Employee Manager Number:'||guru99_emp_rec(i).manager);
dbms_output.put_line('--------------------------------');
END LOOP;
END;
/

Code คำอธิบาย

  • Code บรรทัดที่ 2-8: ประเภทบันทึก ตาราง 'emp_det' ถูกประกาศโดยมีคอลัมน์ emp_no, emp_name, manager และ salary ซึ่งมีชนิดข้อมูลเป็น NUMBER, VARCHAR2, NUMBER และ NUMBER ตามลำดับ
  • Code สาย 9: กำลังสร้างคอลเลกชัน 'emp_det_tbl' ที่มีประเภทเรคอร์ดเป็นองค์ประกอบ 'emp_det'
  • Code สาย 10: ประกาศตัวแปร 'guru99_emp_rec' เป็นชนิด 'emp_det_tbl' และกำหนดค่าเริ่มต้นให้ตัวแปรนั้นด้วยคอนสตรัคเตอร์ที่เป็นค่าว่าง
  • Code บรรทัดที่ 12-15: การแทรกข้อมูลตัวอย่างลงในตาราง 'emp'
  • Code สาย 16: ยืนยันธุรกรรมการแทรก
  • Code สาย 17: ดึงข้อมูลจากตาราง 'emp' และเติมข้อมูลลงในตัวแปรคอลเลกชันแบบกลุ่มโดยใช้คำสั่ง “BULK COLLECT” ขณะนี้ตัวแปร 'guru99_emp_rec' มีข้อมูลทั้งหมดที่มีอยู่ในตาราง 'emp' แล้ว
  • Code บรรทัดที่ 19-26: ตั้งค่าลูป 'FOR' เพื่อพิมพ์ข้อมูลทั้งหมดในคอลเลกชันทีละรายการ โดยใช้เมธอด FIRST และ LAST เป็นขีดจำกัดล่างและบนของคอลเลกชัน ห่วง.

Output: เมื่อรันโค้ดข้างต้น คุณจะได้ผลลัพธ์ดังต่อไปนี้

Employee Detail
Employee Number: 1000
Employee Name: AAA
Employee Salary: 25000
Employee Manager Number: 1000
----------------------------------------------
Employee Number: 1001
Employee Name: XXX
Employee Salary: 10000
Employee Manager Number: 1000
----------------------------------------------
Employee Number: 1002
Employee Name: YYY
Employee Salary: 15000
Employee Manager Number: 1000
----------------------------------------------
Employee Number: 1003
Employee Name: ZZZ
Employee Salary: 7500
Employee Manager Number: 1000
----------------------------------------------

คำถามที่พบบ่อย

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

เนื่องจากเป็นอาร์เรย์แบบเชื่อมโยงในหน่วยความจำ ไม่ใช่ตารางที่จัดเก็บไว้ ดัชนีทำหน้าที่เป็นคีย์ในการค้นหา ดังนั้นสตริงหรือจำนวนเต็มลบจึงใช้งานได้ ซึ่งมีประโยชน์สำหรับการค้นหาโดยใช้คีย์ภายในเซสชันเดียวกัน

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

ใช่แล้ว ไม่ว่าขนาดจะคงที่หรือไม่ ข้อมูลจะถูกจัดเก็บไว้หรือไม่ และจำเป็นต้องใช้คีย์ที่เป็นสตริงหรือไม่ AI ก็สามารถชี้ไปยัง Varray, ตารางซ้อน หรือตารางแบบใช้ดัชนี และอธิบายข้อดีข้อเสียได้

BULK COLLECT โหลดข้อมูลทุกแถวลงในคอลเลกชันในการสลับบริบทเพียงครั้งเดียวระหว่างเอนจิน SQL และ PL/SQL แทนที่จะสลับไปทีละแถว ซึ่งช่วยลดภาระการทำงานสำหรับชุดผลลัพธ์ขนาดใหญ่ได้อย่างมาก

สรุปโพสต์นี้ด้วย: