SQL 쿼리 성능 최적화를 위한 튜닝 팁 6가지 (Query Optimization)

SQL 쿼리 성능 최적화를 위한 튜닝 팁 6가지 (Query Optimization)

보다 효율적인 데이터 검색 및 추출을 위해 필요한 SQL 쿼리 튜닝 방법을 알려 드려요. 실무에서 더 빠르고 효과적으로 데이터를 처리할 수 있도록 하는 꿀팁 6가지에 대해 알아 보세요.

목차
📌
이 글은 이런 분들께 추천해요.
- 데이터베이스 성능 개선에 관심 있는 개발자들
- 데이터 처리와 관리 방법을 개선하고자 하는 데이터 분석가들
- 대시보드 성능 향상에 초점을 맞춘 BI 전문가들
- 시스템의 전반적인 성능과 리소스 사용 최적화를 담당하는 데이터베이스 관리자

안녕하세요! '하트카운트' 팀의 Analytics Engineer, Jaden입니다.
오늘은 데이터베이스, SQL 쿼리 최적화를 위한 6가지 팁을 소개해 드리려 합니다!

데이터베이스에서 쿼리 튜닝은 시스템 성능에 직접적인 영향을 미칩니다. 특히 대용량 데이터를 다루는 경우, 최적화되지 않은 쿼리는 응답 속도 저하, 자원 낭비, 심지어는 서비스 장애까지 초래할 수 있죠.

반면 잘 튜닝된 쿼리는 같은 작업을 더 빠르고 안정적으로 처리할 수 있게 합니다. 회사 차원에서도 효율적인 쿼리 처리는 비용 절감과 생산성 향상으로 직결되기에 매우 중요하게 여겨집니다.

제가 오늘 소개해드릴 쿼리 최적화 팁 6가지는 이런 측면에서 데이터 엔지니어, 개발자, 분석가 등 데이터베이스를 다루는 모든 분들께 실질적인 도움이 될 거라 믿습니다.

각 팁은 실무에서 쉽게 마주할 수 있는 상황들을 예시로 들어 설명드릴 예정이에요. 코드 예제와 함께 어떤 문제가 있었고, 어떻게 하면 더 나은 쿼리 성능을 얻을 수 있는지 자세히 살펴보겠습니다.

아, 그전에 쿼리 최적화를 위해 꼭 알아두셔야 할 핵심 개념이 하나 있습니다. 바로 '인덱스(Index)'인데요. 이것이 무엇이고 쿼리 성능에 어떤 영향을 미치는지 간단히 짚고 넘어가려 해요.

자, 그럼 지금부터 저와 함께 쿼리 튜닝을 위한 6가지 팁을 하나씩 알아보면서 최적화된 쿼리 작성 노하우를 익혀보시죠!


이런 내용을 다룰거에요!

  1. 좌변을 연산하지 마라.
  2. OR 대신 Union을 사용하라
  3. 필요한 Row, Column만 가져와라.
  4. 분석 함수를 최대한 활용하라
  5. 와일드카드(%) 는 끝에 작성하는것이 더 좋다
  6. 계산값을 미리 저장해두고, 나중에 사용하라.

(사전 지식) 데이터베이스에서 인덱싱이란?

효율적인 쿼리문 작성을 위해서 알아야 할 사전 지식이 있습니다.

바로 인덱싱입니다.

데이터베이스에서 데이터를 찾거나 분류하는 작업은 마치 우리가 질문을 던지는 것과 비슷해요. 이때, 우리가 사용하는 '질문'이 바로 쿼리(query)입니다.

쿼리를 통해 데이터베이스에게 원하는 정보를 빠르고 정확하게 찾아달라고 요청하는 거죠. 이 과정에서 중요한 역할을 하는 것이 '인덱싱(indexing)'입니다.

데이터베이스에서 원하는 정보를 빠르게 찾을 수 있도록 해주는 '인덱싱'

인덱싱을 이해하기 위해서는 책의 목차를 생각해 보는 것이 도움이 됩니다. 책을 읽을 때, 우리가 특정 내용을 찾고 싶을 때 목차를 보고 해당 페이지로 바로 갈 수 있잖아요?

이처럼 목차가 책 내에서 원하는 정보를 빠르게 찾도록 도와주는 것처럼, 데이터베이스에서도 인덱스(index)가 비슷한 역할을 합니다.

간단히 말해, 인덱싱은 데이터베이스에서 원하는 정보를 찾기 위한 '지름길'을 만드는 것이라고 생각하면 됩니다. 이로 인해 데이터 검색 속도가 크게 향상되며, 이는 데이터를 효율적으로 관리하고 활용하는 데 큰 도움이 됩니다.

자, 그럼 이제 본론으로 들어가겠습니다!


1. 좌변을 연산하지 않을 것

좌변 연산의 문제점

데이터베이스 쿼리를 작성할 때, 우리는 종종 데이터를 원하는 형태로 변형하거나 계산하기 위해 다양한 연산자를 사용하곤 합니다.

예를 들어, 특정 년도의 데이터만 필터링하고 싶다면 다음과 같은 쿼리를 떠올릴 수 있겠죠.

SELECT * FROM sales WHERE YEAR(date) = 2021;

이 쿼리는 sales 테이블에서 date 컬럼의 연도가 2021년인 모든 데이터를 찾아달라는 요청이에요. 여기서 YEAR(date)date 컬럼에서 연도 부분만을 추출하는 함수입니다.

사용자 입장에서 이런 방식은 매우 직관적이고 이해하기 쉽겠지만, 데이터베이스 관점에서는 효율성에 큰 문제가 생길 수 있어요.

데이터 원본을 변형하여, 내가 찾고자 하는 범위와 비교하는 연산은 데이터베이스가 인덱스를 제대로 활용할 수 없게 만듭니다. 마치 책의 페이지 번호를 함수 연산하여 계산하며 찾는 것과 같은 방식이죠.

인덱스는 원본 데이터를 그대로 가지고 만들어져요. 예를 들어 date 컬럼에 대한 인덱스는 '2023-06-01', '2023-06-02'와 같은 날짜 값 자체를 가지고 구성됩니다.

그래서 앞서 본 것처럼 YEAR(date) = 2021과 같이 데이터를 변형하는 연산을 수행하면, 이 인덱스를 제대로 활용할 수 없게 됩니다.
한 줄 한 줄 일일이 YEAR(date) 연산을 수행하고, 그 결과가 2021인지 확인하는 거죠. 데이터가 많을수록 이는 엄청난 작업량이 될 수밖에 없습니다.

이렇게 되면 데이터베이스는 인덱스를 통해 빠른 검색을 할 수 없고, 결국 모든 데이터를 처음부터 끝까지 훑어봐야만 해요.

효과적인 대안: 우변에서의 데이터 필터링

좌변 연산이 인덱스 활용을 방해한다는 걸 알았으니, 이제 쿼리 성능을 개선할 수 있는 더 나은 방법을 찾아봐야겠죠?

바로 데이터를 '변형'하지 않고, 원본 형태를 유지하면서 필요한 데이터를 찾는 거예요.
이를 위해 우리는 '기간'을 명시하여 데이터를 필터링하는 방식을 활용할 수 있습니다.

예를 들면 이런 식이에요.

SELECT * FROM sales 
WHERE date >= '2021-01-01' AND date <= '2021-12-31';

이 쿼리는 sales 테이블에서 date 컬럼의 값이 2021년 1월 1일부터 2021년 12월 31일 사이에 있는 모든 데이터를 찾아줍니다.

여기서 주목할 점은 date 컬럼을 그대로 사용하고 있다는 거예요. 별도의 연산을 수행하지 않고, 날짜 값 자체를 직접 비교하고 있죠.

이렇게 하면 데이터베이스는 date 컬럼에 대한 인덱스를 효과적으로 활용할 수 있어요. 인덱스에 저장된 날짜 값과 쿼리에서 지정한 기간을 비교하면서, 필요한 데이터를 빠르게 찾을 수 있게 되는 거죠.

이처럼 원본 데이터를 직접 비교하는 조건을 사용하는 것이 인덱스를 최대한 활용하고 쿼리 성능을 높이는 핵심 비결이라고 할 수 있겠네요.


2. OR 대신 UNION을 사용할 것


데이터를 조회할 때 여러 조건을 만족하는 결과를 얻기 위해 우리는 흔히 OR 연산자를 사용합니다.

예를 들어, 'Marketing' 부서나 'IT' 부서에 속한 직원들을 찾고 싶다면 다음과 같은 쿼리를 작성할 수 있겠죠.

SELECT * FROM employees 
WHERE department = 'Marketing' OR department = 'IT';

이 쿼리는 간단하고 직관적이지만, 성능 면에서는 최선의 선택이 아닐 수 있어요.

OR 연산자를 사용하면 데이터베이스는 한 번의 스캔으로 모든 조건을 확인해야 합니다. 이 과정에서 불필요한 데이터까지 대량으로 검색하게 되고, 특히 인덱스를 제대로 활용하지 못하는 경우가 많아요.

department 컬럼에 인덱스가 있다고 가정해 보죠. OR 연산자로 인해 데이터베이스는 이 인덱스를 효율적으로 사용할 수 없게 됩니다.

인덱스는 단일 값에 대한 빠른 검색을 위해 최적화되어 있는데, OR은 여러 값을 동시에 찾아야 하니까요. 결국 인덱스의 장점을 살리지 못하고, 전체 데이터를 모두 뒤져야 하는 상황이 벌어지는 거예요.

이런 문제를 해결하기 위해 우리는 UNION을 활용할 수 있습니다. UNION은 각 조건에 대한 쿼리를 별도로 실행한 뒤, 그 결과를 합쳐주는 연산자예요.

위 쿼리를 UNION을 사용해 다음과 같이 바꿔볼 수 있겠죠.

SELECT * FROM employees WHERE department = 'Marketing'
UNION
SELECT * FROM employees WHERE department = 'IT';

이렇게 하면 데이터베이스는 각 쿼리를 독립적으로 최적화하고 실행할 수 있어요.

department = 'Marketing'department = 'IT'는 각각 인덱스를 통해 빠르게 처리될 수 있습니다. 그리고 나서 UNION이 두 결과를 합치는 거죠. 이 과정에서 중복된 결과는 자동으로 제거됩니다.

만약 중복이 없다는 게 확실하다면 UNION ALL을 사용해 중복 제거 단계를 건너뛰고 성능을 더 높일 수도 있어요. 하지만 대부분의 경우 정확한 결과를 위해선 UNION이 권장되죠.

이처럼 OR 대신 UNION을 사용하는 건 복잡한 쿼리의 성능을 최적화하는 효과적인 방법 중 하나예요.

각 조건마다 별도의 쿼리를 수행하고 인덱스를 활용하도록 함으로써, 데이터베이스의 부하를 줄이고 전체적인 조회 성능을 크게 개선할 수 있습니다.


3. 필요한 Row와 Column만 선택하여 성능 최적화하기


데이터베이스에서 데이터를 조회할 때, 불필요한 정보까지 모두 가져오는 것은 성능 저하의 주범 중 하나예요.

대신 꼭 필요한 데이터만 골라내는 것이 성능 최적화의 핵심이라고 할 수 있죠. 이를 실제 쿼리 예시를 통해 자세히 살펴볼게요.

특정 조건을 만족하는 Row만 선택하기

가령 'Marketing' 부서에 속하면서 매출액이 100,000 이상인 직원들의 이름과 이메일 주소를 조회하고 싶다고 해볼게요.

SELECT name, email
FROM employees
WHERE department = 'Marketing' AND sales >= 100000;

이 쿼리는 employees 테이블에서 부서가 'Marketing'이고 매출액이 100,000 이상인 직원들의 이름(name)과 이메일(email) 정보만 선택합니다.

불필요한 Row를 걸러내고 필요한 Column만 선택함으로써, 데이터베이스가 처리해야 할 데이터의 양을 최소화하는 거죠. 이는 쿼리의 응답 속도를 높이고 시스템 부하를 줄이는 데 큰 도움이 됩니다.

서브쿼리를 활용하여 필요한 데이터만 추출하기

이번에는 각 부서별로 최고 매출액을 달성한 직원의 정보를 조회하는 상황을 가정해 볼게요.

SELECT e.name, e.department, e.sales
FROM employees e
JOIN (
  SELECT department, MAX(sales) AS max_sales
  FROM employees
  GROUP BY department
) d ON e.department = d.department AND e.sales = d.max_sales;

이 쿼리에서는 서브쿼리를 활용하여 부서별 최대 매출액을 계산한 후, 이 결과와 employees 테이블을 조인하여 필요한 정보만 추출하고 있어요.

서브쿼리에서 불필요한 Column을 제외하고 오직 departmentmax_sales만 선택함으로써, 중간 결과의 크기를 최소화하고 있죠.

그리고 이를 바탕으로 최종적으로 필요한 직원의 이름, 부서, 매출액 정보만 조회하는 거예요. 이렇게 하면 쿼리의 효율성을 크게 높일 수 있습니다.

이처럼 데이터베이스에서 정보를 조회할 때는 가능한 한 꼭 필요한 Row와 Column만 선택하는 게 중요해요.

불필요한 데이터 처리를 줄임으로써 응답 시간을 단축하고, 시스템 자원을 효율적으로 활용할 수 있습니다. 이는 데이터베이스 성능 최적화의 기본 중 하나라고 할 수 있겠네요.


4. 분석 함수를 활용하여 쿼리 성능 높이기

분석 함수(Analytic Functions)는 SQL 쿼리의 성능을 한 단계 높이는 강력한 도구예요. 단순히 데이터를 처리하는 것을 넘어, 데이터 분석과 쿼리 최적화에 있어 핵심적인 역할을 한답니다.

이런 함수들은 복잡한 데이터 집합 내에서 각 Row별로 세부적인 계산을 가능하게 해요. 전체 데이터에 걸쳐 다양한 통계와 계산을 유연하게 수행할 수 있게 도와주는 거죠. 대표적으로 ROW_NUMBER(), RANK(), DENSE_RANK(), LEAD(), LAG() 등의 함수가 있습니다.

쿼리 효율성을 높이는 분석 함수

분석 함수를 사용하면 SQL 쿼리의 효율성이 여러 면에서 향상돼요.

먼저, 전통적인 집계 함수와 달리 사전에 데이터를 그룹화할 필요가 없어요. 이는 불필요한 자원 소모를 줄이고 쿼리 성능을 높이는 데 큰 도움이 됩니다.

또한 복잡한 데이터 분석 과정에서 발생할 수 있는 중간 결과물의 저장과 재처리를 최소화할 수 있어요. 이는 쿼리 실행 시간을 크게 단축시키죠.

순위 결정 함수로 데이터 순서 매기기

ROW_NUMBER(), RANK(), DENSE_RANK() 같은 함수는 데이터 내 각 항목의 순위를 정하는 데 유용하게 쓰입니다.

예를 들어 부서별로 급여가 높은 직원 순으로 순위를 매기고 싶다면 ROW_NUMBER() 함수를 이용할 수 있어요.

SELECT 
  name, 
  department, 
  salary,
  ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank
FROM employees;

이 쿼리는 각 부서 내에서 급여 높은 순으로 직원들에게 고유한 순위를 부여합니다.

RANK()DENSE_RANK() 함수도 비슷한 방식으로 동작하지만, 동일한 값에 대해 동일 순위를 부여한다는 점이 달라요. DENSE_RANK()의 경우 순위 간격을 항상 1로 유지하는 특징이 있죠.

데이터 변화를 추적하는 분석 함수

LEAD()LAG() 함수는 현재 Row와 관련하여 이전 또는 다음 Row의 데이터를 참조할 수 있게 해줍니다.

이를 활용하면 각 직원의 연봉 변화율을 쉽게 계산할 수 있어요.

SELECT
  name,
  salary,
  LAG(salary) OVER (PARTITION BY department ORDER BY hire_date) AS prev_salary,
  salary - LAG(salary) OVER (PARTITION BY department ORDER BY hire_date) AS salary_increase
FROM employees;

이 쿼리는 각 부서 내에서 입사일자 순으로 직원을 정렬한 후, 이전 직원의 연봉과 현재 직원의 연봉 차이를 계산하여 연봉 인상액을 구하고 있어요.

이런 함수들은 특히 시계열 데이터나 연속적인 데이터 집합을 다룰 때, 이전 데이터 포인트와의 비교가 필요한 분석에 아주 유용하게 활용됩니다.

분석 함수로 데이터 필터링 최적화하기

만약 각 부서별로 급여가 높은 상위 3명의 직원 정보만 추출하고 싶다면 ROW_NUMBER() 함수를 이용해 다음과 같이 쿼리를 작성할 수 있어요.

WITH ranked_employees AS (
  SELECT 
    name, 
    department, 
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank
  FROM employees
)
SELECT *
FROM ranked_employees
WHERE rank <= 3;

이 쿼리는 먼저 각 부서 내에서 급여 순위를 매긴 후, 순위가 3 이하인 직원들만 선택하는 방식으로 동작해요.

이렇게 하면 전체 데이터를 먼저 스캔하지 않고도 필요한 결과만 빠르게 필터링할 수 있죠.

분석 함수를 활용하면 이처럼 SQL 쿼리의 효율성과 성능을 크게 개선할 수 있어요.앞으로 데이터 처리와 분석 과정에서 이런 분석 함수들을 적극 활용해보시는 걸 추천드려요!


5. 와일드카드(%)는 끝에 작성하는 것이 더 좋다

SQL에서 LIKE 연산자와 함께 와일드카드(%)를 사용하면 텍스트 데이터를 유연하게 검색할 수 있어 정말 유용하죠.

그런데 이 와일드카드의 위치에 따라 쿼리 성능이 크게 달라질 수 있다는 사실, 알고 계셨나요?

지금부터 문자열 끝에 와일드카드를 두는 게 왜 성능 최적화에 도움이 되는지 알아보고, 실제 코드 예시로 그 차이를 확인해 볼게요!

와일드카드의 위치가 중요한 이유

먼저 문자열 앞에 와일드카드를 쓰는 경우를 볼까요?

SELECT * FROM users WHERE name LIKE '%John';

이 쿼리는 'John'으로 끝나는 모든 이름을 찾아내려고 해요. 그런데 문제는 와일드카드가 앞에 있으면, 데이터베이스가 'John'으로 끝나는 모든 가능한 문자열 조합을 일일이 검색해야 한다는 거예요.

인덱스가 있어도 이걸 제대로 활용할 수가 없죠. 결과적으로 데이터베이스는 엄청난 자원을 소모하고, 쿼리 속도도 느려질 수밖에 없어요.

그럼 이번에는 문자열 뒤에 와일드카드를 쓰는 경우를 보겠습니다.

SELECT * FROM users WHERE name LIKE 'John%';

이 쿼리는 'John'으로 시작하는 모든 이름을 찾고 있어요. 이렇게 하면 데이터베이스가 인덱스를 활용해서 검색 범위를 효과적으로 좁힐 수 있죠.

데이터베이스는 우선 인덱스에서 'John'으로 시작하는 첫 번째 항목을 빠르게 찾아내요. 그리고 'John'으로 시작하지 않는 첫 번째 항목이 나올 때까지만 검색하면 됩니다.

이처럼 LIKE 연산자와 와일드카드(%)를 사용할 때는 가급적 문자열 끝에 와일드카드를 두는 게 좋아요. 데이터베이스가 인덱스를 더 잘 활용할 수 있게 도와주니까요.

SQL 쿼리 성능 최적화를 위한 팁 6가지

6. 계산값을 미리 저장해두었다가, 나중에 조회할 것

데이터베이스에서 복잡한 계산을 실시간으로 처리하는 것은 쿼리 성능에 큰 부담이 될 수 있어요. 특히 대량의 데이터를 다루는 경우라면 더욱 그렇죠.

이런 상황에서는 자주 사용 되는 계산값을 미리 저장해두었다가, 필요할 때 바로 꺼내 쓰는 것이 효과적인 최적화 방법이 될 수 있습니다.

실시간 계산의 비효율성

예를 들어 이커머스 사이트에서 각 상품의 평균 구매 금액, 총 매출, 구매자 수, 재구매율 등 다양한 통계치를 실시간으로 계산한다고 가정해 볼게요.

SELECT 
    p.product_id,
    AVG(od.quantity * od.unit_price) AS avg_order_amount,
    SUM(od.quantity * od.unit_price) AS total_sales,
    COUNT(DISTINCT o.customer_id) AS num_purchasers,
    COUNT(DISTINCT CASE WHEN o.customer_id IN (
        SELECT customer_id 
        FROM orders
        WHERE product_id = p.product_id
        GROUP BY customer_id
        HAVING COUNT(*) > 1
    ) THEN o.customer_id END) * 1.0 / COUNT(DISTINCT o.customer_id) AS repurchase_rate
FROM 
    products p
    JOIN order_details od ON p.product_id = od.product_id
    JOIN orders o ON od.order_id = o.order_id
GROUP BY 
    p.product_id;

이 쿼리는 products, order_details, orders 테이블을 조인하여 각 상품(product_id)별로 평균 구매 금액(avg_order_amount), 총 매출(total_sales), 구매자 수(num_purchasers), 재구매율(repurchase_rate)을 계산하고 있어요.

문제는 이 쿼리가 실행될 때마다 방대한 양의 주문 및 고객 데이터를 모두 읽어서 복잡한 계산을 수행해야 한다는 거예요. 특히 재구매율 계산을 위해 서브쿼리까지 사용되고 있어 쿼리 속도는 더욱 느려질 수밖에 없겠죠.

게다가 이런 통계치가 자주 사용된다면, 같은 복잡한 계산을 반복해서 수행하게 되니 엄청난 자원 낭비가 발생할 거예요.

계산값을 저장하고 활용하기

이런 문제를 해결하기 위해 우리는 계산 결과를 별도의 테이블에 저장해둘 수 있어요.

CREATE TABLE product_stats AS
SELECT
    p.product_id,
    AVG(od.quantity * od.unit_price) AS avg_order_amount,
    SUM(od.quantity * od.unit_price) AS total_sales,
    COUNT(DISTINCT o.customer_id) AS num_purchasers,
    COUNT(DISTINCT CASE WHEN o.customer_id IN (
        SELECT customer_id
        FROM orders
        WHERE product_id = p.product_id
        GROUP BY customer_id
        HAVING COUNT(*) > 1
    ) THEN o.customer_id END) * 1.0 / COUNT(DISTINCT o.customer_id) AS repurchase_rate
FROM
    products p
    JOIN order_details od ON p.product_id = od.product_id
    JOIN orders o ON od.order_id = o.order_id
GROUP BY
    p.product_id;

이 쿼리는 먼저 product_stats라는 새 테이블을 만들고, 앞서 본 복잡한 계산을 수행하여 각 상품의 통계치를 미리 저장하고 있어요.

이렇게 해두면 나중에 이런 통계치가 필요할 때, 이 테이블에서 바로 값을 가져올 수 있어요. 복잡한 실시간 계산 대신 미리 저장된 값을 사용하니, 쿼리 속도가 훨씬 빨라지겠죠?

주기적인 계산 결과 업데이트

물론 주문 데이터가 새로 추가될 때마다 통계치를 업데이트해줘야 해요. 하지만 이건 실시간으로 할 필요가 없어요.

대신 일정 주기(예: 하루 한 번)마다 통계치를 업데이트하는 배치 작업을 수행하면 되죠.

UPDATE product_stats ps
SET
    avg_order_amount = (
        SELECT AVG(od.quantity * od.unit_price)
        FROM order_details od
        WHERE od.product_id = ps.product_id
    ),
    total_sales = (
        SELECT SUM(od.quantity * od.unit_price)
        FROM order_details od
        WHERE od.product_id = ps.product_id  
    ),
    num_purchasers = (
        SELECT COUNT(DISTINCT o.customer_id)
        FROM order_details od
        JOIN orders o ON od.order_id = o.order_id
        WHERE od.product_id = ps.product_id
    ),
    repurchase_rate = (
        SELECT 
            COUNT(DISTINCT CASE WHEN o.customer_id IN (
                SELECT customer_id
                FROM orders
                WHERE product_id = ps.product_id
                GROUP BY customer_id
                HAVING COUNT(*) > 1
            ) THEN o.customer_id END) * 1.0 / COUNT(DISTINCT o.customer_id)
        FROM order_details od 
        JOIN orders o ON od.order_id = o.order_id
        WHERE od.product_id = ps.product_id
    );

이 쿼리는 product_stats 테이블의 통계치들을 최신 주문 내역을 바탕으로 업데이트해요.

각 통계치별로 서브쿼리를 사용하여 최신 값을 계산하고, 그 결과로 product_stats 테이블의 값들을 갱신하고 있죠.

이런 식으로 계산 결과를 저장하고 주기적으로 업데이트하면, 복잡한 실시간 쿼리의 부담을 크게 줄일 수 있어요.

자주 사용되는 통계치, 집계값 등은 미리 계산해서 저장해 두는 것이 성능 최적화에 큰 도움이 된다는 사실, 꼭 기억해 주세요!


요약정리

  1. 데이터를 변형하는 연산은 가급적 피하고, 원본 데이터를 직접 비교하는 조건을 사용하세요. 이는 인덱스 활용도를 높여 쿼리 속도를 향상시킵니다.
  2. OR 연산자 대신 UNION을 활용하면 각 조건을 독립적으로 최적화하고 인덱스를 효과적으로 사용할 수 있습니다.
  3. 불필요한 Row와 Column을 제외하고 꼭 필요한 데이터만 조회하세요. 이는 데이터 처리량을 최소화하여 쿼리 성능을 높입니다.
  4. ROW_NUMBER(), RANK(), LEAD(), LAG() 등의 분석 함수를 적극 활용하면 복잡한 데이터 분석을 유연하고 효율적으로 수행할 수 있습니다.
  5. LIKE 연산자와 와일드카드(%)를 사용할 때는 문자열 끝에 와일드카드를 두는 것이 인덱스 활용에 유리합니다.
  6. 복잡한 계산은 실시간으로 처리하기보다는 미리 계산해서 저장해두고 주기적으로 업데이트하는 것이 효율적입니다.


요즘은 쿼리문을 작성할 때, Chat GPT 의 도움을 많이 받기도 하죠?
위에서 소개해 드린 쿼리 튜닝 팁들을 반영한 Optimized SQL을 작성하는 GPTs 를 만들어 두었으니, 사용해 보시면 좋을 것 같습니다!

ChatGPT - Data Analytics Engineer
A conversational AI system that listens, learns, and challenges

오늘도 여러분께 도움이 되었기를 바라며,
Everyone is an Analyst, 하트카운트 팀의 Jaden 이었습니다!

💖
HEARTCOUNT는 실무자들의 데이터 분석 작업을 돕는 셀프 서비스 애널리틱스 툴입니다.
지금 구글 계정으로 로그인하여 사용해 보세요.