ESC را فشار دهید تا بسته شود

زمیوس آموزش، یادگیری و سرگرمی

راهنمای جیبی Oracle SQL؛ خلاصه دستورات مهم + نکات Performance

اگر با Oracle Database کار می‌کنید، احتمالاً خیلی زود به این نتیجه می‌رسید که فقط بلد بودن چند دستور SELECT و JOIN کافی نیست.

در دنیای واقعی، چیزی که تفاوت ایجاد می‌کند این است که بتوانید کوئری درست، سریع، قابل نگهداری و بهینه بنویسید.

دقیقاً به همین دلیل، داشتن یک راهنمای جیبی Oracle SQL می‌تواند برای برنامه‌نویس، DBA، تحلیلگر داده و حتی کسی که در حال یادگیری SQL است، بسیار ارزشمند باشد.

در این مقاله آموزش Cheat Sheet اوراکلی، سعی کرده‌ام مهم‌ترین دستورات Oracle SQL را به زبان ساده و کاربردی مرور کنم و در کنار آن، نکات مهم Performance در Oracle SQL را هم بگویم؛ نکاتی که در پروژه‌های واقعی، جلوی کند شدن کوئری‌ها و مصرف بیهوده منابع را می‌گیرند.

اگر با Oracle Database کار می‌کنید، تسلط بر SQL یکی از مهم‌ترین مهارت‌هایی است که باید داشته باشید. چه یک توسعه‌دهنده باشید، چه تحلیلگر داده یا DBA، دانستن دستورات اصلی SQL به شما کمک می‌کند سریع‌تر داده‌ها را بازیابی، تحلیل و مدیریت کنید.

پیشنهاد می کنم این مقاله زیر رو حتما مطالعه کنی.

در این مقاله شما می خوانید

Oracle SQL زبان استانداردی است که برای کار با داده‌ها در پایگاه داده Oracle استفاده می‌شود. با این زبان می‌توانید:

  • داده‌ها را بخوانید
  • داده‌های جدید ثبت کنید
  • داده‌ها را ویرایش کنید
  • اطلاعات را حذف کنید
  • ساختار جدول‌ها و ایندکس‌ها را تغییر دهید
  • و از همه مهم‌تر، کوئری‌های بهینه و سریع بنویسید

خیلی‌ها SQL را فقط به چشم یک زبان Query می‌بینند، اما واقعیت این است که در Oracle، کیفیت نوشتن SQL مستقیماً روی سرعت سیستم، فشار روی سرور و تجربه کاربر اثر می‌گذارد.

مهم‌ترین دستورات Oracle SQL

در Oracle SQL، دستورات معمولاً به چند گروه اصلی تقسیم می‌شوند:

۱) دستورات Query

برای خواندن اطلاعات:

  • SELECT

۲) دستورات DML

برای تغییر داده‌ها:

  • INSERT
  • UPDATE
  • DELETE
  • MERGE

۳) دستورات DDL

برای ایجاد یا تغییر ساختار:

  • CREATE
  • ALTER
  • DROP
  • TRUNCATE

۴) دستورات مدیریت تراکنش TCL

برای کنترل ثبت یا برگشت تغییرات:

  • COMMIT
  • ROLLBACK
  • SAVEPOINT

۵) دستورات سطح دسترسی

برای مدیریت مجوزها:

  • GRANT
  • REVOKE

پایه‌ای‌ترین و پرکاربردترین دستور در Oracle SQL، دستور SELECT است.

				
					SELECT employee_id, first_name, salary
FROM employees;

				
			

این کوئری سه ستون از جدول employees را برمی‌گرداند.

نکته مهم

خیلی وقت‌ها افراد تازه‌کار از SELECT * استفاده می‌کنند:

				
					SELECT *
FROM employees;

				
			

این کار برای تست‌های سریع شاید بد نباشد، اما در پروژه واقعی معمولاً انتخاب خوبی نیست، چون:

  • داده بیشتری از حد نیاز خوانده می‌شود
  • مصرف I/O بیشتر می‌شود
  • شبکه و حافظه بیشتر درگیر می‌شوند
  • نگهداری کوئری سخت‌تر می‌شود

بهتر است همیشه فقط ستون‌های لازم را انتخاب کنید.

برای محدود کردن نتایج از WHERE استفاده می‌کنیم.

				
					SELECT employee_id, first_name, salary
FROM employees
WHERE salary > ۵۰۰۰;

				
			

چند شرط رایج:

				
					WHERE department_id = 10
WHERE salary BETWEEN 3000 AND 7000
WHERE department_id IN (10, 20, 30)
WHERE first_name LIKE 'A%'
WHERE commission_pct IS NULL

				
			

WHERE یکی از مهم‌ترین بخش‌های Performance هم هست، چون اگر درست نوشته شود، می‌تواند حجم زیادی از داده غیرضروری را حذف کند.

اگر بخواهید داده‌ها را مرتب ببینید، از ORDER BY استفاده می‌کنید.

				
					SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC;

				
			

مرتب‌سازی چندستونه:

				
					SELECT employee_id, first_name, department_id, salary
FROM employees
ORDER BY department_id ASC, salary DESC;

				
			

نکته Performance

مرتب‌سازی روی داده‌های زیاد می‌تواند سنگین باشد. پس:

  • فقط وقتی لازم است ORDER BY بگذارید
  • اگر کوئری حساس است، ایندکس و Execution Plan را بررسی کنید

Oracle توابع بسیار کاربردی دارد که در نوشتن کوئری حرفه‌ای خیلی کمک می‌کنند.

توابع متنی

				
					SELECT UPPER(first_name), LOWER(last_name), LENGTH(first_name)
FROM employees;

				
			
				
					SELECT SUBSTR(first_name, 1, 3)
FROM employees;

				
			

توابع عددی

				
					SELECT ROUND(salary, 0), TRUNC(salary, -2), MOD(salary, 1000)
FROM employees;

				
			

توابع تاریخ

				
					SELECT SYSDATE, ADD_MONTHS(SYSDATE, 3), MONTHS_BETWEEN(SYSDATE, hire_date)
FROM employees;

				
			

توابع تبدیل نوع

				
					SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS')
FROM dual;

				
			
				
					SELECT TO_DATE('2025-01-15', 'YYYY-MM-DD')
FROM dual;

				
			

توابع مدیریت Null

				
					SELECT NVL(commission_pct, 0)
FROM employees;

				
			
				
					SELECT COALESCE(commission_pct, bonus_pct, 0)
FROM employees;

				
			

شرط‌گذاری با CASE

				
					SELECT employee_id,
       salary,
       CASE
           WHEN salary < 3000 THEN 'LOW'
           WHEN salary BETWEEN 3000 AND 7000 THEN 'MEDIUM'
           ELSE 'HIGH'
       END AS salary_level
FROM employees;

				
			

وقتی بخواهید داده‌ها را خلاصه کنید، از GROUP BY و توابع تجمیعی استفاده می‌کنید.

				
					SELECT department_id,
       COUNT(*) AS emp_count,
       AVG(salary) AS avg_salary,
       MAX(salary) AS max_salary
FROM employees
GROUP BY department_id;

				
			

برای فیلتر کردن گروه‌ها از HAVING استفاده می‌شود:

				
					SELECT department_id,
       AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING AVG(salary) > ۵۰۰۰;

				
			

تفاوت WHERE و HAVING

  • WHERE روی ردیف‌ها قبل از گروه‌بندی اعمال می‌شود
  • HAVING روی گروه‌ها بعد از گروه‌بندی اعمال می‌شود

یکی از پایه‌های اصلی SQL حرفه‌ای، تسلط روی Join است.

INNER JOIN

				
					SELECT e.employee_id, e.first_name, d.department_name
FROM employees e
JOIN departments d
  ON e.department_id = d.department_id;

				
			

LEFT JOIN

				
					SELECT e.employee_id, e.first_name, d.department_name
FROM employees e
LEFT JOIN departments d
  ON e.department_id = d.department_id;

				
			

SELF JOIN

				
					SELECT e.employee_id,
       e.first_name AS employee_name,
       m.first_name AS manager_name
FROM employees e
LEFT JOIN employees m
  ON e.manager_id = m.employee_id;

				
			

نکته مهم

Join اشتباه می‌تواند خروجی را چند برابر کند یا باعث Cartesian Product شود. برای همین، شرط Join باید دقیق و شفاف باشد.

Subquery زمانی استفاده می‌شود که بخواهید از نتیجه یک Query در Query دیگر استفاده کنید.

				
					SELECT employee_id, first_name, salary
FROM employees
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
);

				
			

مثال دیگر:

				
					SELECT employee_id, first_name
FROM employees
WHERE department_id IN (
    SELECT department_id
    FROM departments
    WHERE location_id = 1700
);

				
			

در بعضی موارد هم از Correlated Subquery استفاده می‌شود، اما باید حواستان باشد که در دیتاست‌های بزرگ ممکن است Performance ضعیف‌تری داشته باشد.

تفاوت EXISTS و IN در Oracle SQL

اگر هدفتان بررسی وجود رکورد باشد، EXISTS معمولاً انتخاب خوبی است.

				
					SELECT d.department_id, d.department_name
FROM departments d
WHERE EXISTS (
    SELECT 1
    FROM employees e
    WHERE e.department_id = d.department_id
);

				
			

در حالی که IN برای مقایسه با لیستی از مقادیر مناسب‌تر است:

				
					SELECT department_id, department_name
FROM departments
WHERE department_id IN (
    SELECT department_id
    FROM employees
);

				
			

در Oracle، انتخاب بین EXISTS و IN به Execution Plan و ساختار داده هم بستگی دارد، اما از نظر مفهومی این تفاوت را خوب است در ذهن داشته باشید.

برای خواناتر کردن کوئری‌های پیچیده، WITH یا CTE فوق‌العاده مفید است.

				
					WITH dept_salary AS (
    SELECT department_id, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department_id
)
SELECT d.department_name, ds.avg_salary
FROM dept_salary ds
JOIN departments d
  ON ds.department_id = d.department_id;

				
			

این روش کمک می‌کند کوئری‌های بزرگ تمیزتر، قابل فهم‌تر و قابل نگهداری‌تر باشند.

اگر بخواهیم از قابلیت‌های حرفه‌ای Oracle SQL حرف بزنیم، حتماً باید به Analytic Functions اشاره کنیم.

ROW_NUMBER

				
					SELECT employee_id,
       first_name,
       department_id,
       salary,
       ROW_NUMBER() OVER (
           PARTITION BY department_id
           ORDER BY salary DESC
       ) AS rn
FROM employees;

				
			

مثلاً برای پیدا کردن بالاترین حقوق هر دپارتمان:

				
					SELECT *
FROM (
    SELECT employee_id,
           first_name,
           department_id,
           salary,
           ROW_NUMBER() OVER (
               PARTITION BY department_id
               ORDER BY salary DESC
           ) AS rn
    FROM employees
)
WHERE rn = 1;

				
			

RANK و DENSE_RANK

				
					SELECT employee_id,
       first_name,
       salary,
       RANK() OVER (ORDER BY salary DESC) AS rnk,
       DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rnk
FROM employees;

				
			

LAG و LEAD

				
					SELECT employee_id,
       hire_date,
       salary,
       LAG(salary) OVER (ORDER BY hire_date) AS prev_salary,
       LEAD(salary) OVER (ORDER BY hire_date) AS next_salary
FROM employees;

				
			

این توابع در گزارش‌گیری، تحلیل داده و حل مسائل پیچیده بسیار کاربردی‌اند.

دستورات INSERT، UPDATE، DELETE و MERGE

INSERT

				
					INSERT INTO employees (employee_id, first_name, salary, department_id)
VALUES (1001, 'Ali', 8000, 10);

				
			

UPDATE

				
					UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 10;

				
			

DELETE

				
					DELETE FROM employees
WHERE employee_id = 1001;

				
			

MERGE

				
					MERGE INTO employees e
USING new_employees n
   ON (e.employee_id = n.employee_id)
WHEN MATCHED THEN
    UPDATE SET e.salary = n.salary,
               e.department_id = n.department_id
WHEN NOT MATCHED THEN
    INSERT (employee_id, first_name, salary, department_id)
    VALUES (n.employee_id, n.first_name, n.salary, n.department_id);

				
			

MERGE در سناریوهای همگام‌سازی داده و ETL فوق‌العاده کاربردی است.

نکات مهم Performance در Oracle SQL

حالا می‌رسیم به بخشی که معمولاً بیشتر از Syntax اهمیت دارد: بهینه‌سازی کوئری در Oracle.

۱) از SELECT * دوری کنید

فقط ستون‌های لازم را انتخاب کنید.

۲) روی ستون ایندکس‌شده تابع نزنید

مثلاً این مدل معمولاً خوب نیست:

				
					WHERE TRUNC(hire_date) = DATE '2025-01-15'

				
			

نسخه بهتر:

				
					WHERE hire_date >= DATE '2025-01-15'
  AND hire_date <  DATE '2025-01-16'

				
			

۳) تبدیل نوع ضمنی را حذف کنید

اگر ستون عددی است، آن را با رشته مقایسه نکنید.

بد:

				
					WHERE employee_id = '100'

				
			

بهتر:

				
					WHERE employee_id = 100

				
			

۴) DISTINCT را بی‌دلیل استفاده نکنید

خیلی وقت‌ها DISTINCT فقط مشکل Join اشتباه را پنهان می‌کند.

۵) UNION ALL را به UNION ترجیح دهید

اگر حذف رکورد تکراری لازم نیست، UNION ALL سریع‌تر است.

۶) EXISTS را برای بررسی وجود در نظر بگیرید

در بسیاری از سناریوها، خواناتر و بهینه‌تر است.

۷) فیلترها را زودتر اعمال کنید

هرچه زودتر داده‌های غیرضروری حذف شوند، Join و Sort سبک‌تر می‌شود.

۸) Execution Plan را بررسی کنید

همیشه حدس نزنید؛ Plan را ببینید.

				
					EXPLAIN PLAN FOR
SELECT employee_id, first_name
FROM employees
WHERE department_id = 10;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

				
			

۹) Statistics را به‌روز نگه دارید

اگر Optimizer آمار درست نداشته باشد، احتمال انتخاب Plan ضعیف بالا می‌رود.

				
					BEGIN
  DBMS_STATS.GATHER_TABLE_STATS(
    ownname => 'HR',
    tabname => 'EMPLOYEES'
  );
END;
				
			

نمونه واقعی از بهینه‌سازی Query در Oracle

نسخه ضعیف

				
					SELECT DISTINCT *
FROM orders o
JOIN customers c
  ON o.customer_id = c.customer_id
WHERE TRUNC(o.order_date) = TO_DATE('2025-05-20', 'YYYY-MM-DD')
  AND c.customer_id = '100';

				
			

مشکلات این Query

  • استفاده از SELECT DISTINCT *
  • تابع روی ستون تاریخ
  • تبدیل نوع ضمنی
  • انتخاب همه ستون‌ها
  • احتمال پنهان شدن مشکل طراحی Join

نسخه بهتر

				
					SELECT o.order_id,
       o.order_date,
       o.total_amount,
       c.customer_name
FROM orders o
JOIN customers c
  ON o.customer_id = c.customer_id
WHERE o.order_date >= DATE '2025-05-20'
  AND o.order_date <  DATE '2025-05-21'
  AND c.customer_id = 100;

				
			

این نسخه هم تمیزتر است، هم حرفه‌ای‌تر، و هم شانس بیشتری برای استفاده درست از ایندکس دارد.

اشتباهات رایج در Oracle SQL

اگر بخواهم چند خطای پرتکرار را خیلی خلاصه بگویم، این‌ها مهم‌ترند:

  • استفاده زیاد از SELECT *
  • به‌کار بردن بی‌دلیل DISTINCT
  • نوشتن Join بدون شرط دقیق
  • استفاده از تابع روی ستون فیلترشده
  • مقایسه داده‌ها با نوع نامناسب
  • نادیده گرفتن Execution Plan
  • طراحی نامناسب ایندکس

سوالات متداول درباره دستورات Oracle SQL در اوراکل

سریع‌تر شدن کوئری در Oracle معمولاً با چند کار کلیدی اتفاق می‌افتد:

  • به جای SELECT * فقط ستون‌های لازم را بگیر.
  • شرط‌ها را طوری بنویس که قابل استفاده برای ایندکس (SARGable) باشند (روی ستون فیلتر تابع نزن).
  • از تبدیل نوع ضمنی جلوگیری کن (عدد را با رشته مقایسه نکن).
  • بی‌دلیل DISTINCT و UNION استفاده نکن (اگر حذف تکراری لازم نیست UNION ALL بزن).
  • حتماً Execution Plan را بررسی کن و مطمئن شو Statistics به‌روز هستند.

مثال (بد → بهتر):

WHERE TRUNC(order_date) = DATE ‘۲۰۲۵-۰۵-۲۰’

WHERE order_date >= DATE ‘۲۰۲۵-۰۵-۲۰’ AND order_date < DATE ‘۲۰۲۵-۰۵-۲۱’

  • IN یعنی «مقدار این ستون داخل یک لیست هست یا نه».
  • EXISTS یعنی «آیا حداقل یک رکورد مطابق شرط وجود دارد یا نه».

در عمل، اگر هدف «فقط بررسی وجود» باشد، EXISTS خیلی وقت‌ها انتخاب بهتری است (به‌خصوص وقتی زیرکوئری بزرگ است یا ساختار به Semi-Join تبدیل می‌شود).

اما پاسخ قطعی همیشه به Execution Plan و ایندکس‌ها بستگی دارد.

بهترین و تمیزترین روش، استفاده از Analytic Functions مثل ROW_NUMBER است. چون هم خواناست و هم معمولاً از روش‌های پیچیده‌تر مثل self join بهتر درمی‌آید.

مثال (بیشترین حقوق هر دپارتمان):

SELECT * FROM (
SELECT employee_id,first_name,department_id,salary,ROW_NUMBER() OVER (PARTITION BYdepartment_id
ORDER BY salary DESC) AS rn
FROM employees)
WHERE rn = 1;

اگر «چند نفر با حقوق برابرِ رتبه اول» هم باید بیایند، معمولاً RANK() مناسب‌تر است.

این یکی از پرتکرارترین سرچ‌هاست، چون خیلی‌ها ایندکس دارند ولی هنوز Full Table Scan می‌بینند. دلایل رایج:

  • روی ستون ایندکس‌شده تابع زده‌ای (مثل TRUNC(col) یا UPPER(col)).
  • Implicit Conversion رخ داده (مثل مقایسه ستون NUMBER با '۱۲۳').
  • Selectivity پایین است (مثلاً ستون فقط چند مقدار محدود دارد) و Full Scan منطقی‌تر است.
  • Statistics قدیمی/غلط است و Optimizer تصمیم اشتباه می‌گیرد.
  • شرط‌ها طوری نوشته شده‌اند که SARGable نیستند (مثلاً NVL(col,...) = ...).

راه‌حل‌ها:

  • کوئری را بدون تابع بازنویسی کن، یا اگر واقعاً لازم است:
  • از Function-Based Index استفاده کن:

جمع‌بندی

اگر بخواهیم خیلی ساده و انسانی بگوییم، Oracle SQL فقط حفظ کردن دستورها نیست.

اصل ماجرا این است که بدانید چه چیزی را، چگونه، و با چه هزینه‌ای از دیتابیس می‌خواهید.

یک Query شاید از نظر خروجی درست باشد، اما اگر بهینه نباشد، روی سیستم واقعی به سرعت تبدیل به گلوگاه می‌شود.

پس اگر می‌خواهید در Oracle SQL حرفه‌ای شوید، این چند اصل را همیشه یادتان باشد:

  • فقط داده‌ای را بخوان که لازم داری
  • شرط‌ها را هوشمندانه بنویس
  • به ایندکس‌ها احترام بگذار
  • Execution Plan را ببین
  • Analytic Functionها را یاد بگیر
  • و هیچ‌وقت Performance را به شانس واگذار نکن

سؤالی درباره این مقاله داری؟

اگر نکته‌ای در این مقاله برات مبهم بود یا خواستی بیشتر بدونی، همین حالا برام بنویس تا دقیق و صمیمی پاسخت رو بدم — مثل یه گفت‌وگوی واقعی 💬

برو به صفحه پرسش و پاسخ

میثم راد

من یه برنامه نویسم که حسابی با دیتابیس اوراکل رفیقم! از اونایی ام که تا چیزی رو کامل نفهمم،ول کن نیستم، یادگرفتن برام مثل بازیه، و نوشتن اینجا کمک می کنه تا چیزایی که یاد گرفتم رو با بقیه به شریک بشم، با هم پیشرفت کنیم.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *