AD JB Toolbox 01

MariaDB 강좌 / INNER JOIN으로 서로 연결된 데이터 조회하기

INNER JOIN은 연결 조건이 참인 두 테이블의 행 조합만 반환합니다. 어떤 열로 연결할지 ON에 명시하고, 결과 한 행이 고객인지 주문인지부터 정해야 일대다 관계에서 늘어난 행을 중복 오류로 오해하지 않습니다.

연결 조건과 결과의 단위 정하기

주문 화면에 고객 이름을 보여 주려면 주문의 customer_id와 고객의 기본키를 연결합니다. 두 테이블에 customer_id라는 같은 이름이 있어도 자동으로 올바르게 연결되는 것은 아닙니다. AS로 짧은 테이블 별칭을 정하고 각 열의 소속을 명시합니다. 연결 키의 근거는 테이블 관계 설계하기: 일대다·다대다·외래키의 관계 설계이며, 일치 행이 없는 기준 행까지 남길 때는 LEFT JOIN으로 연결되지 않은 행도 조회하기을 선택합니다.

요소 예 역할
주 테이블 orders AS o 주문 한 건을 기준으로 시작
연결 테이블 customers AS c 고객 이름 읽기
ON 조건 c.customer_id=o.customer_id 관계 키를 대응
WHERE 조건 o.status='paid' 반환할 주문의 업무 조건

고객·주문과 연결 데이터를 함께 준비하기

학습용 MariaDB 서버의 shopcourse에 course_user로 접속합니다. 명령줄 클라이언트와 UTF-8 SQL 파일 실행 방법대로 코드를 UTF-8 파일에 저장하고 SOURCE C:/mariadb-lab/lesson.sql;처럼 실제 파일 경로로 실행할 수 있습니다. SQL 문은 운영체제 터미널이 아닌 MariaDB 연결 안에서 실행합니다.

먼저 SELECT VERSION(), DATABASE();와 SHOW TABLES;로 대상 서버·DB·이름 중복을 확인합니다. 아래 c26_customers, c26_products, c26_orders, c26_items는 이 글의 독립된 연습용 이름입니다. 이미 존재하면 정의와 행을 확인하고, 보관할 자료가 없는 이 글의 실습 테이블임이 확실할 때만 마지막 절의 정리 순서로 지운 뒤 다시 만듭니다. 기존 강좌의 동명 없는 테이블이나 운영 데이터는 삭제하지 않습니다.

USE shopcourse;
CREATE TABLE c26_customers (
 customer_id INT NOT NULL PRIMARY KEY,
 customer_name VARCHAR(50) NOT NULL,
 city VARCHAR(30) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE c26_products (
 product_id INT NOT NULL PRIMARY KEY,
 product_name VARCHAR(100) NOT NULL,
 category VARCHAR(30) NOT NULL,
 price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
 stock INT NOT NULL CHECK (stock >= 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE c26_orders (
 order_id INT NOT NULL PRIMARY KEY,
 customer_id INT NOT NULL,
 ordered_at DATETIME NOT NULL,
 status VARCHAR(20) NOT NULL,
 shipping_fee DECIMAL(10,2) NOT NULL DEFAULT 1000,
 CONSTRAINT fk_c26_order_customer FOREIGN KEY (customer_id)
  REFERENCES c26_customers(customer_id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE c26_items (
 order_id INT NOT NULL,
 product_id INT NOT NULL,
 quantity INT NOT NULL CHECK (quantity > 0),
 unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price >= 0),
 PRIMARY KEY (order_id, product_id),
 CONSTRAINT fk_c26_item_order FOREIGN KEY (order_id)
  REFERENCES c26_orders(order_id) ON DELETE RESTRICT,
 CONSTRAINT fk_c26_item_product FOREIGN KEY (product_id)
  REFERENCES c26_products(product_id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO c26_customers VALUES
 (1,'김민수','서울'),(2,'이서연','부산'),
 (3,'박지우','서울'),(4,'최하늘','대전');
INSERT INTO c26_products VALUES
 (1,'노트','문구',3500,20),(2,'볼펜','문구',1500,50),
 (3,'머그컵','생활',8000,12),(4,'스탠드','생활',25000,5);
INSERT INTO c26_orders
 (order_id,customer_id,ordered_at,status,shipping_fee) VALUES
 (101,1,'2026-01-10 10:00:00','paid',1000),
 (102,1,'2026-01-20 11:00:00','paid',1000),
 (103,2,'2026-02-05 12:00:00','paid',1000),
 (104,3,'2026-02-10 13:00:00','canceled',1000);
INSERT INTO c26_items VALUES
 (101,1,2,3500),(101,2,3,1500),(102,3,1,8000),
 (103,1,1,3500),(103,3,2,8000),(104,2,1,1500);

실습 테이블 네 개를 완전히 준비하므로 다른 JOIN 강좌를 먼저 실행할 필요는 없습니다. 이 글은 customers·orders만 사용하고 나머지는 동일한 주문 데이터의 구조를 유지하기 위해 함께 제공합니다. 학습을 끝낸 뒤에는 이 글의 접두어 c26_ 객체만 정리합니다.

주문과 고객을 기본키로 연결하기

SELECT o.order_id,c.customer_id,c.customer_name,o.status
FROM c26_orders AS o
INNER JOIN c26_customers AS c ON c.customer_id=o.customer_id
ORDER BY o.order_id;
order_id customer_id 고객 상태
101 1 김민수 paid
102 1 김민수 paid
103 2 이서연 paid
104 3 박지우 canceled

김민수 이름이 두 번 보이는 것은 주문 두 건에 각각 대응하기 때문입니다. 결과 한 행은 고객 하나가 아니라 주문 하나입니다. 고객 목록 한 번씩만 필요하면 EXISTS나 목적에 맞는 집계를 검토하며 무조건 DISTINCT로 행을 숨기지 않습니다. MariaDB JOIN 문법에서 ON과 테이블 별칭의 구성을 확인하세요.

연결 조건과 필터 조건을 분리하기

SELECT o.order_id,c.customer_name,o.ordered_at
FROM c26_orders AS o
INNER JOIN c26_customers AS c ON c.customer_id=o.customer_id
WHERE o.status='paid' ORDER BY o.order_id;

paid 주문은 101·102·103번이며 canceled인 104번이 빠집니다. ON은 어떤 고객과 주문이 연결되는지를, WHERE는 연결된 주문 중 어떤 상태를 보여 줄지를 드러냅니다. INNER JOIN에서는 조건을 이동해 같은 결과를 만들 수 있는 경우도 있지만 이후 OUTER JOIN으로 확장할 때 의미가 달라질 수 있으므로 역할을 구분하는 습관이 좋습니다.

양쪽의 customer_id를 모두 표시하면 연결 키가 맞는지 점검하기 쉽습니다. customer_name처럼 업무상 바뀔 수 있는 값으로 연결하거나, 서로 의미가 다른 번호를 이름만 같다는 이유로 연결하지 않습니다.

ON 조건이 빠진 곱집합과 행 증가 확인하기

SELECT COUNT(*) AS unconstrained_pairs
FROM c26_customers AS c CROSS JOIN c26_orders AS o;

고객 4명과 주문 4건의 모든 조합은 16개입니다. CROSS JOIN은 모든 조합이 필요할 때 쓰는 기능이지만 잘못된 JOIN 조건으로 같은 결과를 만들면 각 주문에 엉뚱한 고객까지 연결됩니다. 실제 연결 JOIN의 4행과 비교해 보세요.

외래키가 있는 이 데이터에서는 모든 주문의 부모가 존재하므로 주문 기준 INNER JOIN에서 네 건이 모두 남습니다. 실제로 연결되지 않은 주문이 있을 수 있는 데이터에서는 INNER JOIN이 그 행을 제외하므로, 누락을 점검할 때는 LEFT JOIN과 NULL 검사를 사용해야 합니다.

모호한 열·잘못된 연결을 확인하고 반복하기

Column is ambiguous 오류가 나면 양쪽에 있는 customer_id 같은 열 앞에 o. 또는 c.를 붙입니다. 예상보다 행이 많으면 두 연결 열이 고유한지와 일대다 관계를, 너무 적으면 필터와 연결되지 않는 키를 확인합니다. 결과 정렬도 ORDER BY로 명시하고 원본 저장 순서에 의존하지 않습니다.

반복하기 전 현재 DB와 실제 정의·행을 확인하고 필요한 자료를 백업합니다. 보관할 데이터가 없는 이 글의 전용 실습 객체만 아래 순서로 정리합니다. 외래키가 있으면 자식부터 지웁니다. DROP TABLE은 구조와 데이터를 모두 없애며 일반적인 ROLLBACK으로 복원되지 않으므로 취소 가능한 DML 실습과 분리합니다.

DROP TABLE shopcourse.c26_items;
DROP TABLE shopcourse.c26_orders;
DROP TABLE shopcourse.c26_products;
DROP TABLE shopcourse.c26_customers;
같은 카테고리의 다른 글
MariaDB 강좌 / Python에서 MariaDB 연결하고 매개변수 쿼리 사용하기

MariaDB 강좌 / Python에서 MariaDB 연결하고 매개변수 쿼리 사용하기

PyMySQL로 연결·삽입·조회·확정·취소를 수행하는 전체 Python 예제를 제공합니다. 따옴표·한글 입력과 SQL 인젝션 형태의 문자열을 값으로 다루고 정렬 열은 허용 목록으로 제한합니다.

MariaDB 강좌 / 로컬·원격 접속과 연결 오류 해결하기

MariaDB 강좌 / 로컬·원격 접속과 연결 오류 해결하기

로컬 TCP 연결을 명시하고 서버 정보·현재 계정·실제 테이블 권한을 확인합니다. 연결 거절·인증·DB 오류를 나누어 진단하고 원격 수신 주소·계정·TLS의 적용 조건을 설명합니다.

MariaDB 강좌 / 트랜잭션 시작하기: InnoDB·COMMIT·ROLLBACK

MariaDB 강좌 / 트랜잭션 시작하기: InnoDB·COMMIT·ROLLBACK

InnoDB에서 같은 변경을 ROLLBACK과 COMMIT으로 끝내어 미확정·확정의 차이를 비교합니다. 연결 유지, 영향 행 수 검사, 오류 처리와 구조 변경·외부 작업의 취소 한계를 설명합니다.

MariaDB 강좌 / 문자열 다루기: 연결·추출·치환·길이

MariaDB 강좌 / 문자열 다루기: 연결·추출·치환·길이

문자열 연결·추출·치환과 길이 계산을 상품 데이터로 실습합니다. NULL과 빈 문자열의 연결 차이, UTF-8 문자 수·바이트 수, 원본을 보존하며 가공 결과를 확인하는 기준을 설명합니다.

MariaDB 강좌 / ORDER BY·LIMIT로 정렬하고 조회 범위 제한하기

MariaDB 강좌 / ORDER BY·LIMIT로 정렬하고 조회 범위 제한하기

ORDER BY의 여러 열·방향과 LIMIT·OFFSET을 같은 가격의 상품으로 실습합니다. 동점 해소, 페이지 경계 변화, 마지막 정렬값을 이용한 조회와 NULL 배치를 설명합니다.

MariaDB 강좌 / Docker로 MariaDB 실습 환경 만들기

MariaDB 강좌 / Docker로 MariaDB 실습 환경 만들기

Compose 파일로 MariaDB 포트·암호 파일·데이터 볼륨·헬스체크를 구성합니다. 연결 확인과 컨테이너 교체 실습을 통해 데이터 유지, 초기화와 접속 오류의 차이를 이해합니다.

MariaDB / Query / LEFT JOIN, RIGHT JOIN, INNER JOIN

MariaDB / Query / LEFT JOIN, RIGHT JOIN, INNER JOIN

SQL의 JOIN은 여러 테이블을 한 번에 나타낼 때 사용합니다. 대표적인 JOIN 방식은 LEFT JOIN, RIGHT JOIN, INNER JOIN이 있습니다. 다음은 세 가지 JOIN의 차이를 알아보기 위한 간단한 예제 테이블입니다.

MariaDB 강좌 / 이벤트 스케줄러로 정기 작업 실행하고 결과 확인하기

MariaDB 강좌 / 이벤트 스케줄러로 정기 작업 실행하고 결과 확인하기

비활성 일회성 이벤트를 생성한 뒤 미래 시각으로 활성화하고 결과 행과 예약 메타정보를 대조합니다. 반복 작업의 중복·겹침 위험, 권한·시간대 점검과 안전한 중지·정리 방법도 설명합니다.

MariaDB 강좌 / 테이블 관계 설계하기: 일대다·다대다·외래키

MariaDB 강좌 / 테이블 관계 설계하기: 일대다·다대다·외래키

고객·상품·주문·상세 네 테이블로 일대다와 다대다 관계를 설계합니다. 복합키·주문 당시 단가·외래키 실패 입력과 삭제 제한을 실행하고 업무상 삭제 정책을 구분합니다.

MariaDB 강좌 / 조회 종합 실습: 월별 매출·고객별 구매·미판매 상품

MariaDB 강좌 / 조회 종합 실습: 월별 매출·고객별 구매·미판매 상품

월별 paid 매출, 미구매 포함 고객별 구매 금액, 미판매 상품 조회를 종합 실습합니다. 주문·상세 단위와 취소 정책을 정하고 세 결과의 전체 금액을 교차 검증합니다.