در این دوره کوئرینویسی SQL را روی پایگاه دادهٔ یک فروشگاه آنلاین یاد میگیرید. هر کوئری را در مرورگر روی PostgreSQL اجرا میکنید و نتیجه را همان لحظه میبینید.
-- سه کتاب پرفروش و درآمد هر کدام
SELECT b.title,
SUM(oi.qty) AS sold,
SUM(oi.qty * b.price) AS revenue
FROM order_items oi
JOIN books b ON b.id = oi.book_id
GROUP BY b.id, b.title
ORDER BY sold DESC, revenue DESC
LIMIT 3;
| title | sold | revenue |
|---|---|---|
| شازده کوچولو | 6 | 720000 |
| کد تمیز | 5 | 2050000 |
| سمفونی مردگان | 3 | 720000 |
اقلام سفارشها به جدول کتابها وصل میشوند، فروش هر کتاب جمع زده میشود و سه کتاب پرفروش با درآمدشان میآیند. نمونههای دیگر
هر فصل چند دستور تازه را با تمرین روی جدولهای مشتری، محصول، سفارش و کارمند یاد میدهد.
جدول، ردیف، کلید اصلی و کلید خارجی را میشناسید و اولین SELECT را با WHERE مینویسید.
شرطها را با AND و OR ترکیب میکنید و نتیجه را با LIKE، IN، BETWEEN، ORDER BY و LIMIT محدود و مرتب میکنید.
مقدارهای تکراری را با DISTINCT کنار میگذارید، با متن و تاریخ کار میکنید و با CASE داده را دستهبندی میکنید.
تعداد، جمع و میانگین هر گروه را حساب میکنید و فرق WHERE و HAVING را یاد میگیرید.
جدولها را با JOIN به هم وصل میکنید، دامهای رایج LEFT JOIN را میشناسید و نتیجهٔ دو کوئری را با UNION، INTERSECT و EXCEPT ترکیب میکنید.
کوئری چندمرحلهای را با زیرکوئری و WITH خوانا مینویسید و یک چارت سازمانی را با کوئری بازگشتی پیمایش میکنید.
در هر گروه رتبه میدهید، جمع تجمعی درآمد را میگیرید و فروش هر ماه را با ماه قبل مقایسه میکنید.
رفتار NULL را در شرط و گروهبندی میشناسید، جدول و قید میسازید و داده را درج، ویرایش و حذف میکنید.
ویو میسازید، میبینید ایندکس کجا به کار میآید و پلن اجرای کوئری را با EXPLAIN میخوانید.
با تراکنش داده را سالم نگه میدارید، جدولها را نرمالسازی میکنید، گزارش Pivot و خروجی JSON میسازید و جلوی تزریق SQL را میگیرید.
این شش کوئری را برای همین صفحه روی دادهٔ یک کتابفروشی آنلاین نوشتهایم. جدولها و دادههای هر کدام بالای کد آن است و ردیفهای نتیجه کنارش.
از جدول کتابها فقط کتابهایی میآیند که برنامهنویسی نیستند و قیمتشان بین ۱۵۰ تا ۳۰۰ هزار تومان است. نتیجه از ارزان به گران مرتب میشود.
CREATE TABLE books (
id int PRIMARY KEY,
title text NOT NULL,
author text NOT NULL,
genre text NOT NULL,
price int NOT NULL
);
INSERT INTO books VALUES
(1, 'بوف کور', 'صادق هدایت', 'رمان', 180000),
(2, 'سمفونی مردگان', 'عباس معروفی', 'رمان', 240000),
(3, 'کلیدر', 'محمود دولتآبادی', 'رمان', 950000),
(4, 'کد تمیز', 'رابرت مارتین', 'برنامهنویسی', 410000),
(5, 'برنامهنویس عملگرا', 'اندرو هانت', 'برنامهنویسی', 380000),
(6, 'شازده کوچولو', 'آنتوان دو سنتاگزوپری', 'کودک', 120000),
(7, 'قصههای مجید', 'هوشنگ مرادی کرمانی', 'کودک', 210000);
-- کتابهای غیر برنامهنویسی بین ۱۵۰ تا ۳۰۰ هزار تومان، ارزانترین اول
SELECT title, author, price
FROM books
WHERE genre <> 'برنامهنویسی'
AND price BETWEEN 150000 AND 300000
ORDER BY price;
| title | author | price |
|---|---|---|
| بوف کور | صادق هدایت | 180000 |
| قصههای مجید | هوشنگ مرادی کرمانی | 210000 |
| سمفونی مردگان | عباس معروفی | 240000 |
اقلام سفارشها به جدول کتابها وصل میشوند، فروش هر کتاب جمع زده میشود و سه کتاب پرفروش با درآمدشان میآیند.
CREATE TABLE books (
id int PRIMARY KEY,
title text NOT NULL,
author text NOT NULL,
genre text NOT NULL,
price int NOT NULL
);
INSERT INTO books VALUES
(1, 'بوف کور', 'صادق هدایت', 'رمان', 180000),
(2, 'سمفونی مردگان', 'عباس معروفی', 'رمان', 240000),
(3, 'کلیدر', 'محمود دولتآبادی', 'رمان', 950000),
(4, 'کد تمیز', 'رابرت مارتین', 'برنامهنویسی', 410000),
(5, 'برنامهنویس عملگرا', 'اندرو هانت', 'برنامهنویسی', 380000),
(6, 'شازده کوچولو', 'آنتوان دو سنتاگزوپری', 'کودک', 120000),
(7, 'قصههای مجید', 'هوشنگ مرادی کرمانی', 'کودک', 210000);
CREATE TABLE customers (
id int PRIMARY KEY,
name text NOT NULL,
city text NOT NULL
);
INSERT INTO customers VALUES
(1, 'علی', 'تهران'),
(2, 'سارا', 'شیراز'),
(3, 'رضا', 'تهران'),
(4, 'مینا', 'اصفهان'),
(5, 'حسین', 'تبریز'),
(6, 'نگار', 'شیراز');
CREATE TABLE orders (
id int PRIMARY KEY,
customer_id int NOT NULL REFERENCES customers(id)
);
INSERT INTO orders VALUES
(1, 1), (2, 2), (3, 3), (4, 1), (5, 4),
(6, 6), (7, 5), (8, 2), (9, 3), (10, 6);
CREATE TABLE order_items (
order_id int NOT NULL REFERENCES orders(id),
book_id int NOT NULL REFERENCES books(id),
qty int NOT NULL
);
INSERT INTO order_items VALUES
(1, 4, 1), (1, 1, 2), (2, 6, 3), (2, 5, 1), (3, 5, 1),
(3, 4, 1), (4, 3, 1), (5, 2, 2), (5, 7, 1), (6, 6, 1),
(6, 1, 1), (7, 4, 2), (8, 7, 2), (8, 2, 1), (9, 4, 1),
(10, 6, 2);
-- سه کتاب پرفروش و درآمد هر کدام
SELECT b.title,
SUM(oi.qty) AS sold,
SUM(oi.qty * b.price) AS revenue
FROM order_items oi
JOIN books b ON b.id = oi.book_id
GROUP BY b.id, b.title
ORDER BY sold DESC, revenue DESC
LIMIT 3;
| title | sold | revenue |
|---|---|---|
| شازده کوچولو | 6 | 720000 |
| کد تمیز | 5 | 2050000 |
| سمفونی مردگان | 3 | 720000 |
جمع خرید هر مشتری یک بار در یک CTE حساب میشود. بعد فقط مشتریهایی میآیند که از میانگین همهٔ مشتریها بیشتر خرید کردهاند.
CREATE TABLE books (
id int PRIMARY KEY,
title text NOT NULL,
author text NOT NULL,
genre text NOT NULL,
price int NOT NULL
);
INSERT INTO books VALUES
(1, 'بوف کور', 'صادق هدایت', 'رمان', 180000),
(2, 'سمفونی مردگان', 'عباس معروفی', 'رمان', 240000),
(3, 'کلیدر', 'محمود دولتآبادی', 'رمان', 950000),
(4, 'کد تمیز', 'رابرت مارتین', 'برنامهنویسی', 410000),
(5, 'برنامهنویس عملگرا', 'اندرو هانت', 'برنامهنویسی', 380000),
(6, 'شازده کوچولو', 'آنتوان دو سنتاگزوپری', 'کودک', 120000),
(7, 'قصههای مجید', 'هوشنگ مرادی کرمانی', 'کودک', 210000);
CREATE TABLE customers (
id int PRIMARY KEY,
name text NOT NULL,
city text NOT NULL
);
INSERT INTO customers VALUES
(1, 'علی', 'تهران'),
(2, 'سارا', 'شیراز'),
(3, 'رضا', 'تهران'),
(4, 'مینا', 'اصفهان'),
(5, 'حسین', 'تبریز'),
(6, 'نگار', 'شیراز');
CREATE TABLE orders (
id int PRIMARY KEY,
customer_id int NOT NULL REFERENCES customers(id)
);
INSERT INTO orders VALUES
(1, 1), (2, 2), (3, 3), (4, 1), (5, 4),
(6, 6), (7, 5), (8, 2), (9, 3), (10, 6);
CREATE TABLE order_items (
order_id int NOT NULL REFERENCES orders(id),
book_id int NOT NULL REFERENCES books(id),
qty int NOT NULL
);
INSERT INTO order_items VALUES
(1, 4, 1), (1, 1, 2), (2, 6, 3), (2, 5, 1), (3, 5, 1),
(3, 4, 1), (4, 3, 1), (5, 2, 2), (5, 7, 1), (6, 6, 1),
(6, 1, 1), (7, 4, 2), (8, 7, 2), (8, 2, 1), (9, 4, 1),
(10, 6, 2);
-- مشتریهایی که بیشتر از میانگین خرید کردهاند
WITH spend AS (
SELECT o.customer_id, SUM(oi.qty * b.price) AS total
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
JOIN books b ON b.id = oi.book_id
GROUP BY o.customer_id
)
SELECT c.name, c.city, s.total
FROM spend s
JOIN customers c ON c.id = s.customer_id
WHERE s.total > (SELECT AVG(total) FROM spend)
ORDER BY s.total DESC;
| name | city | total |
|---|---|---|
| علی | تهران | 1720000 |
| سارا | شیراز | 1400000 |
| رضا | تهران | 1200000 |
هر دسته فقط دستهٔ بالایی خود را میشناسد. یک کوئری بازگشتی از ریشه پایین میرود و مسیر کامل و عمق هر دسته را میسازد.
CREATE TABLE categories (
id int PRIMARY KEY,
name text NOT NULL,
parent_id int REFERENCES categories(id)
);
INSERT INTO categories VALUES
(1, 'کتاب', NULL),
(2, 'داستان', 1),
(3, 'رمان ایرانی', 2),
(4, 'رمان خارجی', 2),
(5, 'آموزشی', 1),
(6, 'برنامهنویسی', 5),
(7, 'پایتون', 6),
(8, 'زبان انگلیسی', 5);
-- مسیر هر دسته از ریشه تا خودش
WITH RECURSIVE tree AS (
SELECT id, 1 AS depth, name AS path
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, t.depth + 1, t.path || ' / ' || c.name
FROM categories c
JOIN tree t ON c.parent_id = t.id
)
SELECT depth, path
FROM tree
ORDER BY path;
| depth | path |
|---|---|
| 1 | کتاب |
| 2 | کتاب / آموزشی |
| 3 | کتاب / آموزشی / برنامهنویسی |
| 4 | کتاب / آموزشی / برنامهنویسی / پایتون |
| 3 | کتاب / آموزشی / زبان انگلیسی |
| 2 | کتاب / داستان |
| 3 | کتاب / داستان / رمان ایرانی |
| 3 | کتاب / داستان / رمان خارجی |
تابع پنجرهای کتابهای هر ژانر را جداگانه رتبه میدهد و سهم هر کتاب از فروش ژانرش را حساب میکند. دو کتاب با فروش برابر رتبهٔ یکسان میگیرند.
CREATE TABLE books (
id int PRIMARY KEY,
title text NOT NULL,
author text NOT NULL,
genre text NOT NULL,
price int NOT NULL
);
INSERT INTO books VALUES
(1, 'بوف کور', 'صادق هدایت', 'رمان', 180000),
(2, 'سمفونی مردگان', 'عباس معروفی', 'رمان', 240000),
(3, 'کلیدر', 'محمود دولتآبادی', 'رمان', 950000),
(4, 'کد تمیز', 'رابرت مارتین', 'برنامهنویسی', 410000),
(5, 'برنامهنویس عملگرا', 'اندرو هانت', 'برنامهنویسی', 380000),
(6, 'شازده کوچولو', 'آنتوان دو سنتاگزوپری', 'کودک', 120000),
(7, 'قصههای مجید', 'هوشنگ مرادی کرمانی', 'کودک', 210000);
CREATE TABLE customers (
id int PRIMARY KEY,
name text NOT NULL,
city text NOT NULL
);
INSERT INTO customers VALUES
(1, 'علی', 'تهران'),
(2, 'سارا', 'شیراز'),
(3, 'رضا', 'تهران'),
(4, 'مینا', 'اصفهان'),
(5, 'حسین', 'تبریز'),
(6, 'نگار', 'شیراز');
CREATE TABLE orders (
id int PRIMARY KEY,
customer_id int NOT NULL REFERENCES customers(id)
);
INSERT INTO orders VALUES
(1, 1), (2, 2), (3, 3), (4, 1), (5, 4),
(6, 6), (7, 5), (8, 2), (9, 3), (10, 6);
CREATE TABLE order_items (
order_id int NOT NULL REFERENCES orders(id),
book_id int NOT NULL REFERENCES books(id),
qty int NOT NULL
);
INSERT INTO order_items VALUES
(1, 4, 1), (1, 1, 2), (2, 6, 3), (2, 5, 1), (3, 5, 1),
(3, 4, 1), (4, 3, 1), (5, 2, 2), (5, 7, 1), (6, 6, 1),
(6, 1, 1), (7, 4, 2), (8, 7, 2), (8, 2, 1), (9, 4, 1),
(10, 6, 2);
-- رتبه و سهم هر کتاب از فروش ژانر خودش
SELECT b.genre,
b.title,
SUM(oi.qty) AS sold,
RANK() OVER (PARTITION BY b.genre ORDER BY SUM(oi.qty) DESC) AS rank_in_genre,
ROUND(100.0 * SUM(oi.qty) / SUM(SUM(oi.qty)) OVER (PARTITION BY b.genre)) AS share_pct
FROM books b
JOIN order_items oi ON oi.book_id = b.id
GROUP BY b.id, b.genre, b.title
ORDER BY b.genre, rank_in_genre, b.id;
| genre | title | sold | rank_in_genre | share_pct |
|---|---|---|---|---|
| برنامهنویسی | کد تمیز | 5 | 1 | 71 |
| برنامهنویسی | برنامهنویس عملگرا | 2 | 2 | 29 |
| رمان | بوف کور | 3 | 1 | 43 |
| رمان | سمفونی مردگان | 3 | 1 | 43 |
| رمان | کلیدر | 1 | 3 | 14 |
| کودک | شازده کوچولو | 6 | 1 | 67 |
| کودک | قصههای مجید | 3 | 2 | 33 |
بار تازه به انبار رسیده است. موجودی کتابهایی که ردیف دارند زیاد میشود و برای کتاب تازه ردیف جدید ساخته میشود، همه با یک دستور.
CREATE TABLE books (
id int PRIMARY KEY,
title text NOT NULL
);
INSERT INTO books VALUES
(1, 'بوف کور'),
(2, 'سمفونی مردگان'),
(4, 'کد تمیز'),
(6, 'شازده کوچولو');
CREATE TABLE stock (
book_id int PRIMARY KEY REFERENCES books(id),
qty int NOT NULL
);
INSERT INTO stock VALUES (1, 5), (2, 0), (4, 12);
-- بار تازه: موجودی قبلی زیاد میشود و کتاب تازه ردیف میگیرد
WITH arrived AS (
INSERT INTO stock (book_id, qty) VALUES
(1, 10),
(2, 4),
(6, 7)
ON CONFLICT (book_id) DO UPDATE
SET qty = stock.qty + EXCLUDED.qty
RETURNING book_id, qty
)
SELECT b.title, a.qty AS in_stock
FROM arrived a
JOIN books b ON b.id = a.book_id
ORDER BY a.book_id;
| title | in_stock |
|---|---|
| بوف کور | 15 |
| سمفونی مردگان | 4 |
| شازده کوچولو | 7 |
هر درس یک توضیح کوتاه و یک پرسش مشخص دارد و جدولهای فروشگاه با دادههایشان جلوی شماست.
کوئری در مرورگر روی PostgreSQL اجرا میشود و ردیفهای نتیجه همان لحظه نشان داده میشود.
دکمهٔ بررسی کار شما را میسنجد. اگر جایی گیر کردید، راهنمای همان تمرین قدمبهقدم کمکتان میکند.
پایگاه دادهٔ دوره همان PostgreSQL است که در مرورگر شما اجرا میشود.
| موضوع | تمرین در دینا کد | نصب روی رایانه |
|---|---|---|
| برای شروع | یک مرورگر کافی است. | باید PostgreSQL یا MySQL را نصب و تنظیم کنید. |
| داده برای تمرین | پایگاه دادهٔ فروشگاه آماده است. | جدولها و داده را باید خودتان بسازید. |
| یک DELETE اشتباه | هر اجرا با دادهٔ تازه شروع میشود. | دادهای که پاک شود برنمیگردد. |
| وقتی گیر میکنید | بررسی خودکار و راهنمای هر تمرین | باید خودتان ایراد را پیدا کنید. |
کوئریهایی که اینجا مینویسید روی هر سرور PostgreSQL همان نتیجه را میدهند.
۱۹ سرفصل، ۸۸ درس، ۶۱ تمرین
درسهای اول رایگان است و برای شروع پرداختی لازم نیست. هر وقت خواستید ادامه دهید، همین دوره را بخرید یا با اشتراک پرو همهٔ دورهها را باز کنید.
۴ درس ابتدایی رایگان است. برای ادامه، اشتراک بگیرید یا دوره را بخرید.
بدون نیاز به پرداخت مبلغی برای شروع
اگر برنامهتان داده نگه میدارد و میخواهید کوئری را خودتان بنویسید و بدانید چرا کند است.
برای گرفتن گزارش فروش، مشتری و رشد ماهانه از پایگاه داده، بدون اینکه منتظر کس دیگری بمانید.
برای درس پایگاه داده و پروژههایی که با جدول و رابطه کار دارند.
پیشنیاز: یک رایانه و یک مرورگر. تجربهٔ برنامهنویسی لازم نیست.
اولین کوئریتان را همین امروز در مرورگر اجرا کنید. شروع رایگان است و در کمتر از دو دقیقه وارد محیط یادگیری میشوید.