Kodokon kodokon.com

ซับคิวรี: WHERE, FROM, IN และ EXISTS

ซ้อนคิวรีหนึ่งไว้ในอีกคิวรีหนึ่งเพื่อเปรียบเทียบกับค่ารวม กรองด้วยชุดของค่า หรือทดสอบการมีอยู่

9 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

ซับคิวรี (subquery) คือคิวรีที่วางไว้ในวงเล็บภายในอีกคิวรีหนึ่ง ในคำสั่งเดียว มันตอบคำถามอย่างเช่น "คำสั่งซื้อที่สูงกว่าค่าเฉลี่ย" หรือ "ลูกค้าที่มีคำสั่งซื้ออย่างน้อยหนึ่งรายการ" รันสคริปต์นี้ใน sqliteonline.com หรือ sqlite3 ก่อนเริ่ม

SQL
CREATE TABLE customers (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL
);

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL,
  amount REAL NOT NULL
);

INSERT INTO customers (id, name) VALUES
  (1, 'Alice'), (2, 'Karim'),
  (3, 'Mina'), (4, 'Paulo');

INSERT INTO orders (id, customer_id, amount)
VALUES
  (1, 1, 49.90), (2, 1, 15.00),
  (3, 2, 120.50), (4, 2, 80.00),
  (5, 3, 9.90), (6, 1, 200.00);
ชุดข้อมูล: ลูกค้าสี่คน คำสั่งซื้อหกรายการ

เมื่อวางใน WHERE ซับคิวรีแบบ สเกลาร์ (scalar) ต้องคืนค่าเดียว คือหนึ่งแถว หนึ่งคอลัมน์ เอนจินจะคำนวณมันก่อน แล้วจึงใช้มันเหมือนค่าคงที่ตัวอื่น ๆ คุณทำเช่นนี้ด้วย WHERE amount > AVG(amount) ธรรมดาไม่ได้ เพราะเป็นสิ่งต้องห้ามใน SQL

SQL
SELECT id, customer_id, amount
FROM orders
WHERE amount > (
  SELECT AVG(amount) FROM orders
);
คำสั่งซื้อที่สูงกว่าตะกร้าเฉลี่ย (79.22)

IN เปรียบเทียบคอลัมน์กับ ชุด (set) ของค่าที่ซับคิวรีคืนกลับมา EXISTS ทดสอบว่าซับคิวรีแบบ สหสัมพันธ์ (correlated) (ซึ่งอ้างถึง c.id อันเป็นคอลัมน์ของคิวรีภายนอก) คืนอย่างน้อยหนึ่งแถวหรือไม่ เอนจินจะหยุดทันทีที่พบคู่ที่ตรงกันคู่แรก

SQL
SELECT name FROM customers
WHERE id IN (
  SELECT customer_id FROM orders
  WHERE amount > 100
);

SELECT c.name FROM customers AS c
WHERE EXISTS (
  SELECT 1 FROM orders AS o
  WHERE o.customer_id = c.id
);
IN: ลูกค้าที่ใช้จ่ายสูง EXISTS: ลูกค้าที่เคลื่อนไหว

ใน FROM ซับคิวรีจะทำตัวเหมือนตารางชั่วคราวที่เรียกว่า ตารางอนุพัทธ์ (derived table) และต้องกำหนดนามแฝงให้ มันเป็นเครื่องมือคลาสสิกสำหรับการรวมกลุ่มแบบสองขั้น ขั้นแรกหายอดรวมต่อลูกค้าหนึ่งคน แล้วจึงหาค่าเฉลี่ยของยอดรวมเหล่านั้น

SQL
SELECT ROUND(AVG(t.total), 2) AS avg_basket
FROM (
  SELECT customer_id, SUM(amount) AS total
  FROM orders
  GROUP BY customer_id
) AS t;
ตะกร้ารวมเฉลี่ยต่อลูกค้าที่เคลื่อนไหวหนึ่งคน: 158.43

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. ซับคิวรีที่ใช้ใน amount > (...) ต้องคืนค่ากี่ค่า?
    • เพียงค่าเดียวเท่านั้น
    • หนึ่งค่าต่อหนึ่งแถวของตารางภายนอก
    • กี่ค่าก็ได้ตามใจ
  2. ข้อได้เปรียบหลักของ EXISTS เหนือ IN คืออะไร?
    • มันสามารถคืนหลายคอลัมน์ได้
    • มันหยุดที่แถวแรกที่พบ โดยไม่ต้องสร้างชุดข้อมูลทั้งหมด
    • มันทำงานได้โดยไม่ต้องมีซับคิวรี
  3. ซับคิวรีที่วางไว้ใน FROM ต้องมีอะไรเสมอเพื่อให้ใช้ได้กับทุกที่?
    • นามแฝง เช่น AS t
    • ประโยค ORDER BY
    • เครื่องหมายอัฒภาคภายใน