OFFSET FETCH/TOP ํ์ด์ง, GETDATE/DATEDIFF ์ฐ์ฐ, ISNULL/IIF ์กฐ๊ฑด๋ฌธ, CTE ์ฌ๊ท ์ฟผ๋ฆฌ, NOLOCK ํํธ, MERGE ๋ฐ DMV ์ฑ๋ฅ ์ง๋จ ๋ ํผ๋ฐ์ค
## 1. ํ์ด์ง ์ฟผ๋ฆฌ (TOP & OFFSET FETCH)
```sql
-- 1. SQL Server 2012 ์ด์ ํ์ค ํ์ด์ง (ORDER BY ํ์)
SELECT EmployeeID, FirstName, Salary
FROM Employees
ORDER BY EmployeeID
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY; -- 21๋ฒ์งธ๋ถํฐ 10ํ ์กฐํ
-- 2. ์์ N๊ฐ ํ ๋จ์ ์กฐํ (TOP ๊ตฌ๋ฌธ)
SELECT TOP (10) EmployeeID, FirstName, Salary
FROM Employees
ORDER BY Salary DESC;
-- TOP WITH TIES: ๋์ ์๊ฐ ์์ ๊ฒฝ์ฐ N๊ฐ๋ฅผ ์ด๊ณผํด๋ ๋ชจ๋ ํฌํจ
SELECT TOP (5) WITH TIES * FROM Employees ORDER BY Salary DESC;
```
## 2. ๋ ์ง & ์๊ฐ ํจ์ (GETDATE & DATEDIFF)
```sql
-- ํ์ฌ ์ผ์
SELECT GETDATE(), SYSDATETIME();
-- 1. ๋ ์ง ๊ฐ๊ฐ (DATEADD)
SELECT DATEADD(day, 7, GETDATE()) AS ์ผ์ฃผ์ผ_ํ;
SELECT DATEADD(month, -1, GETDATE()) AS ํ๋ฌ_์ ;
SELECT DATEADD(minute, 30, GETDATE()) AS 30๋ถ_ํ;
-- 2. ๋ ์ง ์ฐจ์ด ๊ณ์ฐ (DATEDIFF)
SELECT DATEDIFF(day, '2026-01-01', GETDATE()) AS ์ผ์์ฐจ์ด;
SELECT DATEDIFF(hour, '2026-09-26 09:00:00', GETDATE()) AS ์๊ฐ์ฐจ์ด;
-- 3. ๋ ์ง ๋ฌธ์์ด ํฌ๋งท ๋ณํ (CONVERT 120 = YYYY-MM-DD HH:MI:SS)
SELECT CONVERT(VARCHAR(19), GETDATE(), 120) AS ํ์คํฌ๋งท;
SELECT CONVERT(VARCHAR(8), GETDATE(), 112) AS YYYYMMDD;
-- 4. FORMAT ํจ์ (SQL Server 2012+)
SELECT FORMAT(GETDATE(), 'yyyy-MM-dd HH:mm:ss');
```
## 3. NULL ์ฒ๋ฆฌ & ์กฐ๊ฑด ๋ถ๊ธฐ (ISNULL, IIF, COALESCE)
```sql
-- 1. ISNULL: null์ด๋ฉด ๋์ฒด๊ฐ ๋ฐํ (Oracle NVL์ ๋์)
SELECT ISNULL(CommissionPct, 0) FROM Employees;
-- 2. COALESCE: null์ด ์๋ ์ฒซ ๋ฒ์งธ ๊ฐ ๋ฐํ
SELECT COALESCE(Phone, Mobile, Email, 'N/A') FROM Users;
-- 3. IIF: ๋จ์ผ ์กฐ๊ฑด 3ํญ ์ฐ์ฐ์ (SQL Server 2012+)
SELECT IIF(Salary >= 5000, '๊ณ ์๋', '์ผ๋ฐ') FROM Employees;
-- 4. CASE WHEN ์กฐ๊ฑด ๋ถ๊ธฐ
SELECT
CASE
WHEN Status = 'P' THEN '๋๊ธฐ์ค'
WHEN Status = 'A' THEN '์น์ธ'
WHEN Status = 'R' THEN '๋ฐ๋ ค'
ELSE '๊ธฐํ'
END AS StatusName
FROM Orders;
```
## 4. ๋ฌธ์์ด ์กฐ์ & ์ง๊ณ (CONCAT, STRING_AGG)
```sql
-- 1. ๋ฌธ์์ด ์ฐ๊ฒฐ (+ ์ฐ์ฐ์ ๋๋ CONCAT)
-- ๐ก + ์ฐ์ฐ์๋ ํ๋๋ผ๋ NULL์ด๋ฉด ์ ์ฒด๊ฐ NULL์ด ๋จ! ์์ ํ CONCAT ๊ถ์ฅ
SELECT CONCAT(FirstName, ' ', LastName) AS FullName FROM Employees;
-- 2. ๋ถ๋ถ ๋ฌธ์์ด & ์๋ฅด๊ธฐ (1๋ถํฐ ์์)
SELECT SUBSTRING('SQLSERVER', 1, 3); -- 'SQL'
SELECT LEFT('20260926', 4); -- '2026'
SELECT RIGHT('20260926', 2); -- '26'
-- 3. ๋ฌธ์์ด ์์น ๊ฒ์ (CHARINDEX)
SELECT CHARINDEX('@', 'user@example.com'); -- 5
-- 4. ์ฌ๋ฌ ํ์ ๋ฌธ์์ด์ ํ ์ค๋ก ํฉ์น๊ธฐ (STRING_AGG - 2017+)
SELECT DepartmentID,
STRING_AGG(FirstName, ', ') WITHIN GROUP (ORDER BY FirstName ASC) AS Members
FROM Employees
GROUP BY DepartmentID;
```
## 5. CTE & ์ฌ๊ท ๊ณ์ธต ์ฟผ๋ฆฌ (Common Table Expressions)
```sql
-- ์กฐ์ง๋/์นดํ
๊ณ ๋ฆฌ ํธ๋ฆฌ ์ฌ๊ท ์กฐํ
WITH DeptHierarchy AS (
-- Anchor Member: ์ต์์ ๋ถ์ (๋ฃจํธ)
SELECT DepartmentID, ParentDeptID, DeptName, 1 AS Depth
FROM Departments
WHERE ParentDeptID IS NULL
UNION ALL
-- Recursive Member: ํ์ ๋ถ์ ํ์
SELECT d.DepartmentID, d.ParentDeptID, d.DeptName, h.Depth + 1
FROM Departments d
INNER JOIN DeptHierarchy h ON d.ParentDeptID = h.DepartmentID
)
SELECT Depth, DeptName, DepartmentID, ParentDeptID
FROM DeptHierarchy
ORDER BY Depth, DepartmentID;
```
## 6. IDENTITY & MERGE INTO (Upsert)
```sql
-- 1. ์๋ ์ฆ๊ฐ PK (IDENTITY) ๋ฐ ๋ฐ๊ธ๋ ID ์กฐํ
CREATE TABLE Users (
UserID INT IDENTITY(1, 1) PRIMARY KEY,
UserName NVARCHAR(50) NOT NULL
);
INSERT INTO Users (UserName) VALUES ('ํ๊ธธ๋');
SELECT SCOPE_IDENTITY(); -- ๐ก ํ์ฌ ์ธ์
/์ค์ฝํ์์ ๋ฐฉ๊ธ ์์ฑ๋ PK ๊ฐ ๋ฐํ
-- 2. MERGE INTO (๋์์ฑ ๋ฝ ์ถฉ๋ ๋ฐฉ์ง๋ฅผ ์ํด WITH (HOLDLOCK) ํ์)
MERGE INTO TargetUsers WITH (HOLDLOCK) AS t
USING SourceUsers AS s
ON (t.UserID = s.UserID)
WHEN MATCHED THEN
UPDATE SET t.UserName = s.UserName, t.UpdatedAt = GETDATE()
WHEN NOT MATCHED THEN
INSERT (UserID, UserName, CreatedAt)
VALUES (s.UserID, s.UserName, GETDATE());
```
## 7. ํ
์ด๋ธ ํํธ (WITH (NOLOCK) ๋ฑ)
```sql
-- 1. WITH (NOLOCK): ์ปค๋ฐ๋์ง ์์ ๋ฐ์ดํฐ๋ ์ฝ์ (๋ํฐ ๋ฆฌ๋ ํ์ฉ)
-- ๋์ฉ๋ ์กฐํ ์ ๋ธ๋กํน(Lock)์ ์ ๋ฐํ์ง ์์ ์ฝ๊ธฐ ์ฑ๋ฅ ๊ทน๋ํ (์ค์๊ฐ ์ค์ ๋ฐ์ดํฐ๋ ๊ธ์ง)
SELECT * FROM Orders WITH (NOLOCK) WHERE OrderDate >= '2026-01-01';
-- 2. WITH (ROWLOCK, UPDLOCK): ์กฐํ ์ ํ ๋จ์ ๋ฐฐํ์ ์์ ๋ฝ ์ค์
SELECT * FROM Inventory WITH (UPDLOCK, ROWLOCK) WHERE ProductID = 100;
-- 3. WITH (INDEX(์ธ๋ฑ์ค๋ช
)): ํน์ ์ธ๋ฑ์ค ๊ฐ์ ์ง์
SELECT * FROM Users WITH (INDEX(IX_Users_Email)) WHERE Email = 'test@example.com';
```
## 8. ์๋์ฐ & ์์ ํจ์
```sql
SELECT
EmployeeID, DepartmentID, Salary,
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS RowNum,
RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS SalRank,
DENSE_RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS DenseRank,
SUM(Salary) OVER (PARTITION BY DepartmentID) AS DeptTotalSalary
FROM Employees;
```
## 9. ์ค๋ฌด ์ฑ๋ฅ ์ง๋จ & ์ธ์
๋ชจ๋ํฐ๋ง DMV
```sql
-- 1. ํ์ฌ ์คํ ์ค์ธ ์ฟผ๋ฆฌ ๋ฐ ๋๊ธฐ ์ํ ํ์ธ (sp_who2 ๋์ฒด DMV)
SELECT
r.session_id, r.status, r.blocking_session_id, r.wait_type, r.wait_time,
t.text AS sql_statement
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.session_id > 50;
-- 2. ๋ฝ(Lock) ๋๊ธฐ ์ค์ธ ์ธ์
ํ์ธ
SELECT
waiting_task_address, session_id, exec_context_id, wait_type, wait_duration_ms
FROM sys.dm_os_waiting_tasks
WHERE session_id > 50;
-- 3. ํน์ ์ธ์
๊ฐ์ ์ข
๋ฃ (KILL ์ธ์
ID)
-- KILL 73;
```
## 10. T-SQL ํ๋ก์์ & ์๋ฌ ์ฒ๋ฆฌ (TRY...CATCH)
```sql
CREATE PROCEDURE sp_TransferMoney
@FromAccount INT,
@ToAccount INT,
@Amount DECIMAL(18, 2)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION;
BEGIN TRY
-- ์ถ๊ธ
UPDATE Accounts SET Balance = Balance - @Amount WHERE AccountID = @FromAccount;
-- ์
๊ธ
UPDATE Accounts SET Balance = Balance + @Amount WHERE AccountID = @ToAccount;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
-- ์๋ฌ ์์ธ ์ฌ๋ฐ์
THROW;
END CATCH
END;
```
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์