직원 여덟 명의 생활용품 도매회사 새봄생활은 주문, 재고, 거래처를 엑셀 파일 하나로 관리합니다. 11월 첫 월요일, 그 파일에서 주문이 사라지는 일이 생깁니다. 엑셀 담당 윤서현 대리가 그 파일을 데이터베이스로 옮기는 한 달을 날짜 순서대로 따라갑니다.
데이터베이스를 처음 들어 봐도 괜찮습니다. 내 PC에 SQL Server를 깔고, 실제 엑셀을 DB로 옮기고, 질문을 던지고, 엑셀에서 새로 고침으로 받아 보는 데까지 전부 직접 해 봅니다. 페이지 안에서 SQL을 바로 쳐 볼 수도 있습니다.
새봄생활은 경기도 광주 오포읍에 물류센터를 둔 생활용품 도매회사입니다. 고무장갑, 락스, 키친타월, 쓰레기봉투 같은 물건을 동네 마트, 철물점, 식당, 학원, 펜션에 납품합니다. 거래처는 곳, 상품은 종입니다.
직원은 여덟 명입니다. 강태호 대표, 영업 박지훈 과장과 이민지 대리, 주문과 엑셀을 맡은 영업지원 윤서현 대리, 창고의 최동석 반장과 오세진 주임, 구매 한지우 대리, 경리 정미경 차장. 그리고 이 회사의 모든 일은 새봄생활_통합관리.xlsx라는 파일 하나를 거쳐 갑니다.
파일을 열면 시트가 여덟 개 있습니다. 맨 앞 '메모' 시트에는 3년 동안 쌓인 규칙이 별표를 달고 적혀 있습니다.
"규칙은 다 적어 놨는데, 지키는 건 결국 사람이야. 바쁘면 다들 그냥 친다."
나머지 시트는 거래처, 상품, 주문내역, 입출고, 그리고 수식으로 만든 보고서 세 장(재고현황, 월별매출, 거래처별매출)입니다. 주문내역 시트에는 2년 동안 줄이 쌓였습니다.
이 파일이 지금까지 무리 없이 돌아간 데에는 이유가 있습니다. 서현이 매일 저녁 표를 훑어보며 이상한 줄을 고쳐 왔기 때문입니다.
월요일 아침, 출근하자마자 이민지 대리에게서 메신저가 옵니다.
답장을 보내자마자 민지가 자리로 건너옵니다. 둘이 함께 파일의 저장 기록을 봅니다. 금요일 오후 5시 41분에 민지가 저장했고, 5시 48분에 서현이 저장했습니다. 서현은 그날 오후 내내 파일을 열어 둔 채 출고일을 고치고 있었습니다. 민지의 7분 전 저장 위에 서현의 파일이 통째로 덮어씌워진 것입니다.
"'같이 열면 저장이 꼬입니다'라고 메모에 써 놨는데. 써 놓는 걸로는 안 되는구나."
주문은 민지의 메신저 기록을 보고 다시 넣었습니다. 그런데 이번이 처음이 아닙니다. 지난달에는 창고 재고 숫자가, 지지난달에는 거래처 전화번호가 같은 식으로 되돌아갔습니다. 점심때 이 이야기를 전해 들은 강태호 대표에게서 메신저가 옵니다.
그날 저녁 서도현 개발자가 화상 회의로 들어옵니다. 서현의 설명을 끝까지 듣더니 짧게 정리합니다.
그날 도현이 화면에 적어 보인 낱말은 네 개였습니다. 앞으로 계속 나오니 엑셀에 빗대어 한 번만 정리합니다.
| 낱말 | 뜻 | 엑셀로 치면 |
|---|---|---|
| 데이터베이스(DB) | 규칙에 맞게 정리해서 쌓아 둔 데이터 묶음. 새봄생활에는 새봄DB 하나가 생깁니다. | 통합문서(xlsx 파일) 하나 |
| SQL Server | 데이터베이스를 쥐고 지키는 서버 프로그램. 사람들의 요청을 받아 처리합니다. MSSQL이라고도 부릅니다. | 엑셀 프로그램 자체. 다만 혼자 쓰는 게 아니라 여럿의 요청을 받는 창구 |
| SSMS | SQL Server에 접속해서 요청을 보내고 결과를 보는 프로그램(SQL Server Management Studio). | 엑셀 화면. 데이터를 보고 고치는 창 |
| SQL | 데이터베이스에 보내는 요청 문장. "거래처 표에서 이름과 전화번호를 보여 줘"를 SELECT 거래처명, 전화번호 FROM 거래처라고 씁니다. | 수식. 다만 셀 하나가 아니라 표 전체에 묻는 문장 |
엑셀은 표를 손으로 만지며 계산하고 보고서를 꾸미는 데 여전히 가장 좋은 도구입니다. 이 한 달 동안 바뀌는 건 원본을 어디에 두느냐입니다. 원본은 DB에, 엑셀은 그 원본을 들여다보는 창으로. 8장에서 그 모습이 완성됩니다.
도현의 첫 숙제는 간단했습니다. "윤 대리님 PC에 SQL Server를 깔고, 빈 데이터베이스 하나를 만들어 보세요." 회사에서는 보통 SQL Server를 별도 서버 컴퓨터에 두지만, 배우고 옮겨 보는 단계에서는 내 PC 하나에 서버와 SSMS를 같이 깔아도 됩니다.
윈도우 10 또는 11, 관리자 권한(설치 중 "예"를 누를 수 있어야 함), C 드라이브 여유 공간 10GB 정도. SQL Server Developer 에디션은 무료지만 개발·학습·테스트용입니다. 실제 회사 업무에 쓰려면 Express(무료, 용량 제한) 또는 유료 에디션을 씁니다. 맥을 쓴다면 부록 C를 보세요.
SQL2025-SSEI-StdDev.exe, 4MB 정도)을 실행합니다. 권한을 묻는 창이 뜨면 예를 누릅니다.눈에 보이는 프로그램은 없습니다. SQL Server는 화면이 없는 서비스로 설치되어, 컴퓨터를 켜면 뒤에서 늘 돌고 있습니다(작업 관리자의 서비스 탭에 MSSQLSERVER). 그 서버를 들여다보는 화면이 다음에 설치할 SSMS입니다.
서현은 퇴근 뒤 혼자 남아 설치를 걸었습니다. 진행 막대가 차는 동안, 곧 이 프로그램이 회사의 새 금고가 된다는 게 아직 실감 나지 않았습니다.
vs_SSMS.exe)을 받아 실행합니다.실습 자료는 zip 하나입니다. 새봄생활의 엑셀 원본, 엑셀에서 내보낸 CSV, 장마다 실행할 SQL 스크립트가 들어 있습니다.
C:\로 지정합니다.시작 메뉴에서 SQL Server Management Studio 22를 엽니다.
처음 한 번은 "SQL Server Management Studio에 로그인" 화면이 뜹니다.
로그인 화면 아래쪽 건너뛰고 나중에 계정을 추가하세요.를 누릅니다.
Microsoft 계정은 없어도 됩니다.
연결 창에서 서버 이름(S)에 localhost를 적고, 인증(H)은 Windows 인증 그대로, 서버 인증서 신뢰(T)에 체크한 뒤 연결을 누릅니다.
왼쪽 개체 탐색기에 localhost (SQL Server 17.0...)가 나타납니다.
localhost는 "이 컴퓨터"라는 뜻입니다. 인증서 신뢰에 체크하지 않으면 "신뢰되지 않은 기관에서 인증서 체인을 발급했습니다" 같은 오류가 납니다. 내 PC의 서버에는 정식 인증서가 없기 때문입니다.
메뉴 파일(F) > 열기 > 파일(Ctrl+O)로 C:\SaebomLab\3_SQL\02_DB만들기.sql을 엽니다.
파일 경로의 \는 한국어 윈도우에서 ₩로 보입니다. 같은 글자입니다.
F5를 누릅니다(또는 위쪽 실행(X)).
아래 결과 칸에 새봄DB 한 줄이 나오고, 맨 아래 노란 줄에 "쿼리가 실행되었습니다."가 뜹니다.
실행 전에 USE 새봄DB 아래 빨간 물결 밑줄이 보일 수 있습니다. SSMS가 아직 새봄DB를 몰라서 붙인 표시일 뿐, 실행에는 문제가 없습니다.
개체 탐색기의 데이터베이스 폴더를 오른쪽 클릭해 새로 고침을 누르고 펼칩니다.
새봄DB가 보입니다. 이게 새봄생활의 새 금고입니다.
지금_쓰는_DB = 새봄DB새 데이터베이스를 만들고 그 안으로 들어갔다는 뜻입니다새봄DB새로 고침을 해야 보입니다처음부터 다시 하고 싶다면. 99_처음부터_다시.sql을 열어 F5를 누르면 새봄DB가 통째로 지워집니다. 그다음 02번부터 다시 하면 됩니다.
다음 주에 가장 큰 거래처 ㈜대한상사와 내년 단가를 협상해야 합니다. 거래처별매출 시트를 출력해 보던 대표에게서 메신저가 옵니다.
답장을 보내자마자 대표가 출력물을 들고 직접 자리로 옵니다.
서현이 주문내역 시트를 대한상사로 필터해 봅니다. 거래처명 칸에 같은 회사가 네 가지 이름으로 적혀 있습니다.
줄 수와 금액은 엑셀 주문내역에 적힌 그대로 센 값입니다(점으로 적은 날짜 줄 포함, 취소 제외). 마지막 줄은 DB로 옮긴 뒤 계산한 실제 매출입니다. 엑셀에는 중복 붙여넣기와 금액 오타가 섞여 있어 네 줄을 더한 값과 조금 다릅니다. 이 차이는 6장에서 한 원까지 맞춰 봅니다.
거래처별매출 시트의 SUMIFS는 "거래처명이 정확히 이 글자와 같은 줄"만 더합니다. 그래서 ㈜대한상사로 적힌 줄만 그 줄에 잡혔습니다. 다른 이름으로 적힌 줄은 아예 빠지거나, 코드 없이 한 번 더 등록된 "대한상사(주)" 줄로 갈라졌습니다. 영업 담당이 바뀌고 신입이 들어올 때마다 표기가 하나씩 늘어난 결과입니다.
"대한상사만 그럴까. 이 파일, 제대로 한번 뜯어봐야겠다."
서현은 오후 내내 파일을 뒤졌고, 도현과 함께 찾은 문제를 아홉 가지로 정리했습니다. 아래 실습에서 문제를 하나씩 눌러 보세요. 실습 자료의 엑셀 파일에 실제로 들어 있는 줄들입니다.
도현은 서현이 정리한 목록을 보고 뿌리를 세 개로 묶었습니다. 이 셋이 곧 데이터베이스가 엑셀과 다르게 일하는 이유입니다.
가장 무서운 점은 이 문제들이 조용하다는 것입니다. 날짜가 글자로 들어간 줄은 월별매출 SUMIFS에서 그냥 빠지고, 엑셀은 아무 표시도 하지 않습니다. 실습 자료의 2025년 3월 매출은 엑셀에 원으로 나오지만 실제로는 원입니다.
도현이 처음으로 사무실에 왔습니다. 회의실 화이트보드에 주문내역 시트의 한 줄을 그대로 옮겨 적습니다.
"이 한 줄에 정보가 몇 종류 들어 있을까요?" 서현이 세어 봅니다. 주문에 대한 것(주문번호, 주문일, 상태, 출고일, 담당), 거래처에 대한 것(거래처명), 상품에 대한 것(품번, 상품명), 그리고 이 주문에서 이 상품을 몇 개 얼마에 샀는지(수량, 단가). 금액은 수량 x 단가라 따로 적을 필요도 없습니다.
화이트보드에는 표 다섯 개가 남았습니다.
그림 속 낱말 몇 개를 엑셀 말로 옮기면 이렇습니다.
| DB 낱말 | 뜻 | 새봄생활에서 | 엑셀로 치면 |
|---|---|---|---|
| 테이블(표) | 한 종류의 정보를 모은 표 | 거래처, 상품, 주문, 주문상세, 입출고 | 시트 |
| 행(줄) | 표 안의 한 건 | 거래처 한 곳, 주문 한 건 | 행 |
| 열(칸)과 자료형 | 칸 이름과, 그 칸에 들어갈 값의 종류(날짜, 정수, 글자) | 주문일은 DATE, 수량은 INT, 거래처명은 NVARCHAR(50) | 열 머리글 + 셀 서식. 다만 서식은 장식이고 자료형은 강제 |
| 기본키(PK) | 표 안에서 줄 하나를 콕 집는, 겹치지 않는 값 | 거래처코드 C012, 주문번호 250306-01 | 사번이나 주민번호 같은 "겹치면 안 되는 번호" 열 |
| 외래키(FK) | 다른 표의 기본키를 가리키는 칸. 가리키는 대상이 실제로 있어야 함 | 주문의 거래처코드는 거래처 표에 있는 코드여야 함 | VLOOKUP으로 찾아오던 연결. 다만 못 찾는 값은 아예 못 들어옴 |
주문을 주문(머리)과 주문상세(줄)로 나눈 이유도 같습니다. 주문 한 건에는 상품이 여러 개 있습니다. 주문일과 거래처는 주문마다 한 번이면 되고, 상품과 수량은 줄마다 있어야 합니다. 엑셀처럼 한 표에 두면 주문일과 거래처명을 상품 수만큼 반복해 적게 됩니다.
아래 실습에서 엑셀 주문내역의 줄을 하나 골라 보세요. 그 줄의 정보가 DB의 어느 표, 어느 칸으로 가는지 보입니다.
상품 표의 판매가는 "지금 파는 값"이고, 주문상세의 단가는 "그 주문 때 판 값"입니다. 새봄생활은 2026년 1월에 일부 상품 값을 올렸습니다. 판매가 하나만 두면 2025년 주문의 매출이 올린 값으로 다시 계산돼 버립니다. 이름은 같아 보여도 뜻이 다른 정보입니다.
도현이 보낸 스크립트를 열어 보니 표를 만드는 문장(CREATE TABLE)이 다섯 개 있습니다. 서현의 눈에 들어온 건 칸 이름 옆에 붙은 말들이었습니다.
NOT NULL: 비워 둘 수 없음. 주문일 없는 주문은 못 들어옵니다.PRIMARY KEY: 겹칠 수 없는 번호. 주문 표에 같은 주문번호가 두 번 들어오면 거절합니다. 다만 주문상세는 (주문번호, 줄번호)가 키라서 같은 상품 줄을 다른 줄번호로 또 넣는 것까지는 못 막습니다. 그래서 6장에서 중복 줄을 따로 걸러 냅니다.REFERENCES 거래처(거래처코드): 거래처 표에 있는 코드만 받음. 표기가 흔들릴 틈이 사라집니다.CHECK (상태 IN (N'접수', N'출고완료', N'취소')): 정해 둔 말만 받음. "완료", "출고됨" 같은 변형이 못 들어옵니다.DATE, INT: 칸의 자료형. 날짜 칸에 "2025.3.5" 같은 글자가 그대로 남지 않고, 수량 칸에 "10개"가 못 들어옵니다.규칙이 정말 막아 주는지는 직접 어겨 보면 압니다. 아래 실습은 이 페이지 안의 작은 DB에 일부러 틀린 값을 넣습니다. 결과 칸에는 같은 문장을 실제 SQL Server에 넣었을 때 SSMS에 뜬 메시지 원문을 함께 보여 줍니다.
첫 거절 메시지가 뜨자 서현은 뭔가 망가뜨린 줄 알고 의자를 뒤로 뺐습니다. 도현은 웃었습니다. "그게 규칙이 일하는 모습이에요. 엑셀이었으면 그냥 들어갔을 값이에요."
05_1_테이블만들기.sql을 열고 F5를 누릅니다.
결과 칸에 만든_표 다섯 줄(거래처, 상품, 주문, 주문상세, 입출고)이 나옵니다.
개체 탐색기에서 새봄DB > 테이블을 새로 고침 하고 펼칩니다.
dbo.거래처 같은 이름으로 표 다섯 개가 보입니다. dbo는 표가 들어 있는 기본 폴더 이름입니다.
05_2_막히는지_해보기.sql을 열고 F5를 누릅니다.
아래 칸이 저절로 메시지 탭으로 바뀌고 빨간 글씨가 여러 줄 나옵니다. 맨 아래 노란 줄에는 "쿼리가 완료되었으나 오류가 발생했습니다."
빨간 글씨를 위에서부터 읽으며 스크립트의 번호 주석과 맞춰 봅니다.
"PRIMARY KEY 제약 조건 'PK_거래처'을(를) 위반했습니다", "CHECK 제약 조건 "CK_상태는_접수_출고완료_취소"과(와) 충돌했습니다"처럼 규칙 이름이 그대로 나옵니다.
결과 탭을 눌러 맨 아래 표를 봅니다.
마지막 줄이 0 0 0 0입니다. 연습으로 넣었던 줄은 스크립트 끝에서 지웠습니다.
0 0 0 0표는 비어 있고 규칙만 세워진 상태. 6장에서 이 표들을 채웁니다표는 세웠고 이제 짐을 옮길 차례입니다. 서현은 엑셀 내용을 복사해서 바로 붙이면 되는 줄 알았습니다. 도현의 생각은 달랐습니다.
SQL Server가 엑셀 파일을 직접 읽게 할 수도 있지만, 추가 드라이버가 필요하고 날짜·숫자가 뒤틀리는 일이 잦습니다. 실무에서 흔히 쓰는 길은 시트마다 CSV(쉼표로 칸을 나눈 글자 파일)로 저장해 넘기는 것입니다.
실습 자료의 2_CSV 폴더에 이렇게 저장한 파일 다섯 개가 이미 들어 있습니다(창고 실사표 포함). 시트마다 직접 저장해 봐도 같은 내용이 나옵니다. CSV를 메모장으로 열어 보면 단가가 "1,350"처럼 따옴표와 쉼표째 저장된 것이 보입니다. 엑셀은 화면에 보이는 모양 그대로 내보내기 때문입니다. 엑셀로 열면 따옴표가 감춰지고, 그 상태로 다시 저장하면 전화번호 앞의 0 같은 값이 바뀔 수 있으니 실습 자료의 CSV는 엑셀로 열어 저장하지 마세요.
06_1_CSV_받아두기.sql은 준비라는 칸막이(스키마) 안에 엑셀과 열 순서가 똑같은 표를 만들고, 모든 칸을 글자(NVARCHAR)로 둡니다. 그리고 BULK INSERT로 CSV를 통째로 부어 넣습니다. 글자 칸이니 어떤 값도 거절당하지 않습니다.
3장에서 눈으로 찾은 문제를 이번엔 SQL로 셉니다(06_2_문제찾기.sql). 예를 들어 "주문에 적힌 거래처명 중 거래처 시트에 없는 이름"은 이렇게 묻습니다.
결과는 여덟 줄이었습니다. 서현은 이 목록을 영업팀 단톡방에 올렸습니다.
확인한 결과는 정리표로 남깁니다(06_3_정리표_만들기.sql). "이렇게 적힌 이름은 이 거래처코드"라는 표입니다. 컴퓨터가 "대한상사"와 "(주)대한상사"가 같은 회사라고 짐작하게 두지 않고, 사람이 확인한 답을 데이터로 적어 둡니다. 바뀌기 전 상품명 '고무장갑 중'도 같은 방법으로 P1002에 연결합니다.
06_4_본표로_옮기기.sql이 준비 표에서 꺼내 5장의 표로 옮깁니다. 옮기면서 다음을 다듬습니다.
| 엑셀에 있던 모습 | 옮길 때 하는 일 | 쓰는 SQL |
|---|---|---|
| "2025.3.5" 같은 글자 날짜 | 날짜로 바꿉니다. SQL Server는 점으로 적은 날짜도 날짜로 읽습니다. | TRY_CONVERT(DATE, 주문일) |
| "1,350", "130개" | 쉼표와 '개'를 지우고 숫자로 바꿉니다. | REPLACE, TRY_CONVERT(INT, ...) |
| "대한상사", "카페온도" | 정리표로 거래처코드를 찾습니다. | JOIN 준비.거래처명_정리 |
| 품번 빈칸 | 상품명으로 품번을 찾습니다(옛 이름 포함). | COALESCE(품번, 정리표의 품번) |
| 똑같은 줄 두 번 | 한 번만 넣습니다. | SELECT DISTINCT |
| 금액 칸(수식, 오타 포함) | 옮기지 않습니다. 필요할 때 수량 x 단가로 계산합니다. | (없음) |
| 코드 없이 한 번 더 등록된 "대한상사(주)" | 옮기지 않습니다. 그 이름은 정리표가 C012로 연결합니다. | WHERE 거래처코드 IS NOT NULL |
옮긴 DB로 총매출을 내 보니 엑셀 월별매출 시트의 합계와 다릅니다. 서현은 순간 이사가 잘못된 줄 알았습니다.
매출이 맞자 창고 쪽이 남았습니다. 서현은 최동석 반장이 10월 31일에 센 실사표를 DB의 재고와 비교했고, 개 품목이 달랐습니다. 원인을 묻는 질문(06_5의 4번)이 답을 줬습니다. 취소된 주문 건이 창고 출고 기록에 그대로 남아 있었던 것입니다. 주문은 취소됐고 물건은 창고에 있는데, 장부상으로는 나간 것으로 되어 있었습니다.
"취소되면 상태만 바꾸면 된다며. 창고에서 출고를 지워야 하는 줄은 아무도 안 알려 줬지."
06_6_취소출고_바로잡기.sql은 틀린 출고 기록을 지우지 않고 되돌리는 입고 기록을 한 줄씩 더합니다. 장부를 고칠 때 지우개 대신 정정 기록을 남기는 것과 같습니다. 나중에 누가 봐도 "11월 11일에 이관하면서 바로잡았다"가 보입니다. 다시 비교하니 남은 차이는 두 품목, 창고에서 실제로 깨진 것뿐이었습니다. 이 둘은 9장에서 처리합니다.
06_1_CSV_받아두기.sql을 열고 F5.
결과 칸에 다섯 줄: 거래처 , 상품 , 주문내역 , 입출고 , 창고실사 . 엑셀의 줄 수와 같습니다.
"파일 ...을(를) 열 수 없으므로 대량 로드할 수 없습니다. 운영 체제 오류 코드 3(지정된 경로를 찾을 수 없습니다.)"이 뜨면 실습 폴더가 C:\SaebomLab이 아닌 곳에 있는 것입니다. 폴더를 옮기거나 스크립트의 경로 다섯 줄을 고칩니다. 실행 전 빨간 밑줄은 무시해도 됩니다.
06_2_문제찾기.sql을 열고 F5.
결과 칸에 표가 여덟 개(A~H) 나옵니다. A는 흔들린 이름 8가지, C는 점 날짜 줄, G는 겹친 주문 줄들입니다.
06_3_정리표_만들기.sql을 열고 F5.
마지막 두 표가 모두 0이면 성공. 주문에 적힌 모든 거래처명, 그리고 품번이 빈 줄의 상품명이 정리표에서 찾아진다는 뜻입니다.
06_4_본표로_옮기기.sql을 열고 F5.
거래처 , 상품 , 주문 , 주문상세 , 입출고 .
06_5_엑셀과_맞춰보기.sql을 열고 F5.
첫 표의 다섯 숫자로 "엑셀 합계 + 글자날짜 - 중복 + 오타 = DB 합계"를 계산기로 확인해 보세요. 셋째 표는 실사와 다른 개 품목입니다.
06_6_취소출고_바로잡기.sql을 열고 F5.
실사와 다른 품목이 두 줄만 남습니다: .
엑셀 합계에서 출발해 세 가지 차이를 더하고 빼면 정확히 이 숫자중간에 꼬였다면. 06_4는 실행할 때마다 본 표를 비우고 다시 채우고, 06_6은 두 번 실행해도 정정 기록이 한 번만 들어갑니다. 06_1부터 차례로 다시 실행하면 됩니다.
이사가 끝났으니 대표의 질문에 다시 답할 차례입니다. 엑셀이었다면 필터를 걸고, 이름 네 가지를 찾아 SUMIFS를 네 번 쓰고, 더했을 겁니다. DB에서는 문장 하나입니다.
SQL은 쓰는 순서와 읽는 순서가 다릅니다. FROM부터 읽으면 쉽습니다. "주문상세에서(FROM), 주문을 주문번호로 이어 붙이고(JOIN), 거래처코드가 C012이고 취소가 아닌 줄만 골라(WHERE), 연도별로 묶어(GROUP BY), 수량 x 단가를 더해(SUM) 보여 줘(SELECT)." COUNT(DISTINCT o.주문번호)는 주문번호를 겹치지 않게 센 수, 곧 주문 건수입니다. 결과는 원(2025년)이었습니다. 엑셀이 보여 준 원의 두 배가 넘습니다.
서현은 이 숫자를 그대로 대표에게 보냅니다.
| 엑셀에서 | SQL에서 | 한 줄 예 |
|---|---|---|
| 시트 열기 | SELECT * FROM 표 | SELECT * FROM 거래처 |
| 필터 | WHERE | WHERE 상태 = N'접수' |
| XLOOKUP, VLOOKUP | JOIN ... ON | JOIN 거래처 AS c ON c.거래처코드 = o.거래처코드 |
| SUMIFS, 피벗 | GROUP BY + SUM, COUNT | GROUP BY c.거래처명 |
| 정렬 | ORDER BY (내림차순은 DESC) | ORDER BY 매출 DESC |
| 위에서 10개만 | TOP 10 | SELECT TOP 10 ... |
| 피벗 결과에 다시 필터 | HAVING | HAVING COUNT(*) >= 25 |
SQL에서 글자는 작은따옴표로 감쌉니다. 앞의 N은 "한글 같은 유니코드 글자"라는 표시입니다. SQL Server에서 한글을 비교할 때는 N'취소'처럼 붙여 주는 습관을 들이세요.
아래 실습판은 이 페이지 안에 들어 있는 새봄DB입니다. 6장까지 끝낸 상태 그대로입니다. 왼쪽 목록에서 질문을 골라 실행해 보고, 문장을 고쳐 다시 실행해 보세요. 아래쪽 직접 해 보기 네 문제는 결과 표가 맞으면 정답으로 표시됩니다.
07_질문하기.sql을 열고, -- @Q5 아래 SELECT부터 ORDER BY 연;까지 마우스로 드래그해 선택합니다.
F5를 누릅니다.
선택한 문장만 실행되어 연도별 대한상사 매출 세 줄이 나옵니다.
아무것도 선택하지 않고 F5를 누르면 파일 전체가 실행되어 결과 표가 아홉 개 나옵니다. 그래도 괜찮습니다. 조회만 하는 문장이라 아무것도 바뀌지 않습니다.
Q4의 TOP 10을 TOP 3으로 고쳐 선택 실행해 봅니다.
세 줄만 나옵니다. 1위는 입니다.
가이드의 직접 해 보기 네 문제를 SSMS 새 쿼리 창(새 쿼리(N))에 써 봅니다. 창 위쪽 데이터베이스 선택 칸이 새봄DB인지 확인하세요.
"개체 이름 '주문'이(가) 유효하지 않습니다."가 뜨면 데이터베이스 선택 칸이 master로 되어 있는 것입니다. 칸에서 새봄DB를 고르거나 문장 맨 위에 USE 새봄DB;를 씁니다.
DB 이야기를 처음 들었을 때 가장 걱정한 사람은 정미경 차장이었습니다. 매달 엑셀로 만드는 보고서가 스무 장이 넘습니다. 금요일 아침, 차장에게서 메신저가 옵니다.
서현의 말대로 보고서는 엑셀에서 그대로 만들고, 숫자만 DB에서 가져옵니다. 이를 위해 DB에 뷰(VIEW)를 두 개 만듭니다. 뷰는 질문에 이름을 붙여 저장해 둔 것입니다. 표처럼 보이지만 데이터를 따로 갖지 않고, 열 때마다 원본 표에서 새로 계산합니다.
엑셀에서는 이 뷰를 "데이터 가져오기"로 연결합니다. 한 번 연결해 두면 다음 달부터는 새로 고침 한 번이면 끝입니다. 복사해 붙이는 사람이 없으니 붙여넣다 틀릴 일도, 누가 언제 붙였는지 헷갈릴 일도 없습니다. 그날 오후 서현이 차장의 엑셀에 연결해 보이자, 차장은 새로 고침을 몇 번이고 다시 눌러 봤습니다.
SSMS에서 08_엑셀용_보기만들기.sql을 열고 F5.
결과 칸에 연월별 매출 표와 대한상사 연도별 매출이 나옵니다.
엑셀에서 새 통합 문서를 열고 데이터 탭 > 데이터 가져오기 > 데이터베이스에서(D) > SQL Server 데이터베이스에서(S)를 누릅니다.
서버에 localhost, 데이터베이스(선택 사항)에 새봄DB를 적고 확인.
인증 창 왼쪽 Windows에서 내 현재 자격 증명 사용이 골라진 그대로 연결.
"암호화 지원" 안내가 뜨면 확인.
"암호화된 연결을 사용하여 데이터 원본에 연결할 수 없습니다..."라는 안내입니다. 내 PC의 SQL Server에 정식 인증서가 없어서 뜹니다. 같은 컴퓨터 안의 연결이라 괜찮습니다.
회사 서버에 연결할 때는 IT 담당자가 인증서를 설정해 두어 이 안내가 뜨지 않는 것이 정상입니다.
탐색 창에서 보고_월별매출을 누르고 오른쪽 미리 보기를 확인한 뒤 로드.
새 시트에 표가 생기고, 오른쪽 "쿼리 및 연결" 창에 "개의 행이 로드되었습니다."가 뜹니다.
연월 머리글의 단추를 눌러 텍스트 오름차순 정렬.
DB가 돌려주는 묶음 결과에는 정해진 순서가 없습니다. 정렬은 보는 쪽에서 합니다.
파일을 C:\SaebomLab\월별보고.xlsx로 저장합니다.
9장에서 다시 엽니다.
9장을 마친 뒤 이 파일로 돌아와 데이터 탭 > 모두 새로 고침을 누릅니다.
9장에서 넣은 새 주문 때문에 2026-11 줄이 생기고, 9장에서 취소한 10월 주문만큼 2026-10 줄은 줄어듭니다. 아무것도 복사하지 않았는데 숫자가 바뀝니다.
보고_월별매출, 행2024-11부터 2026-10까지. 매출 합계는 6장의 DB_매출_합계와 같습니다이번 주부터 새 주문은 엑셀이 아니라 DB에 바로 넣습니다. 그 전에 할 일이 두 가지 있습니다.
월요일 아침, 서현은 엑셀 '재고현황' 시트의 SUMIFS 세 개를 뷰 하나로 바꿉니다(09_1). 재고는 어디에 따로 적어 두는 숫자가 아니라 입출고 기록을 더해서 계산되는 숫자이기 때문입니다. 이어서 6장에 남겨 둔 두 품목, 창고에서 실제로 파손된 두 품목()을 실사에 맞춰 조정 기록으로 남깁니다(09_2). 여기서도 숫자를 덮어쓰지 않고 마이너스 기록을 한 줄씩 더합니다. 이제 DB 재고와 창고 실사가 모든 품목에서 같습니다.
화요일 오후, 박지훈 과장의 휴대폰이 울립니다. 대한상사 구매팀 김주연 과장입니다.
주문 하나를 넣으려면 주문 표에 1줄, 주문상세 표에 3줄이 함께 들어가야 합니다. 서현이 상세 셋째 줄의 품번을 P1O32로 칩니다. 숫자 0 자리에 영문 O를 쳤습니다. 이 줄은 상품 표에 없는 품번이라 거절당합니다. 그러면 앞의 세 줄은 어떻게 될까요?
BEGIN TRANSACTION과 COMMIT 사이는 전부 들어가거나 하나도 안 들어갑니다. 은행 이체에서 내 통장에서는 빠졌는데 상대 통장에는 안 들어가는 일이 없는 것과 같은 원리예요. 이걸 트랜잭션이라고 합니다. 단, 묶기만 해서는 부족해요. SQL Server는 기본값으로 오류 난 문장 하나만 건너뛰고 나머지를 확정해 버립니다. 그래서 스크립트 맨 위에 SET XACT_ABORT ON을 먼저 켭니다. '중간에 오류가 나면 묶음 전체를 되돌려라'는 스위치예요. 둘을 함께 써야 진짜로 전부 되거나 하나도 안 됩니다."아래 실습은 9장 스크립트 네 개를 이 페이지 안의 새봄DB에서 그대로 실행합니다. 단추를 차례로 눌러 보세요.
수요일 아침, 화요일에 받은 대한상사 주문이 약속대로 창고를 나갑니다. 머리 한 줄과 상세 세 줄이 한 덩어리로 들어가 있었으니, 최 반장의 출고 목록에도 세 품목이 빠짐없이 올라 있습니다.
목요일 아침, 김주연 과장에게서 다시 전화가 옵니다.
10월 29일 주문은 입고 지연으로 아직 창고를 떠나지 않았습니다. 출고 전이라 상태만 '취소'로 바꾸면 됩니다(09_4). 이때 조건에 상태 = N'접수'를 함께 걸어 두면, 이미 출고된 주문을 실수로 취소하는 일을 막을 수 있습니다. 6장의 최 반장 사건 같은 일을 줄이는 습관입니다. 출고된 주문은 DB 규칙으로 아예 못 바꾸게 막는 방법(트리거 등)도 있지만 이 가이드의 범위 밖입니다.
09_1_재고현황_보기.sql을 열고 F5.
품목 줄의 재고현황. 현재고, 안전재고, 상태 칸이 있습니다.
09_2_실사_조정.sql을 열고 F5.
결과 실사와_다른_품목 = 0.
09_3_새주문_트랜잭션.sql을 열고 F5. 한 번만 실행합니다.
메시지 탭에 FOREIGN KEY 오류가 하나 뜹니다(첫 시도). 결과 탭에는 남은_주문 = 0, 이어서 두 번째 시도로 들어간 3줄(금액 합계 원), 마지막으로 세 상품의 줄어든 재고가 나옵니다.
오류 1개는 일부러 낸 것이라 정상입니다. 결과 탭이 안 보이면 아래쪽 결과 탭을 직접 누르세요. 실수로 다시 실행하면 오류가 2개 나지만 데이터는 바뀌지 않습니다.
09_4_주문취소.sql을 열고 F5.
출고 전 주문 목록, 바뀐_줄수 = 1, 그리고 주문의 상태가 취소로.
한 번 더 실행하면 바뀐_줄수가 0입니다. 이미 취소된 주문은 '접수' 조건에 걸리지 않으니까요.
8장에서 만든 엑셀 파일을 열고 모두 새로 고침.
2026-11 줄에 방금 넣은 주문 금액이 들어옵니다.
11월 마지막 날, 정미경 차장은 서현의 자리에서 월말 보고서를 열고 새로 고침을 눌렀습니다. 예전에는 서현이 주문내역을 훑어 이상한 줄을 고친 다음에야 시작할 수 있던 일입니다. 저녁 무렵 전체 단톡방이 오랜만에 시끄럽습니다.
메시지를 보낸 사람들이 하나둘 서현의 자리로 모여듭니다. 도현도 화상으로 들어왔습니다.
10_다음걸음_맛보기.sql에 두 가지를 넣어 두었습니다.
BACKUP DATABASE 한 줄로 DB 전체를 파일 하나로 떠 둡니다. 엑셀의 "다른 이름으로 저장"과 달리, 누가 쓰는 중이어도 그 순간의 모습을 정확히 떠 줍니다. 실제 운영에서는 매일 밤 자동으로 돌게 예약합니다.지금까지는 서현의 PC 한 대에서 했습니다. 다른 직원의 PC에서도 보려면 몇 가지가 더 필요합니다. 엑셀의 서버 이름을 localhost 대신 서현 PC의 컴퓨터 이름으로 쓰고, SQL Server 구성 관리자에서 TCP/IP를 켜고, Windows 방화벽에서 1433 포트를 열고, 그 직원의 Windows 계정에 새봄DB를 볼 권한을 줍니다. 그리고 Developer 에디션은 학습·개발용이라 실제 업무에는 쓸 수 없습니다. 새봄생활은 다음 달 사무실 서버에 무료인 SQL Server Express를 깔고 새봄DB를 백업 파일로 옮기기로 했습니다.
이 가이드에서 다루지 않은 것도 있습니다. 사람마다 볼 수 있는 표를 나누는 권한, 주문 입력 화면을 따로 만드는 입력 프로그램(앱), 여러 사람이 동시에 같은 줄을 고칠 때의 잠금. 새봄생활은 일단 엑셀 창과 SSMS로 시작했고, 다음 분기에 주문 입력 화면을 만들기로 했습니다.
"매일 저녁 표를 훑던 시간이 없어졌다. 그 시간에 이번엔 거래처별 반품률을 뽑아 봐야지. 이제 질문만 쓰면 되니까."
그날 밤, 새봄생활 2층에는 늦게까지 불이 켜져 있었습니다. 처음으로 누구도 파일을 닫으라고 소리치지 않은 월말이었습니다.
10_다음걸음_맛보기.sql을 열고 F5.
메시지 탭에 "BACKUP DATABASE이(가) 450개의 페이지를 0.2초 동안 처리했습니다" 같은 줄이 뜨고(숫자는 PC마다 다름), 결과 탭에 백업 파일 경로와 크기가 나옵니다.
백업 파일은 SQL Server의 기본 백업 폴더(설치 위치 아래 MSSQL\Backup)에 생깁니다.
끝까지 해 봤다면 99_처음부터_다시.sql로 지우고 처음부터 한 번 더 해 봐도 좋습니다.
| 이럴 때 | 이유 | 이렇게 |
|---|---|---|
| 연결할 때 "신뢰되지 않은 기관에서 인증서 체인을 발급했습니다" | SSMS 22는 기본으로 암호화 연결을 요구하는데 내 PC 서버에는 정식 인증서가 없음 | 연결 창의 서버 인증서 신뢰(T)에 체크 |
| 연결 창에서 "서버를 찾을 수 없거나 액세스할 수 없습니다" | SQL Server 서비스가 꺼져 있거나 설치가 안 됨 | 시작 메뉴에서 "서비스"를 열어 SQL Server (MSSQLSERVER)가 "실행 중"인지 확인. 아니면 오른쪽 클릭 > 시작 |
| "개체 이름 '주문'이(가) 유효하지 않습니다." | 지금 쓰는 DB가 master | 위쪽 데이터베이스 선택 칸에서 새봄DB, 또는 맨 위에 USE 새봄DB; |
| 06_1에서 "운영 체제 오류 코드 3(지정된 경로를 찾을 수 없습니다.)" | 스크립트에 적힌 CSV 경로에 파일이 없음 | 실습 폴더를 C:\SaebomLab으로 옮기거나, 06_1의 경로 다섯 줄을 내 폴더로 고침 |
| 06_1에서 "운영 체제 오류 코드 5(액세스가 거부되었습니다.)" | SQL Server 서비스가 그 폴더를 읽을 권한이 없음(회사 PC의 다른 사용자 폴더, 네트워크 드라이브 등) | 실습 폴더를 C:\SaebomLab처럼 C 드라이브 바로 아래로 옮김 |
| 06_1에서 한글이 깨져 들어옴 | CSV를 "CSV UTF-8"이 아닌 형식으로 저장함 | 엑셀에서 CSV UTF-8(쉼표로 분리)로 다시 저장 |
| "데이터베이스 '새봄DB'이(가) 이미 있습니다. 다른 데이터베이스 이름을 선택하십시오." | 02번을 두 번 실행함 | 그대로 다음 장으로. 처음부터 하려면 99번 |
| 코드 아래 빨간 물결 밑줄 | SSMS 자동 완성이 아직 새 표·새 DB를 모름 | 실행에는 영향 없음. 개체 탐색기 새로 고침 뒤 편집(E) > IntelliSense > 로컬 캐시 새로 고침(Ctrl+Shift+R) |
| 09_3에서 FOREIGN KEY 오류 1개 | 품번을 일부러 틀리게 쓴 첫 시도 | 정상입니다. 같은 파일을 다시 실행하지 않습니다 |
| "...산술 오버플로 오류가 발생했습니다." | 합계가 INT 한도(약 21억)를 넘음 | SUM(CAST(수량 AS BIGINT) * 단가)처럼 큰 정수로 계산 |
SQL Server는 맥에 직접 설치되지 않습니다. 맥에서는 Docker Desktop에 SQL Server 리눅스 컨테이너를 띄우는 방법이 쓰입니다. 다만 애플 실리콘(M1 이후) 맥에서는 x64 에뮬레이션(Rosetta)으로 돌아가고, Microsoft는 이 방식을 테스트하거나 지원하지 않는다고 밝히고 있습니다. 또 SSMS는 윈도우 전용이라 맥에서는 VS Code의 MSSQL 확장 같은 다른 도구를 써야 합니다. 처음 배울 때는 윈도우 PC 한 대에서 하는 것을 권합니다. 이 페이지 안의 실습판(1·3·4·5·6·7·9장)은 맥 브라우저에서도 그대로 됩니다.
실습 zip의 3_SQL 폴더와 같은 파일입니다. 눌러서 펼치고 복사할 수 있습니다.
새봄생활과 등장인물, 거래처는 모두 가상입니다. 데이터는 시드를 고정한 생성기가 2년치 주문과 재고를 실제 업무처럼 흘려서 만들었고, 엑셀 파일은 그 위에 사람이 낼 법한 실수 아홉 가지를 덧씌웠습니다. 사업자번호는 국세청 검증식을 통과하는 형식이지만 실제 번호가 아닙니다. 이 가이드의 모든 SQL은 SQL Server 2025 Developer에서 처음부터 끝까지 실행해 결과를 대조했고, 페이지 안 실습판의 답도 같은 결과와 대조합니다.