อาร์เรย์เทียบกับตารางแบบทำให้เป็นมาตรฐาน
เมื่อใดอาร์เรย์จึงเป็นตัวเลือกที่เหมาะสม
อาร์เรย์เทียบกับตารางแบบทำให้เป็นมาตรฐาน เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- พื้นฐานคอลัมน์อาร์เรย์
- การค้นหาภายในอาร์เรย์
- UNNEST และการรวมค่า
- อาร์เรย์เทียบกับตารางแบบทำให้เป็นมาตรฐาน