AD JB Toolbox 01

MariaDB 강좌 / CSV 가져오기·내보내기: 경로·권한·문자 인코딩

CSV 가져오기는 구분자·따옴표·줄바꿈·인코딩을 맞춘 뒤 임시 적재 테이블에서 검증하고 실제 타입으로 옮기는 과정입니다. LOAD DATA LOCAL INFILE은 클라이언트 파일을 읽고 SELECT INTO OUTFILE은 서버에 파일을 쓰므로 경로와 권한의 기준이 서로 다릅니다.

파일을 누가 읽고 쓰는지 먼저 구분하기

방식 파일이 있는 곳 필요 조건
LOAD DATA LOCAL INFILE 클라이언트 PC 서버·클라이언트의 LOCAL 허용
LOAD DATA INFILE DB 서버 FILE 권한·서버 경로 제한
SELECT INTO OUTFILE DB 서버 FILE 권한·secure_file_priv·OS 쓰기 권한

LOCAL 허용은 신뢰할 서버에 접속하는 상황에서만 사용합니다. 클라이언트가 읽어 보낼 수 있는 파일 범위를 고려해야 하며, 오류를 없애려고 모든 서버의 전역 옵션과 파일 권한을 무조건 풀지 않습니다. LOAD DATA의 LOCAL·인코딩·엄격 모드 조건을 확인하세요. SQL 파일 실행과 클라이언트 경로는 명령줄 클라이언트로 접속하고 SQL 파일 실행하기, 실제 타입 열에 행을 옮길 때의 열·값 대응은 INSERT로 한 행과 여러 행 추가하기와 같은 원칙을 적용합니다.

UTF-8 CSV와 문자형 적재 테이블 준비하기

클라이언트 PC에 C:/mariadb-lab 폴더를 만들고 아래 내용을 products.csv로 저장합니다. UTF-8을 사용하고 첫 줄은 열 이름입니다. 쉼표가 들어 있는 상품명은 큰따옴표로 감싸야 합니다. 기존 파일을 덮어쓰지 말고 새 실습 파일인지 확인합니다.

product_id,product_name,price
1,"노트, 대형",3500.00
2,"머그컵",8000.00
3,"볼펜",1500.00

shopcourse에 접속해 csv45_stage·csv45_products가 없는지 확인한 다음 아래 코드를 실행합니다. 처음부터 숫자 열로 밀어 넣지 않고 원본 문자열을 남겨 잘못된 값을 찾습니다.

USE shopcourse;
CREATE TABLE csv45_stage(raw_id TEXT,raw_name TEXT,raw_price TEXT)
 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE csv45_products(product_id INT PRIMARY KEY,
 product_name VARCHAR(100) NOT NULL,price DECIMAL(10,2) NOT NULL CHECK(price>=0))
 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

클라이언트 파일을 적재하고 경고 확인하기

mariadb --protocol=tcp --host=127.0.0.1 --port=3306 --user=course_user --password --local-infile=1 --default-character-set=utf8mb4 shopcourse
LOAD DATA LOCAL INFILE 'C:/mariadb-lab/products.csv'
INTO TABLE csv45_stage CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n' IGNORE 1 LINES
(@id,@name,@price)
SET raw_id=@id,raw_name=@name,raw_price=TRIM(TRAILING '\r' FROM @price);
SHOW WARNINGS;
SELECT * FROM csv45_stage;
SELECT * FROM csv45_stage
WHERE raw_id IS NULL OR raw_id NOT REGEXP '^[0-9]+$'
 OR raw_price IS NULL OR raw_price NOT REGEXP '^[0-9]+([.][0-9]{1,2})?$'
 OR raw_name IS NULL OR raw_name='';
SELECT CAST(raw_id AS UNSIGNED) AS normalized_id,COUNT(*)
FROM csv45_stage GROUP BY CAST(raw_id AS UNSIGNED) HAVING COUNT(*)>1;
-- 형식 오류가 없음을 확인한 뒤 범위와 길이 검사
SELECT * FROM csv45_stage
WHERE CAST(raw_id AS DECIMAL(65,0))<1
 OR CAST(raw_id AS DECIMAL(65,0))>2147483647
 OR CAST(raw_price AS DECIMAL(65,2))>99999999.99
 OR CHAR_LENGTH(raw_name)>100;

서버의 local_infile이 허용되지 않으면 관리자와 정책을 먼저 확인합니다. LOCAL 적재는 strict 모드라고 모든 잘못된 값을 오류로 거절한다고 가정할 수 없으므로 경고와 원본 문자열을 확인해야 합니다. 이 예제는 LF로 줄을 나누고 마지막 가격의 CR을 제거해 일반적인 CRLF 파일도 처리하지만, 따옴표 안의 복잡한 줄바꿈·이스케이프 규칙은 실제 파일 생산자와 맞춰 추가 검증해야 합니다.

Ubuntu 클라이언트에서는 먼저 터미널에서 작업 폴더를 준비하고 UTF-8 products.csv를 그 안에 저장합니다.

mkdir -p "$HOME/mariadb-lab"
printf '%s\n' "$HOME/mariadb-lab/products.csv"

LOAD DATA LOCAL의 경로는 위 명령이 출력한 실제 절대 경로로 바꿉니다. 예를 들어 로그인 이름이 learner인 PC의 다음 경로는 그 클라이언트 파일을 뜻합니다.

LOAD DATA LOCAL INFILE '/home/learner/mariadb-lab/products.csv'
INTO TABLE csv45_stage CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n' IGNORE 1 LINES
(@id,@name,@price)
SET raw_id=@id,raw_name=@name,raw_price=TRIM(TRAILING '\r' FROM @price);
SHOW WARNINGS;

Windows 예제와 Ubuntu 예제 중 자신의 클라이언트에 맞는 하나만 실행합니다. 같은 stage에 둘 다 적재하면 중복됩니다. 서버가 Ubuntu인 OUTFILE은 아래 경로 정책을 별도로 따릅니다.

검증이 끝난 행만 실제 타입으로 옮기기

불량 형식 조회와 중복 조회가 모두 빈 결과인지 확인합니다. ID는 INT 양수 범위, 가격은 DECIMAL(10,2)의 범위, 상품명은 100문자 이내인지도 확인하세요. 여기의 세 행은 그 범위 안입니다. 문자형 ID 01과 1은 숫자로 같아질 수 있으므로 실제 변환 후 고유성도 판단해야 합니다. 잘못된 행이 있으면 아래 INSERT를 실행하지 말고 원본·정책을 고칩니다.

INSERT INTO csv45_products(product_id,product_name,price)
SELECT CAST(raw_id AS UNSIGNED),raw_name,CAST(raw_price AS DECIMAL(10,2))
FROM csv45_stage;
SELECT product_id,product_name,price FROM csv45_products ORDER BY product_id;

결과는 세 상품이며 첫 상품명은 쉼표를 포함한 노트, 대형입니다. LOAD를 같은 stage에 반복하면 행이 누적될 수 있으므로 단순히 다시 실행하지 않습니다. 실패한 적재를 정리할 때도 원본 파일과 필요한 오류 자료를 먼저 보관합니다.

서버 파일 내보내기와 클라이언트 출력을 구분하기

아래 코드는 FILE 권한이 있는 관리자 또는 승인된 내보내기 계정의 연결에서 실행합니다. 서버의 secure_file_priv와 실제 쓰기 권한을 확인하고 새 파일명을 사용합니다. 서버가 원격이면 C: 경로도 그 서버의 경로이지 현재 PC의 경로가 아닙니다.

SELECT @@secure_file_priv;
SELECT product_id,product_name,price FROM csv45_products ORDER BY product_id
INTO OUTFILE 'C:/mariadb-lab/products-export.csv'
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n';

출력은 열 이름 없는 CSV 세 행이며 기존 파일을 덮어쓰지 않습니다. 헤더가 필요하면 별도 도구나 명시한 내보내기 절차로 추가합니다. mariadb --batch의 기본 탭 구분 출력을 확장자만 .csv로 바꾸면 CSV가 되는 것은 아닙니다. SELECT INTO OUTFILE의 위치·권한 조건을 확인하세요.

Ubuntu 서버에서 OUTFILE을 사용할 때는 SELECT @@secure_file_priv;가 반환한 허용 경로와 MariaDB 서비스 계정의 쓰기 권한을 확인합니다. 예를 들어 허용 디렉터리가 /var/lib/mysql-files/라면 새 파일명을 다음처럼 지정합니다. 이 경로는 서버의 경로이며 클라이언트 홈 폴더와 다릅니다.

SELECT product_id,product_name,price FROM csv45_products ORDER BY product_id
INTO OUTFILE '/var/lib/mysql-files/products-export-new.csv'
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n';

실제 허용 경로가 다르면 그 경로로 바꾸고 해당 서버에서 한 번만 실행합니다. 기존 파일을 지우거나 FILE 권한을 넓혀 오류를 우회하지 않습니다.

원본·행 수·문자열을 대조하고 정리하기

행 수뿐 아니라 쉼표·한글·빈 문자열·NULL·줄바꿈이 같은 의미로 왕복하는지 확인합니다. CSV는 모든 도구에서 똑같은 NULL 규약을 갖지 않으므로 빈 셀과 값 없음의 정책을 정해야 합니다. 이 예제는 세 열 모두 값이 있는 간단한 교환 형식입니다. 파일은 보관 필요성을 확인해 별도로 정리하고 실습용 테이블만 삭제합니다.

DROP TABLE shopcourse.csv45_products;
DROP TABLE shopcourse.csv45_stage;
같은 카테고리의 다른 글
MariaDB 강좌 / 백업을 별도 데이터베이스에 복원하고 검증하기

MariaDB 강좌 / 백업을 별도 데이터베이스에 복원하고 검증하기

새 원본과 별도 빈 DB를 준비해 덤프를 복원하고 객체·행 수·키·업무 합계를 비교합니다. 파일 안의 DB 전환·삭제 문장, 클라이언트 호환성과 부분 복원 실패의 대응을 설명합니다.

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

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

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

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

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

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

MariaDB / 연산자 / 논리 연산자

MariaDB / 연산자 / 논리 연산자

MariaDB의 논리 연산자에 대하여 알아보겠습니다. 예제로 사용하는 테이블은 아래의 데이터를 가진 T_2이라는 테이블입니다. 각 열의 데이터 형식은 다음과 같습니다. C_1 : 정수 C_2 : 문자 +----+------+------+ | ID | C_1 | C_2 | +----+------+------+ | 1 | 1 | A | | 2 | ...

MariaDB 강좌 / mariadb-dump로 데이터베이스 백업하기

MariaDB 강좌 / mariadb-dump로 데이터베이스 백업하기

별도 주문 실습 DB를 준비해 mariadb-dump로 새 SQL 백업 파일을 만듭니다. 종료 코드·해시·문자 인코딩, InnoDB 스냅샷과 동시 DDL의 한계, 객체·권한·별도 복원 검증 기준을 설명합니다.

MariaDB 강좌 / 서브쿼리·EXISTS·NOT EXISTS로 조건 표현하기

MariaDB 강좌 / 서브쿼리·EXISTS·NOT EXISTS로 조건 표현하기

평균값 서브쿼리와 EXISTS·NOT EXISTS를 상품·고객·주문으로 실습합니다. 한 값의 반환 조건, 상관 키 누락, paid 주문 부재의 정의와 NOT IN의 NULL 함정을 설명합니다.

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

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

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

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

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

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

MariaDB 강좌 / 여러 테이블 JOIN하기: 중복 행과 집계 오류 확인

MariaDB 강좌 / 여러 테이블 JOIN하기: 중복 행과 집계 오류 확인

고객·주문·상세·상품을 JOIN하고 행 증가에 따른 배송비 중복 합산을 확인합니다. COUNT DISTINCT와 SUM DISTINCT의 차이, 주문별 선집계로 올바른 금액을 얻는 방법을 설명합니다.

MariaDB 강좌 / SELECT로 열 선택하기: 별칭·계산식·DISTINCT

MariaDB 강좌 / SELECT로 열 선택하기: 별칭·계산식·DISTINCT

SELECT의 열 선택과 AS 별칭, 행별 계산식, DISTINCT의 열 조합 기준을 실습합니다. 원본 데이터와 출력 결과를 구분하고 WHERE에서 별칭을 사용할 때의 주의점을 설명합니다.