0Pricing
SQL Interview Prep · บทเรียน

เปลี่ยนคอลัมน์กลับเป็นแถว

เปลี่ยนตารางแบบกว้างกลับด้วย UNPIVOT หรือ UNION ALL

เปลี่ยนคอลัมน์กลับเป็นแถว เป็นบทเรียน SQL Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

โจทย์ในทางกลับกัน

การคลายตารางคือภาพกลับด้านของการหมุนตาราง: คุณนำ ตารางแบบกว้าง แล้วเปลี่ยนคอลัมน์กลับให้เป็น แถว ผู้สัมภาษณ์จะถามเรื่องนี้เมื่อข้อมูลมาในรูปแบบตารางสเปรดชีต แต่ต้องปรับให้เป็นรูปแบบมาตรฐานเพื่อการวิเคราะห์

ตัวอย่างเช่น ตารางที่มีคอลัมน์ q1, q2, q3, q4 ต่อภูมิภาค ต้องเปลี่ยนเป็นแถวในรูปแบบ (region, quarter, amount) รูปแบบยาวนี้เหมาะกับการรวมค่า การเชื่อมตาราง และการทำแผนภูมิ

-- Wide input we want to unpivot
region | q1  | q2  | q3  | q4
-------+-----+-----+-----+----
East   | 100 | 150 | 120 | 180
West   | 200 | 250 | 210 | 260

รูปแบบ UNION ALL ที่ใช้ข้ามระบบ

คำตอบที่ไม่ขึ้นกับภาษาย่อยคือ UNION ALL: เขียน SELECT หนึ่งชุดต่อหนึ่งคอลัมน์ต้นทาง โดยแต่ละชุดจะส่งป้ายกำกับค่าคงที่และค่าของคอลัมน์นั้นออกมา

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

SELECT region, 'Q1' AS quarter, q1 AS amount FROM wide_sales
UNION ALL
SELECT region, 'Q2', q2 FROM wide_sales
UNION ALL
SELECT region, 'Q3', q3 FROM wide_sales
UNION ALL
SELECT region, 'Q4', q4 FROM wide_sales;

เหตุผลที่ใช้ UNION ALL ไม่ใช่ UNION

นี่เป็นกับดักคลาสสิกในการสัมภาษณ์ UNION จะลบแถวซ้ำทั่วทั้งผลลัพธ์ หาก East และ West ต่างมีค่า 100 ในไตรมาส 1 การใช้ UNION แบบปกติจะรวมแถวที่เหมือนกันจนเหลือแถวเดียว และทำให้ข้อมูลสูญหาย

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

-- UNION would wrongly merge identical (region, quarter, amount) rows
-- UNION ALL keeps every row, always the correct choice here

การจัดแนวชนิดคอลัมน์

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

หากคอลัมน์ในตารางแบบกว้างมีชนิดต่างกัน (เช่น คอลัมน์หนึ่งเป็น int และอีกคอลัมน์เป็น decimal) ระบบเอนจินจะเลือกชนิดข้อมูลร่วม หากชนิดข้อมูลเข้ากันไม่ได้จริง ให้แปลงชนิดข้อมูลอย่างชัดเจนเพื่อไม่ให้การรวมล้มเหลว

SELECT region, 'revenue' AS metric, CAST(revenue AS decimal(12,2)) AS val FROM t
UNION ALL
SELECT region, 'units',   CAST(units   AS decimal(12,2))        FROM t;

UNPIVOT ของเซิร์ฟเวอร์ SQL

เซิร์ฟเวอร์ SQL มีตัวดำเนินการ UNPIVOT โดยเฉพาะ ซึ่งกระชับกว่า UNION ALL คุณระบุชื่อคอลัมน์ค่าใหม่ ชื่อคอลัมน์ป้ายกำกับใหม่ และระบุคอลัมน์ต้นทางที่จะนำมารวมกัน

พฤติกรรมสำคัญอย่างหนึ่งคือ UNPIVOT จะตัดแถวที่ค่าของแถวเป็น NULL ออก ผู้สัมภาษณ์มักทดสอบว่าคุณทราบผลข้างเคียงนี้หรือไม่

SELECT region, quarter, amount
FROM wide_sales
UNPIVOT (
  amount FOR quarter IN (q1, q2, q3, q4)
) AS u;

UNPIVOT ตัดค่า NULL ออก

หากภูมิภาคหนึ่งมีค่า NULL ใน q3 UNPIVOT ของเซิร์ฟเวอร์ SQL จะละเว้นแถวนั้นจากผลลัพธ์ทันที หากคุณต้องการให้มีแถวสำหรับทุกคอลัมน์ไม่ว่าค่าจะเป็น NULL หรือไม่ ให้กลับไปใช้ UNION ALL ซึ่งจะเก็บแถวเหล่านั้นไว้

ควรกล่าวถึงข้อแลกเปลี่ยนนี้ในการสัมภาษณ์: UNPIVOT ในตัวกระชับแต่ทำข้อมูลที่เป็น NULL สูญหาย ส่วน UNION ALL มีรายละเอียดมากกว่าแต่เก็บข้อมูลได้ครบถ้วน

-- UNPIVOT: q3 NULL for East -> no (East, Q3) row produced
-- UNION ALL: (East, 'Q3', NULL) row IS produced

PostgreSQL: LATERAL VALUES

PostgreSQL ไม่มี UNPIVOT แต่มีรูปแบบการเขียนที่กระชับคือใช้ CROSS JOIN LATERAL กับรายการ VALUES แต่ละแถวแบบกว้างจะถูกขยายด้วยตารางแทรกขนาดเล็กที่ประกอบด้วยคู่ (ป้ายกำกับ, ค่า)

วิธีนี้อ่านง่ายกว่า UNION ALL ที่ยาว และอ่านตารางต้นทางเพียงครั้งเดียว

SELECT w.region, v.quarter, v.amount
FROM wide_sales w
CROSS JOIN LATERAL (VALUES
  ('Q1', w.q1),
  ('Q2', w.q2),
  ('Q3', w.q3),
  ('Q4', w.q4)
) AS v(quarter, amount);

การอ่านตารางเพียงครั้งเดียว

ประเด็นด้านประสิทธิภาพที่ควรพูดถึงคือ UNION ALL แบบพื้นฐานจะอ่านตารางแบบกว้างหนึ่งครั้งต่อหนึ่งส่วน (สี่ครั้งสำหรับสี่ไตรมาส) ส่วนรูปแบบ LATERAL VALUES และ UNPIVOT ของเซิร์ฟเวอร์ SQL จะอ่านแหล่งข้อมูล เพียงครั้งเดียว

เรื่องนี้สำคัญกับตารางขนาดใหญ่ หากคุณจำเป็นต้องใช้ UNION ALL ตัวปรับคำสั่งอาจยังอ่านซ้ำหลายครั้ง ดังนั้นควรกล่าวถึง LATERAL หรือ UNPIVOT ว่าเป็นตัวเลือกที่มีประสิทธิภาพกว่า

การกรองช่องว่างออก

เมื่อใช้ UNION ALL หรือ LATERAL คุณจะเก็บแถวที่มีค่า NULL ไว้ หากคำถามต้องการเฉพาะช่องที่มีค่า ให้เพิ่มตัวกรอง วิธีนี้เลียนแบบสิ่งที่ UNPIVOT ของเซิร์ฟเวอร์ SQL ทำโดยอัตโนมัติ

การตัดสินใจว่าจะเก็บหรือลบค่า NULL เป็นเรื่องที่ต้องพิจารณาตามบริบท ดังนั้นควรขอความชัดเจนเกี่ยวกับข้อกำหนดจากผู้สัมภาษณ์ก่อนเขียนโค้ด

SELECT region, quarter, amount
FROM (
  SELECT region, 'Q1' AS quarter, q1 AS amount FROM wide_sales
  UNION ALL SELECT region, 'Q2', q2 FROM wide_sales
) t
WHERE amount IS NOT NULL;

ตัวอย่างที่ทำเสร็จแล้ว: การคลายพิวอตหลังการรวมยอด

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

ตัวอย่างนี้แสดงให้เห็นเหตุผลที่แท้จริงว่าทำไมจึงควรคลายพิวอตก่อน การรวมค่าจากสี่คอลัมน์แยกกันทำให้ดูแลได้ยาก แต่คำสั่ง SUM(amount) GROUP BY region ในรูปแบบยาวสามารถรองรับจำนวนไตรมาสเท่าใดก็ได้

WITH long_sales AS (
  SELECT region, 'Q1' AS quarter, q1 AS amount FROM wide_sales
  UNION ALL SELECT region, 'Q2', q2 FROM wide_sales
  UNION ALL SELECT region, 'Q3', q3 FROM wide_sales
  UNION ALL SELECT region, 'Q4', q4 FROM wide_sales
)
SELECT region, SUM(amount) AS total
FROM long_sales
GROUP BY region;

เมื่อใดควรคลายพิวอต

ให้สังเกตสัญญาณที่บ่งบอกว่าควรคลายพิวอตจากโจทย์ที่เขียนเป็นคำบรรยาย:

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

รูปแบบยาวมักเป็นรูปแบบที่เหมาะสมที่สุดสำหรับการทำงาน SQL ต่อ ดังนั้นการคลายพิวอตจึงมักเป็นขั้นตอนแรก

ตรวจสอบอย่างรวดเร็ว

ตรวจสอบว่าคุณเข้าใจข้อผิดพลาดที่พบบ่อยที่สุดในการคลายพิวอตแล้ว

สรุปทบทวน

การคลายพิวอตจะเปลี่ยนคอลัมน์ให้เป็นแถว:

  • ใช้ได้กับหลายระบบ: ใช้ SELECT หนึ่งคำสั่งต่อหนึ่งคอลัมน์ แล้วเชื่อมด้วย UNION ALL (ห้ามใช้ UNION แบบธรรมดา)
  • SQL Server: มี UNPIVOT ในตัว กระชับแต่จะตัดค่า NULL ออก
  • PostgreSQL: ใช้ CROSS JOIN LATERAL (VALUES ...) และสแกนข้อมูลเพียงครั้งเดียว
  • จัดจำนวนและชนิดข้อมูลของคอลัมน์ในแต่ละแขนงคำสั่งให้ตรงกัน และกรองค่า NULL หากโจทย์กำหนดให้ทำ

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

บทเรียน “เปลี่ยนคอลัมน์กลับเป็นแถว” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เปลี่ยนคอลัมน์กลับเป็นแถว” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส SQL Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เปลี่ยนคอลัมน์กลับเป็นแถว”

เปลี่ยนตารางแบบกว้างกลับด้วย UNPIVOT หรือ UNION ALL คุณปฏิบัติ SQL Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน SQL Interview Prep หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน SQL Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “เปลี่ยนคอลัมน์กลับเป็นแถว” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน SQL Interview Prep นี้ได้ไหม

ได้ บทเรียน SQL Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การทำ Pivot ด้วยการรวมตามเงื่อนไข
  2. ไวยากรณ์ PIVOT และ Crosstab เฉพาะระบบ
  3. เปลี่ยนคอลัมน์กลับเป็นแถว
  4. Pivot แบบไดนามิกเมื่อไม่ทราบคอลัมน์ล่วงหน้า
← กลับไปที่ SQL Interview Prep