راهنمای جامع دستورات T-SQL در SQL Server؛ Cheat Sheet کاربردی 2026
این راهنما یک مرجع سریع و کاربردی برای مهمترین دستورات T-SQL در Microsoft SQL Server است. مطالب بر اساس ساختار صفحه «SQL Server Cheat Sheet» منتشرشده در سال 2026 تنظیم شدهاند و از عملیات پایه CRUD تا JOINها، CTE، Window Functions، مدیریت Table، Index و ابزارهای Query Tuning را پوشش میدهند.
توضیحات به زبان فارسی نوشته شدهاند، اما نام دستورها، توابع، Keywordها، Data Typeها و سایر اصطلاحات مرتبط با SQL بدون ترجمه باقی ماندهاند تا بتوانید مثالها را مستقیماً در SQL Server Management Studio یا محیطهای مشابه اجرا و ویرایش کنید.
در این Cheat Sheet چه چیزهایی یاد میگیرید؟
- خواندن، درج، ویرایش و حذف داده با
SELECT، INSERT، UPDATE و DELETE
- فیلتر کردن، مرتبسازی و Pagination با
WHERE، TOP و OFFSET FETCH
- گروهبندی و محاسبات آماری با Aggregate Functions
- ترکیب Tableها با انواع
JOIN و APPLY
- ساخت Queryهای پیشرفته با Subquery،
CTE و Window Functions
- کار با قابلیتهای اختصاصی SQL Server مانند
IDENTITY، OUTPUT و TRY_CAST
- مدیریت Table، Index، Query Store و Execution Plan
تفاوت SQL و T-SQL چیست؟
SQL زبان استاندارد کار با پایگاه دادههای رابطهای است. T-SQL یا Transact-SQL توسعه مایکروسافت بر SQL استاندارد محسوب میشود و در SQL Server و Azure SQL Database استفاده میشود.
علاوه بر قابلیتهای استاندارد SQL، در T-SQL دستورها و توابع اختصاصی مانند TOP، TRY_CAST، IIF، CHOOSE، OUTPUT، CROSS APPLY، Temp Table، Table Variable و ساختارهای کنترلی وجود دارند.
دستورات پایه CRUD
چهار عملیات اصلی روی دادهها شامل خواندن، درج، ویرایش و حذف است. این عملیات با دستورهای SELECT، INSERT، UPDATE و DELETE انجام میشوند.
SELECT برای خواندن داده
با SELECT میتوانید یک یا چند Column را از یک Table دریافت کنید. استفاده از نام Columnها معمولاً خواناتر و کنترلشدهتر از SELECT * است.
-- Select specific columns SELECT first_name, last_name, email FROM customers; -- Select all columns SELECT * FROM products; -- Use aliases SELECT first_name AS display_name, email AS contact_email FROM customers;
INSERT INTO برای افزودن Row جدید
INSERT INTO برای درج یک Row، چند Row یا انتقال نتیجه یک SELECT به Table دیگر استفاده میشود.
-- Insert one row INSERT INTO customers (first_name, last_name, email) VALUES ('Sara', 'Ahmadi', 'sara@example.com'); -- Insert multiple rows INSERT INTO products (product_name, category, price) VALUES ('Keyboard', 'hardware', 85.00), ('Monitor', 'hardware', 420.00); -- Insert rows returned by another query INSERT INTO archived_orders (order_id, customer_id, total_amount) SELECT order_id, customer_id, total_amount FROM orders WHERE order_date < '2025-01-01';
UPDATE برای تغییر دادههای موجود
دستور UPDATE Rowهای موجود را تغییر میدهد. وجود شرط WHERE تعیین میکند کدام Rowها ویرایش شوند.
-- Update one customer UPDATE customers SET email = 'new-address@example.com' WHERE customer_id = 42; -- Update several columns UPDATE products SET price = price * 1.10, updated_at = GETDATE() WHERE category = 'hardware';
نکته مهم: اجرای UPDATE بدون WHERE تمام Rowهای Table را تغییر میدهد. پیش از اجرای نهایی، شرط را با یک SELECT آزمایش کنید.
DELETE برای حذف Rowها
DELETE Rowهای انتخابشده را حذف میکند. با حذف شرط WHERE، تمام Rowهای Table حذف میشوند، اما ساختار Table باقی میماند.
-- Delete selected rows DELETE FROM orders WHERE status = 'cancelled' AND created_at < '2025-01-01'; -- Delete every row while keeping the table DELETE FROM temporary_logs;
MERGE برای ترکیب INSERT، UPDATE و DELETE
MERGE یک دستور اختصاصی SQL Server است که Source و Target را مقایسه میکند و بر اساس وضعیت Match، عملیات موردنیاز را انجام میدهد.
MERGE INTO inventory AS target USING staging_inventory AS source ON target.sku = source.sku WHEN MATCHED THEN UPDATE SET target.quantity = source.quantity, target.updated_at = GETDATE() WHEN NOT MATCHED BY TARGET THEN INSERT (sku, product_name, quantity, created_at) VALUES (source.sku, source.product_name, source.quantity, GETDATE()) WHEN NOT MATCHED BY SOURCE THEN DELETE;
فیلتر کردن و مرتبسازی دادهها
فیلترها مشخص میکنند چه Rowهایی در نتیجه نمایش داده شوند. مرتبسازی نیز ترتیب نمایش نتیجه را کنترل میکند.
WHERE، AND و OR
SELECT * FROM orders WHERE status = 'shipped' AND total_amount > 100 AND (region = 'EU' OR region = 'ME');
IN و BETWEEN
IN مقدار را با یک List مقایسه میکند. BETWEEN برای محدوده Inclusive بهکار میرود؛ یعنی ابتدا و انتهای محدوده نیز در نتیجه قرار میگیرند.
SELECT * FROM products WHERE category IN ('hardware', 'software', 'books'); SELECT * FROM orders WHERE order_date BETWEEN '2026-01-01' AND '2026-03-31';
LIKE برای Pattern Matching
% هر تعداد Character را Match میکند.
_ دقیقاً یک Character را Match میکند.
SELECT * FROM customers WHERE email LIKE '%@example.com'; SELECT * FROM products WHERE sku LIKE 'PRD-____';
IS NULL و IS NOT NULL
برای بررسی مقدارهای NULL نباید از عملگر مساوی استفاده کرد. از IS NULL و IS NOT NULL استفاده کنید.
SELECT * FROM customers WHERE phone IS NULL; SELECT * FROM customers WHERE phone IS NOT NULL;
ORDER BY، TOP و Pagination
در SQL Server برای محدود کردن تعداد Rowها از TOP استفاده میشود. Keyword رایج LIMIT در SQL Server پشتیبانی نمیشود. برای Pagination نیز میتوان از OFFSET و FETCH NEXT استفاده کرد.
-- First 10 rows after sorting SELECT TOP 10 product_name, price FROM products ORDER BY price DESC; -- Top 5 percent SELECT TOP 5 PERCENT * FROM orders ORDER BY total_amount DESC; -- Page 3 with 20 rows per page SELECT * FROM customers ORDER BY created_at DESC OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY;
توجه: برای نتیجه قابل پیشبینی، TOP را همراه ORDER BY استفاده کنید. در مقابل، OFFSET FETCH به ORDER BY نیاز دارد.
Aggregation و گروهبندی
Aggregate Functions مجموعهای از Rowها را خلاصه میکنند و خروجیهایی مانند تعداد، مجموع، میانگین، کمترین و بیشترین مقدار میسازند.
COUNT، SUM، AVG، MIN و MAX
SELECT COUNT(*) AS total_orders, SUM(total_amount) AS total_revenue, AVG(total_amount) AS average_order_value, MIN(total_amount) AS smallest_order, MAX(total_amount) AS largest_order FROM orders WHERE order_date >= '2026-01-01';
GROUP BY
GROUP BY Rowهای دارای مقدار مشترک را در یک Group قرار میدهد تا Aggregate Function روی هر Group جداگانه اجرا شود.
SELECT category, COUNT(*) AS product_count, SUM(price) AS inventory_value FROM products GROUP BY category ORDER BY inventory_value DESC;
HAVING برای فیلتر Groupها
WHERE پیش از Grouping روی Rowها اعمال میشود، اما HAVING پس از Grouping روی نتیجه Aggregate اعمال میشود.
SELECT category, COUNT(*) AS product_count FROM products GROUP BY category HAVING COUNT(*) > 10 ORDER BY product_count DESC;
| Keyword |
زمان اعمال |
کاربرد |
WHERE |
پیش از GROUP BY |
فیلتر کردن Rowهای اولیه |
HAVING |
پس از GROUP BY |
فیلتر کردن Groupها و Aggregate Result |
STRING_AGG برای ترکیب Stringها
STRING_AGG مقدارهای String را با یک Separator در کنار هم قرار میدهد و از SQL Server 2017 در دسترس است.
SELECT department, STRING_AGG(employee_name, ', ') WITHIN GROUP (ORDER BY hire_date) AS team_members FROM employees GROUP BY department;
انواع JOIN در SQL Server
JOIN برای ترکیب Rowهای دو یا چند Table بر اساس یک رابطه مشترک استفاده میشود.
| نوع |
رفتار |
کاربرد رایج |
INNER JOIN |
فقط Rowهای Matchشده در هر دو Table |
نمایش دادههایی که رابطه معتبر دارند |
LEFT JOIN |
تمام Rowهای Table سمت چپ و Matchهای سمت راست |
نمایش همه رکوردهای اصلی حتی بدون رابطه |
RIGHT JOIN |
تمام Rowهای Table سمت راست و Matchهای سمت چپ |
حفظ کامل دادههای Table سمت راست |
FULL OUTER JOIN |
تمام Rowهای هر دو Table |
مقایسه کامل دو مجموعه داده |
CROSS JOIN |
تمام ترکیبهای ممکن |
ساخت Matrix یا Combination |
CROSS APPLY |
اجرای Subquery مرتبط برای هر Row؛ رفتار شبیه INNER JOIN |
Top N برای هر Parent |
OUTER APPLY |
مانند CROSS APPLY اما Parent بدون نتیجه را نیز نگه میدارد |
رفتار شبیه LEFT JOIN |
INNER JOIN
SELECT o.order_id, o.order_date, c.customer_name, c.email FROM orders AS o INNER JOIN customers AS c ON o.customer_id = c.customer_id;
LEFT JOIN
SELECT c.customer_name, COUNT(o.order_id) AS order_count FROM customers AS c LEFT JOIN orders AS o ON c.customer_id = o.customer_id GROUP BY c.customer_name;
RIGHT JOIN
SELECT p.product_name, SUM(oi.quantity) AS total_sold FROM order_items AS oi RIGHT JOIN products AS p ON oi.product_id = p.product_id GROUP BY p.product_name;
FULL OUTER JOIN
SELECT c.customer_name, o.order_id FROM customers AS c FULL OUTER JOIN orders AS o ON c.customer_id = o.customer_id;
CROSS JOIN
SELECT s.size_name, c.color_name FROM sizes AS s CROSS JOIN colors AS c;
CROSS APPLY و OUTER APPLY
CROSS APPLY و OUTER APPLY امکان اجرای یک Correlated Subquery یا Table-Valued Function را برای هر Row از Query بیرونی فراهم میکنند.
-- Top 3 orders for every customer SELECT c.customer_name, x.order_id, x.total_amount FROM customers AS c CROSS APPLY ( SELECT TOP 3 order_id, total_amount FROM orders WHERE customer_id = c.customer_id ORDER BY total_amount DESC ) AS x; -- Latest order while keeping customers with no orders SELECT c.customer_name, x.order_id, x.order_date FROM customers AS c OUTER APPLY ( SELECT TOP 1 order_id, order_date FROM orders WHERE customer_id = c.customer_id ORDER BY order_date DESC ) AS x;
Subquery و CTE
Subquery و CTE برای تقسیم Queryهای پیچیده به بخشهای قابل مدیریت استفاده میشوند.
Scalar Subquery
Scalar Subquery باید یک مقدار واحد برگرداند و میتواند در بخش SELECT یا شرطها استفاده شود.
SELECT product_name, price, price - (SELECT AVG(price) FROM products) AS difference_from_average FROM products;
Subquery همراه IN
SELECT customer_name, email FROM customers WHERE customer_id IN ( SELECT customer_id FROM orders WHERE total_amount > 500 );
EXISTS
EXISTS بررسی میکند که Subquery حداقل یک Row برمیگرداند یا خیر.
SELECT p.product_name FROM products AS p WHERE EXISTS ( SELECT 1 FROM order_items AS oi WHERE oi.product_id = p.product_id );
CTE با WITH
CTE یک Result Set نامگذاریشده و موقت برای Query جاری ایجاد میکند.
WITH monthly_revenue AS ( SELECT DATEFROMPARTS(YEAR(order_date), MONTH(order_date), 1) AS revenue_month, SUM(total_amount) AS revenue FROM orders GROUP BY YEAR(order_date), MONTH(order_date) ) SELECT revenue_month, revenue, SUM(revenue) OVER (ORDER BY revenue_month) AS running_total FROM monthly_revenue ORDER BY revenue_month;
Recursive CTE
Recursive CTE برای دادههای سلسلهمراتبی مانند ساختار سازمانی، دستهبندیهای Parent/Child و مسیرها کاربرد دارد.
WITH organization AS ( SELECT employee_id, employee_name, manager_id, 0 AS hierarchy_level FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.employee_id, e.employee_name, e.manager_id, o.hierarchy_level + 1 FROM employees AS e INNER JOIN organization AS o ON e.manager_id = o.employee_id ) SELECT employee_id, employee_name, hierarchy_level FROM organization ORDER BY hierarchy_level, employee_name;
Window Functions
Window Functions روی مجموعهای از Rowهای مرتبط با Row جاری محاسبه انجام میدهند، بدون آنکه Rowها را مانند GROUP BY به یک Row خلاصه کنند.
ROW_NUMBER، RANK، DENSE_RANK و NTILE
SELECT employee_name, department, salary, ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_number_value, RANK() OVER (ORDER BY salary DESC) AS rank_value, DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank_value, NTILE(4) OVER (ORDER BY salary DESC) AS salary_quartile FROM employees;
| Function |
رفتار |
ROW_NUMBER() |
شماره یکتای متوالی برای هر Row ایجاد میکند. |
RANK() |
برای مقدارهای برابر Rank یکسان میدهد و پس از Tie فاصله ایجاد میکند؛ مانند 1، 2، 2، 4. |
DENSE_RANK() |
برای Tie مقدار یکسان میدهد اما فاصله ایجاد نمیکند؛ مانند 1، 2، 2، 3. |
NTILE(n) |
Rowها را به تعداد Group مشخص تقسیم میکند. |
PARTITION BY
PARTITION BY Window را به بخشهای مستقل تقسیم میکند. مثال زیر کارکنان را داخل هر Department رتبهبندی میکند.
SELECT employee_name, department, salary, RANK() OVER ( PARTITION BY department ORDER BY salary DESC ) AS department_rank FROM employees;
LAG و LEAD
LAG مقدار Row قبلی و LEAD مقدار Row بعدی را در Window برمیگرداند.
SELECT revenue_month, revenue, LAG(revenue, 1) OVER (ORDER BY revenue_month) AS previous_month, LEAD(revenue, 1) OVER (ORDER BY revenue_month) AS next_month, revenue - LAG(revenue, 1) OVER (ORDER BY revenue_month) AS month_change FROM monthly_revenue;
Running Total و Moving Average
-- Running total SELECT order_date, total_amount, SUM(total_amount) OVER ( ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS running_total FROM orders; -- 7-row moving average SELECT sales_date, daily_revenue, AVG(daily_revenue) OVER ( ORDER BY sales_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ) AS moving_average_7 FROM daily_sales;
قابلیتهای اختصاصی SQL Server
IDENTITY و SCOPE_IDENTITY
IDENTITY(seed, increment) برای تولید مقدار Auto-Increment استفاده میشود. برای دریافت آخرین Identity ثبتشده در Scope جاری، SCOPE_IDENTITY() انتخاب مناسبتری نسبت به @@IDENTITY است، زیرا @@IDENTITY ممکن است مقدار درجشده توسط Trigger را برگرداند.
CREATE TABLE app_users ( user_id INT IDENTITY(1,1) PRIMARY KEY, user_name NVARCHAR(100) NOT NULL, email NVARCHAR(255) NOT NULL ); INSERT INTO app_users (user_name, email) VALUES (N'Sara', N'sara@example.com'); SELECT SCOPE_IDENTITY() AS new_user_id;
ISNULL و COALESCE
ISNULL مختص SQL Server است و دقیقاً دو Argument دریافت میکند.
COALESCE بخشی از SQL استاندارد است و اولین مقدار Non-NULL را از چند Argument برمیگرداند.
SELECT ISNULL(phone, 'N/A') AS phone FROM customers; SELECT COALESCE(mobile_phone, work_phone, home_phone, 'N/A') AS contact_number FROM customers;
TOP WITH TIES
WITH TIES علاوه بر N Row اول، Rowهایی را که در آخرین جایگاه مقدار برابر دارند نیز برمیگرداند.
SELECT TOP 5 WITH TIES product_name, price FROM products ORDER BY price DESC;
OUTPUT Clause
OUTPUT امکان مشاهده Rowهای درجشده، حذفشده یا مقدارهای پیش و پس از Update را فراهم میکند.
-- Return inserted values INSERT INTO app_users (user_name, email) OUTPUT INSERTED.user_id, INSERTED.user_name, INSERTED.email VALUES (N'Ali', N'ali@example.com'); -- Return deleted values DELETE FROM orders OUTPUT DELETED.order_id, DELETED.customer_id, DELETED.total_amount WHERE status = 'cancelled'; -- Return old and new values UPDATE products SET price = price * 1.10 OUTPUT DELETED.price AS old_price, INSERTED.price AS new_price, INSERTED.product_id WHERE category = 'hardware';
TRY_CAST و TRY_CONVERT
در صورت نامعتبر بودن Conversion، این توابع بهجای ایجاد Error مقدار NULL برمیگردانند.
SELECT TRY_CAST('abc' AS INT) AS invalid_integer; SELECT TRY_CAST('123' AS INT) AS valid_integer; SELECT TRY_CONVERT(DATE, '2026-13-01') AS invalid_date; SELECT TRY_CONVERT(DATE, '2026-03-12') AS valid_date;
IIF و CHOOSE
SELECT employee_name, salary, IIF(salary > 100000, 'Senior', 'Standard') AS salary_band FROM employees; SELECT order_id, CHOOSE( MONTH(order_date), 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ) AS order_month FROM orders;
Temp Table و Table Variable
SQL Server سه الگوی رایج برای داده موقت دارد: Local Temp Table، Global Temp Table و Table Variable.
-- Local temp table: current session CREATE TABLE #recent_orders ( order_id INT, customer_id INT, total_amount DECIMAL(12,2) ); INSERT INTO #recent_orders SELECT order_id, customer_id, total_amount FROM orders WHERE order_date >= '2026-01-01'; -- Global temp table: visible to sessions while it exists CREATE TABLE ##shared_cache ( cache_key NVARCHAR(100), cache_value NVARCHAR(MAX) ); -- Table variable: current batch or procedure scope DECLARE @top_customers TABLE ( customer_id INT, total_spent DECIMAL(12,2) ); INSERT INTO @top_customers SELECT TOP 100 customer_id, SUM(total_amount) FROM orders GROUP BY customer_id ORDER BY SUM(total_amount) DESC; SELECT * FROM @top_customers;
| ساختار |
Scope |
Syntax |
| Local Temp Table |
Session جاری |
#table_name |
| Global Temp Table |
Sessionهای مختلف تا زمان موجود بودن Object |
##table_name |
| Table Variable |
Batch، Function یا Stored Procedure جاری |
@table_name |
NEWID برای UNIQUEIDENTIFIER
SELECT NEWID() AS generated_id; CREATE TABLE user_sessions ( session_id UNIQUEIDENTIFIER DEFAULT NEWID() PRIMARY KEY, user_id INT NOT NULL, created_at DATETIME2 DEFAULT SYSDATETIME() );
توابع String در T-SQL
توابع String برای ترکیب، برش، پاکسازی، جستوجو، جایگزینی و Format کردن متن استفاده میشوند.
-- CONCAT is NULL-safe SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM customers; -- + operator propagates NULL SELECT first_name + ' ' + last_name AS full_name FROM customers; -- Extract part of a string; position is 1-based SELECT SUBSTRING(phone, 1, 3) AS area_code FROM contacts; -- Remove spaces SELECT TRIM(product_name) FROM products; SELECT LTRIM(RTRIM(product_name)) FROM products; -- Change letter case SELECT UPPER(email) FROM customers; SELECT LOWER(city) FROM addresses; -- String length, excluding trailing spaces SELECT product_name, LEN(product_name) AS name_length FROM products; -- Replace text SELECT REPLACE(url, 'http://', 'https://') AS secure_url FROM links; -- Find substring position; 0 means not found SELECT CHARINDEX('@', email) AS at_position FROM customers; -- Delete and insert at a position SELECT STUFF('SQL Server', 5, 6, 'Database') AS changed_text; -- Combine grouped values SELECT department, STRING_AGG(employee_name, ', ') AS members FROM employees GROUP BY department; -- Locale-aware formatting SELECT FORMAT(price, 'C', 'en-US') AS formatted_price FROM products;
نکته نسخه: TRIM و STRING_AGG در SQL Server 2017 و نسخههای جدیدتر در دسترس هستند. برای نسخههای قدیمیتر میتوان از ترکیب LTRIM و RTRIM برای حذف فاصله ابتدا و انتهای String استفاده کرد.
توابع Date و Time
توابع Date و Time در T-SQL برای دریافت زمان جاری، افزودن Interval، محاسبه اختلاف، استخراج Date Part، Format و ساخت Date استفاده میشوند.
-- Current date and time SELECT GETDATE(); SELECT SYSDATETIME(); SELECT GETUTCDATE(); -- Add or subtract intervals SELECT DATEADD(DAY, 7, order_date) AS one_week_later FROM orders; SELECT DATEADD(MONTH, -3, GETDATE()) AS three_months_ago; -- Difference between dates SELECT DATEDIFF(DAY, ship_date, delivery_date) AS transit_days FROM shipments; -- Extract date parts SELECT DATEPART(WEEKDAY, order_date) AS weekday_number, DATEPART(QUARTER, order_date) AS quarter_number FROM orders; SELECT YEAR(order_date) AS order_year, MONTH(order_date) AS order_month, DAY(order_date) AS order_day FROM orders; -- Format date values SELECT FORMAT(order_date, 'yyyy-MM-dd') AS iso_date FROM orders; -- CONVERT style codes SELECT CONVERT(VARCHAR, GETDATE(), 23) AS iso_date; SELECT CONVERT(VARCHAR, GETDATE(), 101) AS us_date; SELECT CONVERT(VARCHAR, GETDATE(), 108) AS time_value; -- End of month SELECT EOMONTH(GETDATE()) AS current_month_end; SELECT EOMONTH(GETDATE(), -1) AS previous_month_end; SELECT EOMONTH(GETDATE(), 2) AS future_month_end; -- Build values from parts SELECT DATEFROMPARTS(2026, 3, 12) AS created_date; SELECT DATETIME2FROMPARTS(2026, 3, 12, 14, 30, 0, 0, 0) AS created_datetime; -- Last 30 days SELECT * FROM orders WHERE order_date >= DATEADD(DAY, -30, GETDATE());
| Function |
خروجی یا کاربرد |
GETDATE() |
تاریخ و زمان جاری با Data Type از نوع DATETIME |
SYSDATETIME() |
تاریخ و زمان جاری با Data Type از نوع DATETIME2 و Precision بالاتر |
GETUTCDATE() |
تاریخ و زمان جاری بر اساس UTC |
DATEADD() |
افزودن یا کمکردن Interval |
DATEDIFF() |
محاسبه اختلاف دو Date بر اساس Date Part تعیینشده |
DATEPART() |
استخراج بخش خاصی از Date بهصورت Integer |
EOMONTH() |
دریافت آخرین روز ماه |
DATEFROMPARTS() |
ساخت مقدار DATE از Year، Month و Day |
عملیات مربوط به Table
CREATE TABLE
CREATE TABLE customers ( customer_id INT IDENTITY(1,1) PRIMARY KEY, first_name NVARCHAR(100) NOT NULL, last_name NVARCHAR(100) NOT NULL, email NVARCHAR(255) UNIQUE NOT NULL, created_at DATETIME2 DEFAULT SYSDATETIME(), is_active BIT DEFAULT 1 );
ALTER TABLE
-- Add a column ALTER TABLE customers ADD phone NVARCHAR(20); -- Change a column definition ALTER TABLE products ALTER COLUMN price DECIMAL(12,2) NOT NULL; -- Add a foreign key ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (customer_id) REFERENCES customers(customer_id); -- Add a default constraint ALTER TABLE customers ADD CONSTRAINT df_customers_status DEFAULT 'active' FOR status; -- Drop a column ALTER TABLE customers DROP COLUMN phone;
DROP TABLE
-- SQL Server 2016+ DROP TABLE IF EXISTS import_staging; -- Older versions IF OBJECT_ID('import_staging', 'U') IS NOT NULL DROP TABLE import_staging;
CREATE INDEX و DROP INDEX
-- Nonclustered index CREATE NONCLUSTERED INDEX idx_customers_email ON customers(email); -- Composite index CREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date); -- Unique index CREATE UNIQUE INDEX idx_customers_email_unique ON customers(email); -- Clustered index CREATE CLUSTERED INDEX idx_orders_date ON orders(order_date); -- Covering index with included columns CREATE INDEX idx_orders_status ON orders(status) INCLUDE (customer_id, total_amount); -- Drop an index DROP INDEX idx_customers_email ON customers;
TRUNCATE TABLE
TRUNCATE TABLE تمام Rowها را حذف میکند و Identity را Reset میکند. این دستور برای حذف کامل دادههای Table با حفظ ساختار استفاده میشود.
TRUNCATE TABLE temporary_logs;
بررسی ساختار Table با sp_help و sp_columns
EXEC sp_help 'customers'; EXEC sp_columns 'customers';
Query Tuning و بررسی Performance
SET STATISTICS IO و SET STATISTICS TIME
این تنظیمات اطلاعات Logical Read، CPU Time و Elapsed Time را در بخش Messages نمایش میدهند.
SET STATISTICS IO ON; SET STATISTICS TIME ON; SELECT c.customer_name, COUNT(o.order_id) AS order_count FROM customers AS c INNER JOIN orders AS o ON c.customer_id = o.customer_id GROUP BY c.customer_name; SET STATISTICS IO OFF; SET STATISTICS TIME OFF;
Estimated و Actual Execution Plan
Estimated Plan بدون اجرای Query، Plan تخمینی را ارائه میکند. Actual Plan Query را اجرا میکند و اطلاعات واقعی مانند تعداد Rowها را نشان میدهد.
-- Estimated plan SET SHOWPLAN_XML ON; GO SELECT * FROM orders WHERE customer_id = 42; GO SET SHOWPLAN_XML OFF; -- Actual plan SET STATISTICS XML ON; SELECT * FROM orders WHERE customer_id = 42; SET STATISTICS XML OFF;
در SSMS میتوانید از Ctrl+L برای Estimated Plan و Ctrl+M برای فعال کردن Actual Plan استفاده کنید.
انواع Index در یک نگاه
| Index Type |
ویژگی اصلی |
کاربرد |
Clustered Index |
ترتیب فیزیکی Rowها را تعیین میکند و هر Table فقط یک مورد دارد. |
اغلب برای Primary Key یا Column اصلی مرتبسازی |
Nonclustered Index |
ساختاری جداگانه برای اشاره به Rowها ایجاد میکند و هر Table میتواند چند مورد داشته باشد. |
Columnهای استفادهشده در WHERE، JOIN و ORDER BY |
Columnstore Index |
داده را بهصورت Column-Based ذخیره میکند و Compression بالایی دارد. |
Analytics و Aggregation روی Tableهای بزرگ |
Filtered Index |
فقط بخشی از دادهها را با شرط WHERE Index میکند. |
دادههای Partial مانند Rowهای Active |
Query Store
Query Store برای نگهداری History مربوط به Query، Plan و Runtime Statistics استفاده میشود.
ALTER DATABASE SalesDatabase SET QUERY_STORE = ON; SELECT TOP 10 qt.query_sql_text, rs.avg_duration, rs.avg_logical_io_reads, rs.count_executions FROM sys.query_store_query_text AS qt INNER JOIN sys.query_store_query AS q ON qt.query_text_id = q.query_text_id INNER JOIN sys.query_store_plan AS p ON q.query_id = p.query_id INNER JOIN sys.query_store_runtime_stats AS rs ON p.plan_id = rs.plan_id ORDER BY rs.avg_duration DESC;
Dynamic SQL با sp_executesql
در Dynamic SQL، استفاده از Parameter بهجای اتصال مستقیم مقدارها به String، Query را قابل کنترلتر میکند و از الگوی Parameterized Query پیروی میکند.
DECLARE @sql NVARCHAR(MAX); DECLARE @category NVARCHAR(50) = N'hardware'; DECLARE @min_price DECIMAL(12,2) = 100; SET @sql = N' SELECT product_name, price FROM products WHERE category = @category_parameter AND price >= @price_parameter ORDER BY price DESC'; EXEC sp_executesql @sql, N'@category_parameter NVARCHAR(50), @price_parameter DECIMAL(12,2)', @category_parameter = @category, @price_parameter = @min_price;
اشتباهات رایج
- اجرای
UPDATE یا DELETE بدون WHERE: این کار تمام Rowهای Table را تحت تأثیر قرار میدهد.
- استفاده از
LIMIT در SQL Server: برای محدود کردن نتیجه باید از TOP یا OFFSET FETCH استفاده شود.
- مقایسه
NULL با علامت مساوی: عبارتهایی مانند phone = NULL درست نیستند؛ از IS NULL استفاده کنید.
- استفاده از Aggregate Function در
WHERE: شرط Aggregate معمولاً باید در HAVING قرار گیرد.
- استفاده از
@@IDENTITY بدون توجه به Trigger: برای Scope جاری، SCOPE_IDENTITY() یا OUTPUT INSERTED مناسبتر است.
- Pagination بدون ترتیب مشخص:
OFFSET FETCH به ORDER BY نیاز دارد.
- استفاده نادرست از
RIGHT JOIN: در بسیاری از Queryها میتوان با جابهجایی ترتیب Tableها و استفاده از LEFT JOIN خوانایی را بیشتر کرد؛ اما رفتار اصلی هر دو باید بهدرستی درک شود.
- نادیده گرفتن Execution Plan: Query صحیح ممکن است روی داده زیاد کند باشد؛ Plan و Statistics به شناسایی Bottleneck کمک میکنند.
بهترین روشها
- بهجای
SELECT * فقط Columnهای موردنیاز را انتخاب کنید.
- پیش از اجرای دستورهای تغییردهنده، شرط
WHERE را با SELECT بررسی کنید.
- برای Result Set محدود،
TOP را همراه ORDER BY بنویسید.
- برای داده فارسی یا Unicode از Data Typeهای
NCHAR و NVARCHAR و در Literalها از پیشوند N استفاده کنید.
- در Dynamic SQL از
sp_executesql و Parameter استفاده کنید.
- Index را بر اساس الگوی واقعی
WHERE، JOIN و ORDER BY طراحی کنید.
- برای Queryهای سنگین،
SET STATISTICS IO، SET STATISTICS TIME و Execution Plan را بررسی کنید.
- تفاوت
WHERE و HAVING، و همچنین تفاوت ROW_NUMBER، RANK و DENSE_RANK را در Queryهای تحلیلی در نظر بگیرید.
- برای Conversionهای ورودی نامطمئن،
TRY_CAST یا TRY_CONVERT را بهکار ببرید.
مثال عملی یک Query گزارشگیری
در سناریوی زیر، درآمد ماهانه هر Customer محاسبه میشود، تنها Customerهایی که مجموع خریدشان بیش از 1000 است باقی میمانند و رتبه آنها داخل هر Month تعیین میشود.
WITH customer_monthly_sales AS ( SELECT DATEFROMPARTS(YEAR(o.order_date), MONTH(o.order_date), 1) AS sales_month, c.customer_id, c.customer_name, COUNT(o.order_id) AS order_count, SUM(o.total_amount) AS total_revenue FROM customers AS c INNER JOIN orders AS o ON c.customer_id = o.customer_id WHERE o.status = 'completed' AND o.order_date >= '2026-01-01' GROUP BY YEAR(o.order_date), MONTH(o.order_date), c.customer_id, c.customer_name HAVING SUM(o.total_amount) > 1000 ) SELECT sales_month, customer_id, customer_name, order_count, total_revenue, DENSE_RANK() OVER ( PARTITION BY sales_month ORDER BY total_revenue DESC ) AS revenue_rank FROM customer_monthly_sales ORDER BY sales_month, revenue_rank, customer_name;
پرسشهای متداول
برای محدود کردن نتیجه در SQL Server از چه دستوری استفاده میشود؟
از TOP N برای دریافت N Row اول استفاده میشود. برای Pagination میتوان از ORDER BY ... OFFSET ... FETCH NEXT استفاده کرد.
CROSS APPLY چه تفاوتی با INNER JOIN دارد؟
CROSS APPLY میتواند برای هر Row از Query بیرونی یک Correlated Subquery یا Table-Valued Function را اجرا کند و به Columnهای همان Row دسترسی داشته باشد. نتیجه آن از نظر حفظ Rowها شبیه رفتار INNER JOIN است. OUTER APPLY Rowهای بدون Match را نیز نگه میدارد.
شناسه Row تازه درجشده را چگونه دریافت کنیم؟
برای Columnهای IDENTITY میتوان از SCOPE_IDENTITY() استفاده کرد. راه دیگر استفاده از OUTPUT INSERTED.column_name در همان دستور INSERT است.
تفاوت WHERE و HAVING چیست؟
WHERE Rowهای اولیه را پیش از Grouping فیلتر میکند. HAVING پس از GROUP BY برای فیلتر کردن Groupها یا نتیجه Aggregate Functionها استفاده میشود.
برای تبدیل امن String به Number یا Date چه کنیم؟
از TRY_CAST یا TRY_CONVERT استفاده کنید. در صورت نامعتبر بودن مقدار، خروجی NULL خواهد بود.
چگونه هزینه Query را بررسی کنیم؟
SET STATISTICS IO ON و SET STATISTICS TIME ON اطلاعات I/O و زمان را نمایش میدهند. Estimated یا Actual Execution Plan نیز مسیر اجرای Query را نشان میدهد.
جمعبندی
این Cheat Sheet مهمترین بخشهای T-SQL در SQL Server را در یک مسیر منظم پوشش میدهد: ابتدا عملیات CRUD، سپس فیلتر، Aggregation و JOIN، بعد Subquery، CTE و Window Functions، و در ادامه قابلیتهای اختصاصی SQL Server، توابع String و Date، مدیریت Table و ابزارهای Query Tuning.
برای استفاده روزمره، لازم نیست تمام Syntaxها را حفظ کنید. مهمتر این است که بدانید برای هر مسئله باید سراغ کدام دسته از دستورها بروید، شرطها در چه مرحلهای اعمال میشوند و چگونه نتیجه و Performance Query را بررسی کنید.
چکلیست نهایی SQL Server
- برای خواندن داده از
SELECT و برای تغییر داده از دستور مناسب CRUD استفاده کردهاید.
- دستورهای
UPDATE و DELETE دارای WHERE کنترلشده هستند.
- برای محدود کردن نتیجه از
TOP یا OFFSET FETCH استفاده شده است، نه LIMIT.
- مقدارهای
NULL با IS NULL یا IS NOT NULL بررسی میشوند.
- شرط Rowها در
WHERE و شرط Groupها در HAVING قرار گرفته است.
- نوع
JOIN با نیاز واقعی Result Set هماهنگ است.
- برای Ranking یا Running Total از Window Functions استفاده شده است.
- برای دریافت Identity جدید از
SCOPE_IDENTITY() یا OUTPUT استفاده شده است.
- Dynamic SQL با
sp_executesql و Parameter نوشته شده است.
- برای Queryهای مهم، Statistics و Execution Plan بررسی شدهاند.
منبع تدوین مقاله
این مقاله بهصورت مستقل و فارسی، بر اساس موضوعات و ترتیب آموزشی صفحه زیر تدوین شده است:
- SQL Server Cheat Sheet: Every T-SQL Command You Need (2026) – AI2SQL
تاریخ درجشده در صفحه منبع: 12 مارس 2026. مثالها برای انتشار فارسی بازنویسی و یکپارچه شدهاند و نام دستورها و اصطلاحات SQL بدون ترجمه حفظ شدهاند.