404 Brain Not Found
[database] SELECT 쿼리 실행순서 본문
관계형 데이터 베이스를 사용할때 대부분 직접적으로 SQL 구문을 작성하게 됩니다.
이때 성능에 큰 영향을 미치는것은 주로 데이터를 조회하는 것 입니다.
데이터를 조회할때 SELECT 쿼리를 작성하게 되는데,
성능이 좋은 SELECT 쿼리를 작성하기 위해서는 SELECT 문의 실행 순서를 알아봐야 할 필요가 있습니다.
STEP 1. SELECT 문의 실행 순서
.
1. FROM : 데이터를 가져올 테이블을 지정합니다.
2. WHERE: 조건에 맞는 행을 필터링합니다.
3. GROUP BY: 지정된 열을 기준으로 행을 그룹화합니다.
4.HAVING: 그룹화된 결과에 대한 조건을 적용합니다.
5.SELECT: 최종적으로 출력할 열을 선택합니다.
6.ORDER BY: 결과를 정렬합니다.
7.LIMIT / OFFSET: 결과의 행 수를 제한하거나 특정 위치에서 시작하는 결과를 반환합니다.
STEP 2. SELECT 문을 최적화 하려면?
SELECT 구문을 최적화하기 위해서는 인덱스 최적화, 데이터베이스 구조 변경 등, 필수적인 방법들이 존재하지만 이것보다는 SQL 쿼리문을 최적화하는 방법을 생각해보겠습니다.
먼저 SELECT의 실행순서를 생각해보면 FROM 절이 가장 먼저 실행되게 됩니다.
이말은 즉, FROM 가져올 데이터의 양이 적다면 그만큼 SQL 쿼리의 실행 속도가 개선된다는것을 의미합니다.
아래의 예시를 들겠습니다.
1: N 구조를 가지는 테이블을 임시로 만들어보겠습니다.
Order (주문) 테이블과 OrederDetails (주문 상세) 테이블을 예시로 들겠습니다.
-- 주문 테이블 생성
CREATE TABLE Orders (
OrderID INT PRIMARY KEY,
CustomerName VARCHAR(100),
OrderDate DATE,
TotalAmount DECIMAL(10, 2)
);
-- 주문 상세 테이블 생성
CREATE TABLE OrderDetails (
OrderDetailID INT PRIMARY KEY,
OrderID INT,
ProductName VARCHAR(100),
Quantity INT,
UnitPrice DECIMAL(10, 2),
FOREIGN KEY (OrderID) REFERENCES Orders(OrderID)
);
-- 샘플 데이터 삽입
INSERT INTO Orders (OrderID, CustomerName, OrderDate, TotalAmount)
VALUES
(1, '홍길동', '2023-09-01', 50000),
(2, '김철수', '2023-09-02', 75000);
INSERT INTO OrderDetails (OrderDetailID, OrderID, ProductName, Quantity, UnitPrice)
VALUES
(1, 1, '노트북', 1, 30000),
(2, 1, '마우스', 2, 10000),
(3, 2, '키보드', 1, 15000),
(4, 2, '모니터', 1, 60000);
위의 쿼리를 실행하면 orders와 orderdetails에 데이터가 아래처럼 삽입됩니다.
이제 데이터가 삽입 되었으므로 orders와 orderdetails의 테이블을 left outer join을 걸어서 살펴보겠습니다.
SELECT
o.orderid
, o.customername
, o.orderdate
, o.totalamount
, od.orderdetailid
, od.productname
, od.quantity
, od.unitprice
FROM orders o
LEFT OUTER JOIN orderdetails od
ON o.orderid = od.orderid;
아무런 조건 없이 order와 orderdetails 테이블을 조회하였습니다.
지금은 데이터가 얼마 없지만 만약에 order의 데이터가 1억개 이상이라면 위의 쿼리를 작동시키면
서비스가 재수없으면 뻗을수도 있습니다. 그렇기에 limt를 주거나, 조건을 주어서 검색해야 합니다.
그러면 UnitPrice(단가)가 30000원 이상인 Order들만 조회하도록 해보겠습니다.
그러면 아래처럼 검색할 수 있을 것 입니다.
원하는 결과가 나왔습니다. 그리고 신입이라면 이렇게 작성하고 넘어갈수도 있습니다.
우리는 조금 스마트한 사람이니깐, 조금 더 쿼리를 개선해봅시다.
(쿼리를 개선할수록 프로그램의 성능이 매우 향상됩니다. 애초에 데이터를 빠르게 가져오면 프로그램단에서 코드를 이상하게 작성해도 어느정도 속도가 보장됩니다.)
먼저 우리는 SELECT 쿼리의 동작 순서를 이해하였습니다. 가장 먼저 수행되는것은 FROM구문입니다.
어차피 od.unitprice는 orderdetails에 있는 컬럼입니다. 그렇다면 JOIN을 하기전에 WHERE절을 사용하여 JOIN할 데이터를 줄이는것이 더 좋습니다.
예를들어서 orderdetails에 데이터가 10억개가 있다고 가정해보겠습니다.
위의 쿼리 구문을 사용하기 위하여 left outer join을 할때 10억개의 테이블과 조인을 하는것보다.
10억개의 데이터를 where 절을 사용하여 데이터를 줄인다음에 left outer join을 하는것 어느것이 빠를까요?
당연히 후자가 훨씬더 빠릅니다.
그렇기 때문에 우리는 쿼리를 아래와 같이 바꿀수 있습니다.
SELECT
o.orderid
, o.customername
, o.orderdate
, o.totalamount
, od.orderdetailid
, od.productname
, od.quantity
, od.unitprice
FROM orders o
LEFT OUTER JOIN
(
SELECT
orderdetailid
, orderid
, productname
, quantity
, unitprice
FROM orderdetails
WHERE orderdetails.unitprice >= 30000.00
) od
ON o.orderid = od.orderid;
일반적으로 데이터가 작을때는 서브쿼리를 사용하는 것 보다 테이블을 조인걸어서 사용하는 것이 더 빠릅니다.
하지만 지금 상황에서는 order와 orderdetails 테이블의 데이터가 매우 많다고 가정하였으므로, 위의 쿼리를 사용하는것이 더 빠릅니다.
하지만 해당 결과는 인덱스를 어떻게 설정하느냐에 따라서 성능이 바뀔수도 있습니다.
결론 위의 쿼리가 더 빠른 이유
- 데이터 필터링: unitprice >= 30000.00 조건으로 처리할 데이터량을 크게 줄입니다.
- 조인 최적화: 필터링된 더 작은 데이터셋과의 조인은 더 효율적입니다.
- 인덱스 활용: unitprice에 대한 인덱스가 있다면, 필터링 단계에서 이를 효과적으로 활용할 수 있습니다.
'DB 정리' 카테고리의 다른 글
[database] UNION, UNION ALL (3) | 2024.09.16 |
---|---|
[database] SELECT JOIN (2) | 2024.09.16 |