MySQL / Subquery

Subquery

Subquery (дэд query) нь өөр SQL мэдэгдлийн дотор байрлах query юм. Нарийн шүүлт, харьцуулалт хийхэд хүчтэй хэрэгсэл.

Суурь жишээ

sql
CREATE TABLE products (
  id    INT PRIMARY KEY AUTO_INCREMENT,
  name  TEXT NOT NULL,
  price DECIMAL(10,2),
  stock INT
);

CREATE TABLE orders (
  id         INT PRIMARY KEY AUTO_INCREMENT,
  product_id INT REFERENCES products(id),
  customer   TEXT,
  quantity   INT
);

INSERT INTO products (name, price, stock) VALUES
  ('Ном',    15000, 50),
  ('Дэвтэр',  3500, 200),
  ('Үзэг',    1200, 500),
  ('Тоглоом', 45000, 10);

INSERT INTO orders (product_id, customer, quantity) VALUES
  (1, 'Болд',  2),
  (2, 'Оюун',  5),
  (1, 'Дорж',  1),
  (3, 'Болд', 10),
  (1, 'Оюун',  3);

WHERE дахь subquery

sql
-- Дундаж үнээс өндөр бараанууд
SELECT name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);
код
name     | price
---------|------
Ном      | 15000
Тоглоом  | 45000

Дотоод query (SELECT AVG(price) FROM products) эхлээд ажиллаж нэг тоо буцаана, дараа нь гадна query тэр тоотой харьцуулна.

sql
-- Хамгийн их захиалга авсан барааны мэдээлэл
SELECT name, price
FROM products
WHERE id = (
  SELECT product_id
  FROM orders
  GROUP BY product_id
  ORDER BY SUM(quantity) DESC
  LIMIT 1
);

IN оператор — олон утгатай харьцуулах

sql
-- Захиалга авсан бараануудын жагсаалт
SELECT name, price
FROM products
WHERE id IN (SELECT DISTINCT product_id FROM orders);

-- Захиалга аваагүй бараануудын жагсаалт
SELECT name, price
FROM products
WHERE id NOT IN (SELECT DISTINCT product_id FROM orders);

IN нь subquery олон мөр буцаах үед ашиглана. = (subquery) зөвхөн нэг утга буцаана гэж найдвартай мэдэх үед.

EXISTS оператор — байгаа эсэхийг шалгах

sql
-- Дор хаяж нэг захиалга авсан бараанууд
SELECT name
FROM products p
WHERE EXISTS (
  SELECT 1
  FROM orders o
  WHERE o.product_id = p.id
);

EXISTS нь subquery хэдэн ч мөр буцаасан хамаагүй — байгаа эсэхийг л шалгана. SELECT 1 ашигладаг учир нь ямар утга буцаахаас хамаагүй, зөвхөн мөр байгаа эсэх л чухал.

ANY ба ALL

sql
-- Аль нэг захиалгын тоо хэмжээнээс их нөөцтэй бараанууд
SELECT name, stock
FROM products
WHERE stock > ANY (SELECT quantity FROM orders);
-- stock > 1 байвал хангалттай (хамгийн бага утгаас их)

-- Бүх захиалгын тоо хэмжээнээс их нөөцтэй бараанууд
SELECT name, stock
FROM products
WHERE stock > ALL (SELECT quantity FROM orders);
-- stock > 10 байх ёстой (хамгийн их утгаас их)

FROM дахь subquery (derived table)

sql
-- Хэрэглэгч бүрийн нийт зарцуулалтын дундаж
SELECT AVG(нийт) AS дундаж_зарцуулалт
FROM (
  SELECT customer, SUM(p.price * o.quantity) AS нийт
  FROM orders o
  JOIN products p ON o.product_id = p.id
  GROUP BY customer
) AS хэрэглэгч_нийт;

FROM дахь subquery-г derived table буюу гаралтын хүснэгт гэнэ. Заавал alias өгөх шаардлагатай (AS хэрэглэгч_нийт).

Correlated subquery

Correlated subquery нь гадна query-н мөр бүрт тусад нь ажилладаг — гадна query-н утгыг дотор ашигладаг.

sql
-- Бүх барааны дундаж үнээс өндөр үнэтэй барааг тус тусын дундажтай харьцуулах
SELECT name, price,
  (SELECT AVG(price) FROM products) AS ерөнхий_дундаж
FROM products
WHERE price > (SELECT AVG(price) FROM products);

Жинхэнэ correlated subquery:

sql
-- Хамгийн их тоогоор захиалагдсан бараа бүрийн мэдээлэл
SELECT name, price
FROM products p
WHERE (
  SELECT SUM(quantity)
  FROM orders o
  WHERE o.product_id = p.id   -- гадна query-н p.id ашиглаж байна
) > 3;

Subquery vs JOIN

Ихэнх subquery-г JOIN-аар бас бичиж болно:

sql
-- Subquery хувилбар
SELECT name FROM products
WHERE id IN (SELECT product_id FROM orders);

-- JOIN хувилбар (ихэвчлэн хурдан)
SELECT DISTINCT p.name
FROM products p
JOIN orders o ON p.id = o.product_id;

Том хүснэгтэд JOIN нь subquery-аас хурдан байдаг. Гэхдээ subquery нь заримдаа уншихад илүү ойлгомжтой.

Дараагийн хичээлд:

String функцүүд — текст боловсруулах CONCAT, SUBSTRING, REPLACE, LIKE зэрэг функцүүдийг үзнэ.