devvoyage
by test

(MY)SQL JOIN 가이드 for Beginer

8 min read

TL;DR

JOIN?

JOIN은 관계형 데이터베이스에서 여러 테이블의 데이터를 결합하는 핵심 기능입니다. 테이블 간 관계는 1:1, 1:N, N:M으로 구분됩니다.

이해하기

이해를 쉽게 돕게하기 위해 가상의 테이블을 정의한 후 해당 데이터를 기준으로 설명하겠습니다.

INIT Database

SQL
CREATE TABLE Customers (
    c_id INT PRIMARY KEY,
    c_name VARCHAR(100) NOT NULL,
    c_email VARCHAR(100) UNIQUE NOT NULL,
    c_phone VARCHAR(20),
    address VARCHAR(200),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE Products (
    p_id INT PRIMARY KEY,
    p_name VARCHAR(100) NOT NULL,
    p_price DECIMAL(10, 2) NOT NULL,
    description TEXT,
    stock_quantity INT NOT NULL DEFAULT 0,
    category VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE Orders (
    o_id INT PRIMARY KEY,
    o_date DATE NOT NULL,
    o_total_amount DECIMAL(10, 2) NOT NULL,
    status VARCHAR(20) NOT NULL,
    c_id INT NOT NULL,
    shipping_address VARCHAR(200),
    payment_method VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE OrderDetails (
    o_d_id INT PRIMARY KEY,
    o_id INT NOT NULL,
    p_id INT NOT NULL,
    quantity INT NOT NULL,
    unit_price DECIMAL(10, 2) NOT NULL,
    subtotal DECIMAL(10, 2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
    discount DECIMAL(10, 2) DEFAULT 0.00
);

INSERT INTO Customers (c_id, c_name, c_email, c_phone, address) VALUES
    (1, 'Noah', 'noah@gmail.com', '111-222-3333', '123 Apple St, New York, NY 10001'),
    (2, 'Kyle', 'kyle@gmail.com', '444-555-6666', '456 Banana Ave, Los Angeles, CA 90001'),
    (3, 'Chuck', 'chuck@gmail.com', '777-888-9999', '789 Cherry Ln, Chicago, IL 60601');

INSERT INTO Products (p_id, p_name, p_price, description, stock_quantity, category) VALUES
    (1, 'iPhone', 10000.00, 'Latest model iPhone with advanced features', 100, 'Electronics'),
    (2, 'iPad', 20000.00, 'Powerful and versatile iPad for work and play', 50, 'Electronics'),
    (3, 'AirPod', 30000.00, 'Wireless earbuds with exceptional sound quality', 200, 'Accessories');

INSERT INTO Orders (o_id, o_date, o_total_amount, status, c_id, shipping_address, payment_method) VALUES
    (1, '2024-08-14', 100000.00, 'Pending', 1, '123 Apple St, New York, NY 10001', 'Credit Card'),
    (2, '2024-08-15', 300000.00, 'Delivered', 2, '456 Banana Ave, Los Angeles, CA 90001', 'PayPal'),
    (3, '2024-08-16', 60000.00, 'Processing', 2, '456 Banana Ave, Los Angeles, CA 90001', 'Debit Card');

INSERT INTO OrderDetails (o_d_id, o_id, p_id, quantity, unit_price, discount) VALUES
    (1, 1, 1, 10, 10000.00, 0.00),
    (2, 2, 1, 10, 10000.00, 0.00),
    (3, 2, 2, 10, 20000.00, 0.00),
    (4, 3, 3, 5, 30000.00, 5000.00);

SQL test website

왜 사용할까?

  1. 데이터 통합: 여러 테이블에 분산된 관련 데이터를 하나의 결과 집합으로 결합합니다.
  2. 정규화된 데이터 활용: 정규화로 분리된 테이블의 데이터를 효율적으로 조회할 수 있습니다.
  3. 데이터 중복 감소: 데이터를 여러 테이블로 나누어 저장함으로써 중복을 줄이고 일관성을 유지합니다.
  4. 복잡한 쿼리 작성: 여러 테이블의 데이터를 조합하여 복잡한 비즈니스 로직을 구현할 수 있습니다.
  5. 데이터 분석 용이: 다양한 각도에서 데이터를 분석하고 인사이트를 얻을 수 있습니다.

이러한 이유들로 JOIN은 관계형 데이터베이스에서 핵심적인 기능으로 사용됩니다.

diagram

1. INNER JOIN

2. LEFT (OUTER) JOIN

3. RIGHT (OUTER) JOIN

4. FULL (OUTER) JOIN

실습하기

QUERY database

INNER JOIN

SQL
SELECT 
    c.c_name AS customer_name,
    o.o_id AS order_id,
    o.o_date AS order_date,
    p.p_name AS product_name,
    od.quantity,
    od.unit_price,
    od.subtotal,
    (od.subtotal - od.discount) AS final_price
FROM 
    Customers c
INNER JOIN Orders o ON c.c_id = o.c_id
INNER JOIN OrderDetails od ON o.o_id = od.o_id
INNER JOIN Products p ON od.p_id = p.p_id
WHERE 
    o.o_date BETWEEN '2024-08-01' AND '2024-08-31'
    AND p.category = 'Electronics'
ORDER BY 
    o.o_date DESC, od.subtotal DESC;

LEFT (OUTER) JOIN

SQL
SELECT 
    c.c_name AS customer_name,
    COALESCE(COUNT(DISTINCT o.o_id), 0) AS total_orders,
    COALESCE(SUM(od.quantity), 0) AS total_items_purchased,
    COALESCE(SUM(od.subtotal - od.discount), 0) AS total_spent,
    MAX(o.o_date) AS last_order_date,
    GROUP_CONCAT(DISTINCT p.category SEPARATOR ', ') AS purchased_categories
FROM 
    Customers c
LEFT JOIN Orders o ON c.c_id = o.c_id
LEFT JOIN OrderDetails od ON o.o_id = od.o_id
LEFT JOIN Products p ON od.p_id = p.p_id
GROUP BY 
    c.c_id, c.c_name
HAVING 
    COUNT(DISTINCT o.o_id) >= 0
ORDER BY 
    total_spent DESC, total_orders DESC;

Conclusion

1. 성능 최적화

필요한 테이블만 JOIN 해서 쿼리 성능을 향상시켜야합니다. JOIN 컬럼에 적절한 인덱스를 생성하면 쿼리 속도를 크게 향상시킬 수 있습니다. 그리고 가능하면 작은 테이블을 큰 테이블에 JOIN하는 것이 효율적입니다.

2. NULL 값 처리

OUTER JOIN 사용 시 NULL 값 처리에 특히 주의해야 합니다. COALESCE 또는 IFNULL 함수를 사용하여 NULL 값을 적절히 처리하고, NULL 값 비교 시 IS NULL 또는 IS NOT NULL 연산자를 사용해야합니다.

3. 데이터 정확성 확인

JOIN 조건을 정확히 설정했는지 항상 확인해야합니다. 복잡한 JOIN 쿼리의 경우, 단계별로 결과를 확인하며 작성하는 것이 도움됩니다.

4.가독성 향상

복잡한 JOIN 쿼리는 적절한 들여쓰기와 주석을 사용하여 가독성을 높이고, 서브쿼리나 CTE(Common Table Expression)를 활용하여 복잡한 쿼리를 구조화할 수 있습니다.

5.지속적인 학습과 실습

데이터베이스의 실행 계획을 분석하여 쿼리의 성능을 이해하고 개선하세요. 다양한 JOIN 기법(SELF JOIN, CROSS JOIN 등)을 익히고 적절한 상황에 활용하세요.

✍️ Edit on Github