0Pricing
SQL Academy · บทเรียน

อาร์เรย์เทียบกับตารางแบบทำให้เป็นมาตรฐาน

เมื่อใดอาร์เรย์จึงเป็นตัวเลือกที่เหมาะสม

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

สองวิธีในการจัดเก็บค่าหลายค่า

เมื่อแถวเดียวต้องเก็บค่าที่เกี่ยวข้องกันหลายค่า PostgreSQL มีแนวทางหลักสองแบบ ได้แก่ จัดเก็บค่าเหล่านั้นเป็น คอลัมน์อาร์เรย์ ในแถวเดียวกัน หรือสร้าง ตารางลูก แยกต่างหาก โดยให้แต่ละค่าอยู่ในแถวของตนเอง

การทำความเข้าใจว่าเมื่อใดควรใช้แต่ละแนวทางเป็นทักษะสำคัญในการออกแบบฐานข้อมูลที่มีประสิทธิภาพและดูแลรักษาได้ง่าย

แนวทางแบบทำให้เป็นมาตรฐาน

ใน schema ที่ทำให้เป็นมาตรฐานอย่างสมบูรณ์ ข้อมูลแต่ละส่วนจะอยู่ในแถวของตนเอง หากผู้ใช้หนึ่งคนมีหมายเลขโทรศัพท์ได้หลายหมายเลข คุณจะสร้างตาราง user_phones พร้อมคีย์นอกที่อ้างกลับไปยัง users

นี่คือโมเดลเชิงสัมพันธ์แบบดั้งเดิม และเป็นตัวเลือกเริ่มต้นในสถานการณ์ส่วนใหญ่

CREATE TABLE users (
  id   SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);

CREATE TABLE user_phones (
  id      SERIAL PRIMARY KEY,
  user_id INT REFERENCES users(id),
  phone   TEXT NOT NULL
);

INSERT INTO users (name) VALUES ('Alice'), ('Bob');

INSERT INTO user_phones (user_id, phone) VALUES
  (1, '+1-555-0101'),
  (1, '+1-555-0102'),
  (2, '+1-555-0200');

แนวทางแบบอาร์เรย์

TEXT[] ของ PostgreSQL (หรือชนิดข้อมูลอื่นใดที่ตามด้วย []) ช่วยให้คุณจัดเก็บค่าหลายค่าไว้โดยตรงในคอลัมน์เดียว ไม่จำเป็นต้องมีตารางเพิ่มเติม

ข้อมูลหมายเลขโทรศัพท์ชุดเดียวกันนี้สามารถจัดเก็บไว้ในแถวขนาดกะทัดรัดหนึ่งแถวต่อผู้ใช้หนึ่งคน

CREATE TABLE users_with_phones (
  id     SERIAL PRIMARY KEY,
  name   TEXT    NOT NULL,
  phones TEXT[]
);

INSERT INTO users_with_phones (name, phones) VALUES
  ('Alice', ARRAY['+1-555-0101', '+1-555-0102']),
  ('Bob',   ARRAY['+1-555-0200']);

การค้นหาภายในอาร์เรย์ทำได้ง่าย

การค้นหาภายในคอลัมน์อาร์เรย์ทำได้โดยตรงด้วยตัวดำเนินการ ANY หรือ ตัวดำเนินการ @> (มีค่าอยู่ภายใน) คุณสามารถค้นหาผู้ใช้ทั้งหมดที่มีหมายเลขโทรศัพท์เฉพาะได้ด้วยส่วนคำสั่ง WHERE อย่างง่าย

-- Find users who have a specific phone number
SELECT name
FROM users_with_phones
WHERE '+1-555-0101' = ANY(phones);

-- Or using the array-contains operator
SELECT name
FROM users_with_phones
WHERE phones @> ARRAY['+1-555-0101'];

อาร์เรย์เหมาะที่สุดเมื่อใช้ค้นหาแบบง่าย

อาร์เรย์เป็นตัวเลือกที่เหมาะสมเมื่อ:

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

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

CREATE TABLE posts (
  id         SERIAL PRIMARY KEY,
  title      TEXT    NOT NULL,
  tags       TEXT[]
);

INSERT INTO posts (title, tags) VALUES
  ('Intro to SQL',      ARRAY['sql', 'beginner', 'database']),
  ('Advanced Indexes',  ARRAY['sql', 'performance', 'indexes']),
  ('NoSQL Overview',    ARRAY['nosql', 'beginner']);

-- Get all posts tagged 'beginner'
SELECT title FROM posts
WHERE 'beginner' = ANY(tags);

ตารางที่ทำให้เป็นมาตรฐานเหมาะที่สุดเมื่อมีความสัมพันธ์

ตารางที่ทำให้เป็นมาตรฐานเป็นตัวเลือกที่ดีกว่าเมื่อ:

  • ค่าแต่ละค่าต้องมีคุณลักษณะของตนเอง (เช่น หมายเลขโทรศัพท์มีประเภท: บ้าน/ที่ทำงาน)
  • คุณจำเป็นต้องเชื่อมตารางโดยใช้ค่าแต่ละค่า
  • ค่าต่าง ๆ เปลี่ยนแปลงแยกจากกันและบ่อยครั้ง
  • คุณต้องการความถูกต้องของการอ้างอิงผ่านคีย์นอก
-- Phone numbers need a 'type' attribute — array can't do this cleanly
CREATE TABLE user_phones (
  id      SERIAL PRIMARY KEY,
  user_id INT REFERENCES users(id),
  phone   TEXT NOT NULL,
  type    TEXT CHECK (type IN ('home', 'work', 'mobile'))
);

INSERT INTO user_phones (user_id, phone, type) VALUES
  (1, '+1-555-0101', 'home'),
  (1, '+1-555-0102', 'work');

ความแตกต่างด้านการทำดัชนี

สำหรับตารางที่ทำให้เป็นมาตรฐาน คุณสามารถเพิ่มดัชนีแบบบีทรีมาตรฐานบนคอลัมน์คีย์นอกหรือคอลัมน์ค่าได้ ส่วนอาร์เรย์ คุณต้องใช้ดัชนี GIN (ดัชนีกลับด้านแบบทั่วไป) เพื่อให้ค้นหาภายในอาร์เรย์ได้อย่างรวดเร็ว

ดัชนี GIN ทำงานได้ดี แต่มีขนาดใหญ่กว่าและอัปเดตได้ช้กว่าดัชนีแบบบีทรี

-- Index for fast array element lookups
CREATE INDEX idx_posts_tags ON posts USING GIN (tags);

-- Now this query uses the index efficiently
EXPLAIN SELECT title FROM posts
WHERE tags @> ARRAY['sql'];

การรวมข้อมูลข้ามแถว: ตารางที่ทำให้เป็นมาตรฐานเหมาะกว่า

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

-- Count posts per tag (array approach — needs unnest)
SELECT tag, COUNT(*) AS post_count
FROM posts, unnest(tags) AS tag
GROUP BY tag
ORDER BY post_count DESC;

-- With a normalized post_tags table this would be simpler:
-- SELECT tag, COUNT(*) FROM post_tags GROUP BY tag;

การแก้ไขสมาชิกในอาร์เรย์

การอัปเดตหรือลบสมาชิกเพียงตัวเดียวภายในอาร์เรย์ต้องใช้รูปแบบคำสั่งที่ยุ่งยาก — คุณต้องแทนที่อาร์เรย์ทั้งชุดหรือใช้ array_remove() ในตารางที่ทำให้เป็นมาตรฐาน คุณเพียงใช้ DELETE หรือ UPDATE กับแถวที่ต้องการ

-- Remove a single tag from an array column
UPDATE posts
SET tags = array_remove(tags, 'beginner')
WHERE id = 1;

-- Append a new tag
UPDATE posts
SET tags = array_append(tags, 'tutorial')
WHERE id = 1;

SELECT title, tags FROM posts WHERE id = 1;

การบังคับใช้ค่าที่ถูกต้อง

ในตารางที่ทำให้เป็นมาตรฐาน คุณสามารถใช้คีย์นอกเพื่อบังคับให้ทุกค่ามาจากชุดค่าที่ทราบแน่ชัด อาร์เรย์ไม่สามารถอ้างอิงตารางอื่นได้ — ไม่รองรับคีย์นอก

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

-- Normalized: only valid category IDs allowed (FK enforced)
CREATE TABLE categories (
  id   SERIAL PRIMARY KEY,
  name TEXT UNIQUE NOT NULL
);

CREATE TABLE post_categories (
  post_id     INT REFERENCES posts(id),
  category_id INT REFERENCES categories(id),
  PRIMARY KEY (post_id, category_id)
);

-- Array: no constraint possible — any text value is accepted
-- UPDATE posts SET tags = ARRAY['totally_invalid_tag'] WHERE id = 1;

คู่มือช่วยตัดสินใจในทางปฏิบัติ

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

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

-- Summary example: tags as array (good fit)
SELECT title, tags
FROM posts
WHERE tags @> ARRAY['sql']
ORDER BY title;

-- Unnest when you need row-level processing
SELECT title, unnest(tags) AS tag
FROM posts
ORDER BY title, tag;

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

สถานการณ์ใดเหมาะสมที่สุดสำหรับการจัดเก็บข้อมูลเป็นอาร์เรย์ของ PostgreSQL แทนตารางลูกที่ทำให้เป็นมาตรฐาน

สรุปบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ข้อแลกเปลี่ยนสำคัญระหว่างอาร์เรย์กับตารางที่ทำให้เป็นมาตรฐานใน PostgreSQL

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

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

บทเรียน “อาร์เรย์เทียบกับตารางแบบทำให้เป็นมาตรฐาน” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “อาร์เรย์เทียบกับตารางแบบทำให้เป็นมาตรฐาน”

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

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

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

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

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

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

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

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

  1. พื้นฐานคอลัมน์อาร์เรย์
  2. การค้นหาภายในอาร์เรย์
  3. UNNEST และการรวมค่า
  4. อาร์เรย์เทียบกับตารางแบบทำให้เป็นมาตรฐาน
← กลับไปที่ SQL Academy