본문 바로가기
● Data Insights/SQL

MSSQL에서 자주 발생하는 문제 쿼리와 튜닝 솔루션

by DATA Canvas 2025. 11. 3.
반응형

데이터베이스 성능 저하의 주요 원인은 비효율적인 쿼리 작성에 있습니다. 아래 주요 사례별로 문제 쿼리, 원인, 그리고 변경 쿼리를 제시합니다. 이를 참고해 쿼리 성능을 크게 개선할 수 있습니다.


1. SELECT * 사용으로 인한 불필요한 I/O 증가

문제 쿼리

SELECT *
FROM Sales.SalesOrderHeader
WHERE OrderDate >= '2025-01-01';

원인

  • 테이블의 모든 컬럼을 가져와 불필요한 데이터 I/O와 네트워크 전송 비용이 발생
  • 실제 필요한 컬럼을 명시하지 않아 SQL Server가 테이블 스캔 유도

변경 쿼리

SELECT OrderID, CustomerID, OrderDate, TotalDue
FROM Sales.SalesOrderHeader
WHERE OrderDate >= '2025-01-01';
반응형

2. 함수 사용으로 인한 인덱스 미사용

문제 쿼리

SELECT OrderID, OrderDate
FROM Sales.SalesOrderHeader
WHERE CONVERT(char(10), OrderDate, 120) = '2025-05-15';

원인

  • 컬럼에 함수 적용 시 인덱스를 탈락시켜 전체 스캔 발생
  • 변환 비용이 추가되어 CPU 부하 증가

변경 쿼리

SELECT OrderID, OrderDate
FROM Sales.SalesOrderHeader
WHERE OrderDate >= '2025-05-15' 
  AND OrderDate < '2025-05-16';

3. 커서(Cursor) 사용으로 인한 느린 반복 처리

문제 쿼리

DECLARE order_cursor CURSOR FOR
SELECT OrderID
FROM Sales.SalesOrderHeader;

OPEN order_cursor;
FETCH NEXT FROM order_cursor INTO @OrderID;
WHILE @@FETCH_STATUS = 0
BEGIN
    -- 복잡한 처리 로직
    FETCH NEXT FROM order_cursor INTO @OrderID;
END
CLOSE order_cursor;
DEALLOCATE order_cursor;

원인

  • 행 단위 반복 처리로 수천~수만 건 처리 시 컨텍스트 스위칭 비용 상승
  • 세트 연산을 활용하지 않아 전체 처리 시간이 비효율적

변경 쿼리

-- 예: 주문 상태별 집계 처리
SELECT OrderStatus, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY OrderStatus;

4. 조인 순서 및 불필요한 조인으로 인한 실행계획 비효율

문제 쿼리

SELECT C.CustomerID, O.OrderID, D.ProductID
FROM Sales.SalesOrderDetail D
JOIN Sales.SalesOrderHeader O ON D.SalesOrderID = O.SalesOrderID
JOIN Sales.Customer C ON O.CustomerID = C.CustomerID
WHERE D.OrderQty > 10;

원인

  • 먼저 큰 테이블(D)에 필터가 적용되지 않아 조인 후 필터링
  • 조인 순서가 비효율적으로 실행계획에서 비용 상승

변경 쿼리

SELECT C.CustomerID, O.OrderID, D.ProductID
FROM Sales.SalesOrderDetail D
-- 먼저 필터링하여 데이터를 줄임
WHERE D.OrderQty > 10
JOIN Sales.SalesOrderHeader O ON D.SalesOrderID = O.SalesOrderID
JOIN Sales.Customer C ON O.CustomerID = C.CustomerID;

5. 서브쿼리 대신 IN절/EXISTS절 미사용

문제 쿼리

SELECT OrderID, CustomerID
FROM Sales.SalesOrderHeader
WHERE CustomerID IN (
    SELECT CustomerID
    FROM Sales.Customer
    WHERE TerritoryID = 5
);

원인

  • IN절 내부에서 NULL 처리 및 중복 스캔으로 속도 저하
  • 서브쿼리 재실행으로 인한 불필요한 비용

변경 쿼리

SELECT H.OrderID, H.CustomerID
FROM Sales.SalesOrderHeader H
WHERE EXISTS (
    SELECT 1
    FROM Sales.Customer C
    WHERE C.CustomerID = H.CustomerID
      AND C.TerritoryID = 5
);

6. TOP절에 ORDER BY 미지정으로 인한 예상치 못한 결과

문제 쿼리

SELECT TOP 100 *
FROM Sales.SalesOrderHeader;

원인

  • 정렬 기준이 없어 랜덤한 100건을 가져와 안정적이지 않음
  • 인덱스를 타고 가더라도 원하는 최신 데이터가 아닐 수 있음

변경 쿼리

SELECT TOP 100 *
FROM Sales.SalesOrderHeader
ORDER BY OrderDate DESC;

위 사례들을 참고해 불필요한 데이터 조회 최소화, 인덱스 활용을 방해하는 함수 제거, 반복 처리 대신 세트 연산, 적절한 조인 순서 및 필터링 우선 적용, EXISTS절 활용, 명확한 정렬 기준 지정 등을 적용한다면 MSSQL 쿼리 성능을 효과적으로 향상시킬 수 있습니다.

반응형