مشاوره رایگان دریافت کنید !
ما هم اکنون آماده پاسخگویی هستیم!
برای مشاوره رایگان فرم زیر را پر کنید تا مشاوران ما با شما در ارتباط باشند.
siteQ
طراحی سایت ، سئو ، تولید محتوا ، گرافیک ، سوشال مدیا و...
برای مشاوره رایگان فرم زیر را پر کنید تا مشاوران ما با شما در ارتباط باشند.
طراحی سایت ، سئو ، تولید محتوا ، گرافیک ، سوشال مدیا و...
در دنیای امروز، مدیریت دادهها و استخراج اطلاعات ارزشمند از دیتابیسها یکی از پایههای اصلی توسعه نرمافزار و تحلیل داده است. Queryها ابزار اصلی ما برای ارتباط با دیتابیسها هستند. این مقاله به صورت جامع انواع Queryها، بهینهسازی آنها، Queryهای پیچیده و همچنین مثالهای عملی در SQL و NoSQL را پوشش میدهد.
چهار دستور اصلی برای کار با دادهها در SQL عبارتند از:
-- انتخاب دادهها
SELECT name, email FROM users WHERE status = 'active';
-- اضافه کردن دادهها
INSERT INTO users (name, email, status) VALUES ('Ali', 'ali@example.com', 'active');
-- بهروزرسانی دادهها
UPDATE users SET status = 'inactive' WHERE last_login < '2025-01-01';
-- حذف دادهها
DELETE FROM users WHERE status = 'inactive';
این دستورات به ما امکان فیلتر و ترکیب دادهها را میدهند:
-- فیلتر با WHERE
SELECT * FROM orders WHERE total > 1000;
-- INNER JOIN بین دو جدول
SELECT users.name, orders.total
FROM users
INNER JOIN orders ON users.id = orders.user_id;
-- گروهبندی دادهها و محاسبه مجموع
SELECT user_id, SUM(total) as total_spent
FROM orders
GROUP BY user_id
HAVING total_spent > 5000;
ایندکسها باعث افزایش سرعت جستجو در دیتابیس میشوند. برای مثال:
-- ایجاد ایندکس روی ستون email
CREATE INDEX idx_email ON users(email);
این کار باعث میشود جستجو بر اساس ایمیل بسیار سریعتر انجام شود.
SELECT name, email
FROM users
WHERE status = 'active'
ORDER BY name ASC
LIMIT 10;
توضیح: این Query ده کاربر فعال را بر اساس نام به ترتیب صعودی برمیگرداند.
JOINها برای ترکیب دادهها از چند جدول استفاده میشوند:
-- INNER JOIN: فقط رکوردهای مشترک بین دو جدول
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- LEFT JOIN: همه کاربران و سفارشهایشان (اگر سفارش نداشته باشند NULL نمایش داده میشود)
SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- RIGHT JOIN: همه سفارشها و اطلاعات کاربران (اگر کاربر نباشد NULL)
SELECT u.name, o.total
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- FULL OUTER JOIN: همه کاربران و همه سفارشها، با NULL در صورت نبود داده
SELECT u.name, o.total
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
Subquery یا Query تو در تو برای استخراج دادهها از یک Query دیگر کاربرد دارد:
-- دریافت کاربرانی که مجموع سفارشاتشان بالای 5000 است
SELECT name
FROM users
WHERE id IN (
SELECT user_id
FROM orders
GROUP BY user_id
HAVING SUM(total) > 5000
);
توضیح: ابتدا در Subquery مجموع سفارشات کاربران محاسبه شده و سپس تنها کاربرانی که بیش از 5000 سفارش داشتهاند انتخاب میشوند.
CASE برای اعمال شرایط در انتخاب دادهها استفاده میشود:
-- دستهبندی کاربران بر اساس مجموع خرید
SELECT u.name,
CASE
WHEN SUM(o.total) > 10000 THEN 'ویژه'
WHEN SUM(o.total) > 5000 THEN 'نقرهای'
ELSE 'عادی'
END AS user_status
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.name;
توضیح: با CASE میتوان وضعیت کاربران را بر اساس مجموع خریدشان دستهبندی کرد.
توابع Aggregation برای جمعبندی و تحلیل دادهها کاربرد دارند:
-- تعداد سفارشها و مجموع آنها برای هر کاربر
SELECT user_id, COUNT(*) as total_orders, SUM(total) as total_spent
FROM orders
GROUP BY user_id;
-- میانگین مبلغ سفارشها
SELECT AVG(total) as avg_order
FROM orders;
-- بیشترین و کمترین سفارش
SELECT MAX(total) as max_order, MIN(total) as min_order
FROM orders;
-- دریافت ۵ کاربر برتر بر اساس مجموع خرید، به همراه تعداد سفارش
SELECT u.name, COUNT(o.id) as order_count, SUM(o.total) as total_spent
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.name
ORDER BY total_spent DESC
LIMIT 5;
توضیح: این Query اطلاعاتی ترکیبی از کاربران و سفارشاتشان استخراج میکند و کاربران برتر را نمایش میدهد.
برای بررسی عملکرد یک Query و پیدا کردن نقاط ضعف، از دستور EXPLAIN یا ANALYZE استفاده میکنیم:
-- بررسی Query
EXPLAIN SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.total > 1000;
توضیح: این دستور نمایش میدهد که دیتابیس چگونه Query را اجرا میکند، چه ایندکسهایی استفاده میشوند و چه تعداد رکورد اسکن میشود.
ایندکسها مهمترین عامل افزایش سرعت جستجو هستند:
-- ایجاد ایندکس روی ستون user_id جدول orders
CREATE INDEX idx_user_id ON orders(user_id);
-- ایجاد ایندکس ترکیبی روی ستون user_id و total
CREATE INDEX idx_user_total ON orders(user_id, total);
Partitioning: تقسیم جدولهای بزرگ به بخشهای کوچک برای افزایش سرعت Queryهای حجیم.
با استفاده از Caching میتوان Queryهایی که مکرراً اجرا میشوند را سریعتر کرد:
-- مثال ساده در PHP با ذخیره نتایج Query در Cache
$cacheKey = 'top_users';
$cachedData = getCache($cacheKey);
if(!$cachedData) {
$result = $db->query("
SELECT u.name, SUM(o.total) as total_spent
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.name
ORDER BY total_spent DESC
LIMIT 10
");
$cachedData = $result->fetchAll();
setCache($cacheKey, $cachedData, 3600); // ذخیره یک ساعته
}
بازنویسی Query میتواند سرعت اجرای آن را بسیار افزایش دهد. مثال:
-- Query اولیه
SELECT * FROM orders WHERE MONTH(created_at) = 10;
-- Query بهینه با شرط تاریخ
SELECT * FROM orders
WHERE created_at >= '2025-10-01' AND created_at < '2025-11-01';
توضیح: استفاده از توابع روی ستونها باعث عدم استفاده از ایندکس میشود، بنابراین شرط تاریخ مستقیم سریعتر اجرا میشود.
برای Queryهای حجیم، پردازش دستهای بهتر است:
-- پردازش 1000 رکورد در هر بار
SELECT * FROM orders
ORDER BY id
LIMIT 1000 OFFSET 0;
SELECT * FROM orders
ORDER BY id
LIMIT 1000 OFFSET 1000;
توضیح: این روش باعث کاهش فشار روی سرور و جلوگیری از Timeout میشود.
گاهی لازم است Queryها به صورت پویا ساخته شوند. مثال:
<?php
$filters = [];
if(!empty($_GET['status'])) {
$filters[] = "status = '" . $_GET['status'] . "'";
}
if(!empty($_GET['min_total'])) {
$filters[] = "total >=" . (int)$_GET['min_total'];
}
$where = '';
if(count($filters) > 0) {
$where = 'WHERE ' . implode(' AND ', $filters);
}
$sql = "SELECT * FROM orders $where";
$result = $db->query($sql);
?>
توضیح: Query به صورت پویا بر اساس فیلترهای ورودی کاربر ساخته میشود. اما این روش در برابر SQL Injection آسیبپذیر است.
Prepared Statement برای جلوگیری از SQL Injection استفاده میشود:
<?php
$status = $_GET['status'] ?? '';
$min_total = $_GET['min_total'] ?? 0;
$sql = "SELECT * FROM orders WHERE status = :status AND total >= :min_total";
$stmt = $pdo->prepare($sql);
$stmt->execute(['status' => $status, 'min_total' => $min_total]);
$results = $stmt->fetchAll();
?>
توضیح: پارامترها به صورت امن به Query ارسال میشوند و احتمال حمله SQL Injection صفر است.
import psycopg2
conn = psycopg2.connect("dbname=test user=postgres password=secret")
cur = conn.cursor()
status = 'active'
min_total = 1000
cur.execute("""
SELECT * FROM orders
WHERE status = %s AND total >= %s
""", (status, min_total))
rows = cur.fetchall()
for row in rows:
print(row)
conn.close()
توضیح: در Python هم استفاده از پارامترها (placeholders) باعث امنیت و جلوگیری از SQL Injection میشود.
const mysql = require('mysql2/promise');
async function getOrders(status, minTotal) {
const connection = await mysql.createConnection({host:'localhost', user:'root', database:'shop'});
const [rows] = await connection.execute(
'SELECT * FROM orders WHERE status = ? AND total >= ?',
[status, minTotal]
);
await connection.end();
return rows;
}
getOrders('active', 1000).then(console.log);
توضیح: استفاده از ? و آرایه پارامترها مشابه Prepared Statement عمل میکند و امنیت Query حفظ میشود.
<?php
$conditions = [];
$params = [];
if(!empty($_GET['status'])) {
$conditions[] = "status = :status";
$params['status'] = $_GET['status'];
}
$where = '';
if(count($conditions) > 0) {
$where = 'WHERE ' . implode(' AND ', $conditions);
}
$sql = "SELECT user_id, COUNT(*) as total_orders, SUM(total) as total_spent
FROM orders $where
GROUP BY user_id";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$results = $stmt->fetchAll();
?>
توضیح: این روش ترکیبی از Dynamic Query و Prepared Statement است که هم انعطافپذیر و هم امن است.
MongoDB یک دیتابیس NoSQL مبتنی بر Document است که از JSON برای ذخیره داده استفاده میکند.
-- پیدا کردن همه کاربران فعال
db.users.find({ status: "active" });
-- پیدا کردن کاربران با مجموع سفارش بیش از 5000
db.orders.aggregate([
{ $group: { _id: "$user_id", total_spent: { $sum: "$total" } } },
{ $match: { total_spent: { $gt: 5000 } } }
]);
-- اضافه کردن یک کاربر جدید
db.users.insertOne({ name: "Ali", email: "ali@example.com", status: "active" });
-- بهروزرسانی وضعیت کاربران
db.users.updateMany(
{ last_login: { $lt: new Date("2025-01-01") } },
{ $set: { status: "inactive" } }
);
-- حذف کاربران غیرفعال
db.users.deleteMany({ status: "inactive" });
توضیح: در MongoDB عملیات CRUD با متدهای find, insertOne, updateMany و deleteMany انجام میشود و Aggregation Pipeline برای تحلیل دادهها استفاده میشود.
Redis یک دیتابیس Key-Value بسیار سریع است و بیشتر برای کشینگ و دادههای موقت استفاده میشود.
# ذخیره مقدار
SET user:1001 "Ali"
# خواندن مقدار
GET user:1001
# ذخیره Hash
HSET user:1001 name "Ali" status "active"
# دریافت همه فیلدها
HGETALL user:1001
# ذخیره لیست سفارشها
LPUSH orders:1001 500 700 300
# خواندن همه آیتمها
LRANGE orders:1001 0 -1
توضیح: Redis برای ذخیره دادههای کوچک و سریع بسیار مناسب است و عملیات روی Key-Value یا ساختارهای دادهای مانند Hash، List و Set انجام میشود.
Elasticsearch برای جستجوی سریع متن و تحلیل دادههای بزرگ استفاده میشود.
# جستجوی ساده با REST API
GET /users/_search
{
"query": {
"match": {
"status": "active"
}
}
}
# جستجوی شرطی و مرتبسازی
GET /orders/_search
{
"query": {
"range": {
"total": {
"gte": 1000
}
}
},
"sort": [
{ "total": "desc" }
]
}
# Aggregation برای محاسبه مجموع خرید هر کاربر
GET /orders/_search
{
"size": 0,
"aggs": {
"total_per_user": {
"terms": { "field": "user_id" },
"aggs": {
"sum_total": { "sum": { "field": "total" } }
}
}
}
}
توضیح: Elasticsearch از JSON برای Query و Aggregation استفاده میکند و برای تحلیل و جستجوی سریع دادههای بزرگ بسیار مناسب است.
Window Functions برای محاسباتی که نیاز به حفظ ردیفها دارند استفاده میشوند، مثل رتبهبندی، میانگین متحرک و جمع تجمعی.
-- رتبهبندی کاربران بر اساس مجموع خرید
SELECT user_id, SUM(total) as total_spent,
RANK() OVER (ORDER BY SUM(total) DESC) as rank
FROM orders
GROUP BY user_id;
-- محاسبه میانگین متحرک تعداد سفارشها
SELECT order_date, COUNT(*) as daily_orders,
AVG(COUNT(*)) OVER (ORDER BY order_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as moving_avg
FROM orders
GROUP BY order_date
ORDER BY order_date;
CTE برای ایجاد Queryهای خوانا و قابل نگهداری استفاده میشود:
-- CTE برای پیدا کردن کاربران فعال و مجموع خرید آنها
WITH user_totals AS (
SELECT user_id, SUM(total) as total_spent
FROM orders
GROUP BY user_id
)
SELECT u.name, ut.total_spent
FROM users u
INNER JOIN user_totals ut ON u.id = ut.user_id
WHERE ut.total_spent > 5000;
برای مدیریت دادههای سلسلهمراتبی (مانند دستهبندیها یا سازمان) از Recursive Query استفاده میکنیم:
-- پیدا کردن همه زیرمجموعههای یک دسته
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id
FROM categories
WHERE id = 1 -- دسته اصلی
UNION ALL
SELECT c.id, c.name, c.parent_id
FROM categories c
INNER JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree;
توضیح: این Query تمام زیرمجموعههای دسته اصلی را به صورت بازگشتی استخراج میکند.
PostgreSQL امکان ذخیره و Query دادههای JSON را دارد:
-- انتخاب کاربران که مقدار خاصی در JSON دارند
SELECT id, data->>'email' as email
FROM users
WHERE data->>'status' = 'active';
-- Aggregation روی دادههای JSON
SELECT data->>'user_id' as user_id, SUM((data->>'total')::numeric) as total_spent
FROM orders
GROUP BY data->>'user_id';
توضیح: استفاده از JSON در PostgreSQL انعطافپذیری بالایی برای ذخیره دادههای پیچیده فراهم میکند و امکان Query و Aggregation مستقیم روی آن وجود دارد.
فرض کنید یک فروشگاه آنلاین داریم با جداول زیر:
-- جدول کاربران
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100),
status VARCHAR(20),
created_at TIMESTAMP DEFAULT NOW()
);
-- جدول سفارشها
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
total NUMERIC(10,2),
order_date DATE,
status VARCHAR(20)
);
-- جدول محصولات
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
price NUMERIC(10,2),
category_id INT
);
-- جدول دستهبندی محصولات
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
parent_id INT
);
-- انتخاب همه کاربران فعال
SELECT * FROM users WHERE status = 'active';
-- اضافه کردن سفارش جدید
INSERT INTO orders (user_id, total, order_date, status)
VALUES (1, 1500, '2025-10-08', 'completed');
-- بهروزرسانی وضعیت سفارش
UPDATE orders SET status = 'shipped' WHERE id = 5;
-- حذف سفارش منقضی
DELETE FROM orders WHERE status = 'cancelled';
-- دریافت 5 کاربر برتر بر اساس مجموع خرید
SELECT u.name, SUM(o.total) as total_spent
FROM users u
INNER JOIN orders o ON u.id = o.user_id
GROUP BY u.name
ORDER BY total_spent DESC
LIMIT 5;
-- دستهبندی کاربران با CASE
SELECT u.name,
CASE
WHEN SUM(o.total) > 10000 THEN 'ویژه'
WHEN SUM(o.total) > 5000 THEN 'نقرهای'
ELSE 'عادی'
END AS user_status
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.name;
-- ایجاد ایندکس روی ستون user_id
CREATE INDEX idx_orders_user ON orders(user_id);
-- پردازش دستهای سفارشها
SELECT * FROM orders
ORDER BY id
LIMIT 1000 OFFSET 0;
<?php
$status = $_GET['status'] ?? 'completed';
$min_total = $_GET['min_total'] ?? 1000;
$sql = "SELECT user_id, SUM(total) as total_spent
FROM orders
WHERE status = :status AND total >= :min_total
GROUP BY user_id";
$stmt = $pdo->prepare($sql);
$stmt->execute(['status' => $status, 'min_total' => $min_total]);
$results = $stmt->fetchAll();
?>
-- رتبهبندی کاربران بر اساس مجموع خرید
SELECT user_id, SUM(total) as total_spent,
RANK() OVER (ORDER BY SUM(total) DESC) as rank
FROM orders
GROUP BY user_id;
-- CTE برای کاربران فعال با مجموع خرید
WITH user_totals AS (
SELECT user_id, SUM(total) as total_spent
FROM orders
GROUP BY user_id
)
SELECT u.name, ut.total_spent
FROM users u
INNER JOIN user_totals ut ON u.id = ut.user_id
WHERE ut.total_spent > 5000;
-- Recursive Query برای دستهبندی محصولات
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id
FROM categories
WHERE id = 1
UNION ALL
SELECT c.id, c.name, c.parent_id
FROM categories c
INNER JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree;
-- فرض کنید جدول orders یک ستون JSON به نام details دارد
SELECT details->>'product_id' as product_id,
SUM((details->>'total')::numeric) as total_spent
FROM orders
GROUP BY details->>'product_id';
نتیجهگیری Case Study: با این پروژه عملی، شما توانایی اجرای انواع Queryها از ساده تا پیشرفته، بهینهسازی عملکرد، استفاده از Query پویا و Prepared Statements، و کار با دادههای سلسلهمراتبی و JSON در PostgreSQL را کسب کردید.
در این مقاله جامع، شما با تمامی جنبههای Queryهای دیتابیس آشنا شدید، از سادهترین دستورهای SQL تا Queryهای پیشرفته و بهینهسازی شده، و همچنین نحوه کار با دیتابیسهای NoSQL را یاد گرفتید. نکات کلیدی شامل موارد زیر است:
توصیههای عملی:
با مطالعه و اجرای این مقاله، شما اکنون توانایی طراحی، اجرای و بهینهسازی Queryهای دیتابیس SQL و NoSQL را دارید و میتوانید پروژههای واقعی با دادههای پیچیده را مدیریت کنید.
پایان مقاله جامع Queryهای دیتابیس