분석-설계-구현-시험-유지보수 단계
예시를 통해 알아보자
아이디 회원이름 주소 Thomas 토마스 경기 Edward 에드워드 서울 Henry 고든 인천
제품명 가격 제조일 제조사 남은 수량 냉장고 10 2019 삼성 17 컴퓨터 20 2020 롯데 22
user : root
password : 1234
입력하면 mysql 연결
만약 오류가 발생 시 [Database]-[Connect to Databse] 선택 후 stored connection에서 local instance mysql 선택 후 위의 아이디 비번 입력해서 접속
인스턴스 : mysql 프로그램이 컴퓨터에서 활성화돼있는 서비스
workbench 화면 구성
mysql에서는 스키마=데이터베이스
workbench [SCHEMAS] > [Create Schema] > 이름입력
개체이름은 영문 사용
SELECT 열이름 FROM 테이블이름 WHERE 조건;
SELECT * FROM memberTBL;
SELECT * FROM memberTBL WHERE memberName='토마스';
CREATE TABLE "my testTBL" (id INT);
DROP TABLE "my TestTBL";
인덱스, 뷰, 스토어드 프로시저, 트리거, 함수, 커서
데이터베이스 튜닝이란?
데이털베이스의 성능을 향상하거나 응답하는 시간을 단축시키는 것입니다. 튜닝 시 쿼리에 대한 응답을 줄이기위해 가장 집중적으로 보는 부분 중 하나가 인덱스.
CREATE TABLE indexTBL(first_name varchar(14), last_name varchar(16), hire_date date);
INSERT INTO indexTBL
SELECT first_name, last_name, hire_date
FROM employees.employees
LIMIT 500;
SELECT * FROM indexTBL;
SELECT * FROM indexTBL WHERE first_name='Mary';
- 전체 테이블 스캔 : 모든 행 데이터 읽어보는 것
- 대량 데이터가 들어있는데이터베이스에서 검색한다면 시간이 많이 걸리는 것은 물론 시스템에 과부하 초래
CREATE INDEX idx_indexTBL_firstname ON indexTBL(first_name);
SELECT * FROM indexTBL WHERE first_name='Mary';
Non-Unique key Lookup
= 인덱스를 사용했다는 의미🗣 아르바이트생을 고용해 회원의 정보 가운데 주소만 변경하는 일을 시키고 싶습니다!
회원이름과 주소만 있는 뷰 생성
CREATE VIEW uv_memberTBL
AS
SELECT memberName, memberAddress FROM memberTBL;
아르바이트 생은 뷰인지 테이블인지 알 필요도 없이 다른 테이블과 동일하게 사용가능=> 주소 변경 작업 맡길 수 있음
SELECT * FROM uv_memberTBL;
스토어드 프로시저 : mysql에서 제공하는 프로그래밍 기능, sql문을 하나로 묶어 편리하게 사용하는 기능
SELECT * FROM memberTBL WHERE memberName='토마스';
SELECT * FROM productTBL WHERE productName='냉장고';
DELIMITER//
CREATE PROCEDURE myProc()
BEGIN
SELECT * FROM memberTBL WHERE memberName='토마스';
SELECT * FROM productTBL WHERtNameE='냉장고';
END//
DELIMITER;
SQL문 뒤의 //(구문문자)는 기존의 세미콜론을 대신한다는 의미
INSERT INTO memberTBL VALUES('Soccer','흥민','서울시');
UDATE memberTBL SET memberAddress='서울시' WHERE memberName='흥민';
DELETE FROM memberTBL WHERE memberName='흥민';
위의 문제를 해결하기 위해
# SQL문 생성
CREATE TABLE deleteMemberTBL(
memberID char(8),
memberName char(5),
memberAddress char(20),
deleteDate date
);
# 트리거 생성
# 삭제된 행이 deletedMemberTBL 에 저장됨
DELIMITER //
CREATE TRIGGER trg_deleteMemberTBL
AFTER DELETE
ON memberTBL
FOR EACH ROW
BEGIN
INSERT INTO deletedMemberTBL
VALUES(OLD.memberID, OLD.memberName, OLD.memberAddress, CURDATE());
END //
DELIMITER;
조인(Join) 이란?
2개 이상의 테이블을 묶어서 하나의 결과 테이블을 만드는 것
🙎 조인을 공부해보기 이전에 일대다 테이블 구조를 살펴봅시다. 👨👩👧👦
Q. 데이터베이스가 데이터를 여러개의 테이블에 나누어 저장하는 이유?
A. 데이터의 중복저장,저장공간의 낭비 등을 피하고 데이터 무결성을 보장하기 위해서
나누어져 있는 테이블은 서로 관계를 맺고 있는데 [그림 8-1] 의 회원테이블과 구매테이블이 맺고 있는 '일대다' 관계이다. '일대다' 관계란, 한쪽 테이블에는 하나의 값만 존재하고 그 값과 대응하는 다른 쪽 테이블의 값은 여러개인 관계를 말한다.
일상생활에서의 일대다 관계
- 급여 : 기업의 직원테이블과 급여테이블
- 학점 : 학교의 학생테이블과 학점테이블
2.1.두 테이블의 내부 조인
1. 구매 테이블에서 KYM이라는 아이디를 가진 회원이 구매한 물건을 발송하기
USE cookDB;
SELECT *
FROM buyTBL
INNER JOIN userTBL
ON buyTBL.userID=userTBL.userID
WHERE buyTBL.userID='KYM';
2. 마지막 행인 WHERE buyTBL.userID='KYM';
을 생략하면 구매 테이블의 모든 행에 대해 동일한 방식으로 회원 테이블과 결합
USE cookDB;
SELECT *
FROM buyTBL
INNER JOIN userTBL
ON buyTBL.userID=userTBL.userID
3. 아이디,이름,구매물품,주소,연락처 등 필요한 열만 추출해보기
SELECT userID, userName, prodName, addr, CONCAT(mobile1, mobile2) AS '연락처'
FROM buyTML
INNER JOIN userTBL
ON buyTBML.userID=userTBL.userID;
❗️ 오류 발생
Error code: Column 'userid' in field list is ambiguous
💡 해결
열이름 userID가 불확실하다는 오류메세지는 userID는 두 테이블 모두 들어있는데 어느 테이블의 userID를 추출할지 명시하지 않아 오류가 발생한 것. 이럴 때는 어느 테이블의 userID를 추출할 건지 선택
# 구매테이블(buyTBL)을 기준으로 회원테이블(userTBL)을 조인하는것이므로 구매 테이블의 userID를 명시
SELECT buyTBL.userID, userName, prodName, addr, CONCAT(mobile1, mobile2) AS '연락처'
FROM buyTML
INNER JOIN userTBL
ON buyTBML.userID=userTBL.userID;
# 결과 : userID, userName, prodName, addr, mobile 등의 원하는 속성만 출력
4. 코드를 좀 더 명확하게 하기 위해 모두 '테이블이름.열이름' 형식으로 작성해보기
SELECT buyTBL.userID, userTBL.userName, buyTBL.prodName, userTBL.addr,
CONCAT(userTBL.mobile1, userTBL.mobile2) AS '연락처'
FROM buyTBL
INNER JOIN userTBL
ON buyTBL.userID=userTBL.userID;
하지만 코드가 길어져서 복잡해보인다. 별칭을 사용해서 보다 간단하게 작성해보자. 별칭은 FROM절에 나오는 테이블 이름 뒤에 별칭을 붙이면 된다.
SELECT B.userID, U.userName, B.prodName, U.addr,
CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM buyTBL B
INNER JOIN userTBL U
ON B.userID=U.userID;
KYM이라는 아이디를 가진 회원이 구매한 물건과 회원정보를 조인하되, 아이디,이름,물품,주소,연락처만 출력되게 하고 코드도 간결하게 작성해보자(구매 테이블 기준 조회)
SELECT B.userID, U.userName, B.prodName, U.addr, CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM buyTBL B
INNER JOIN userTBL U
ON B.userID=U.userID
WHERE B.userID='KYM';
userId | userName | prodName | addr | 연락처 |
---|---|---|---|---|
KYM | 김용만 | 모니터 | 서울 | 0104444444 |
🔼 결과 테이블은 동일 🔽
KYM이라는 아이디를 가진 회원이 구매한 물건과 회원정보를 조인하되, 아이디,이름,물품,주소,연락처만 출력되게 하고 코드도 간결하게 작성해보자(회원 테이블 기준 조회)
SELECT U.userID, U.userName, B.prodName, U.addr, CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM userTBL U
INNER JOIN buyTBL B
ON U.userID=B.userID
WHERE B.userID='KYM';
5. 전체 회원이 구매한 목록을 모두 출력해보기
SELECT U.userID, U.userName, B.prodName, U.addr, CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM userTBL U
INNER JOIN buyTBL B
ON U.userID=B.userID
# 결과를 보기 쉽게 회원 아이디순으로 정렬
ORDER BY U.userID;
문제가 없어보이지만 원래 의도한 '전체 회원'이 아니라 '구매한 기록이 있는 회원'을 출력한 것. 이처럼 구매한 회원의 기록뿐만 아니라 구매하지 않은 회원의 이름,주소 등도 출력하고자 할 때 사용하는 조인 방식은 외부조인이다.
🧐 외부조인 : 양쪽 테이블에 내용이 있는 것은 물론이고 한쪽 테이블에만 내용이 있어도 조인하는 방식
6. 내부조인이 유용하게 사용되는 예
쇼핑몰에서 한번이라도 구매한 기록이 있는 우수회원에게 감사의 안내문을 발송해보자
# 중복제거 키워드 DISTINCT를 통한 sql문
SELECT DISTINCT U.userID, U.userName, U.addr
FROM userTBL U
INNER JOIN buyTBL B
ON U.userID=B.userID
ORDER BY U.userID;
userId | userName | addr |
---|---|---|
KHD | 강호동 | 경북 |
KJD | 김제동 | 경남 |
KYM | 김용만 | 서울 |
LHJ | 이휘재 | 경기 |
PSH | 박수홍 | 서울 |
🔼 결과 테이블은 동일 🔽
# 서브쿼리 안의 조건유무를 검사하는 EXISTS 키워드를 통한 sql문
SELECT U.userID, U.userName, U.addr
FROM userTBL U
WHERE EXISTS(
SELECT *
FROM buyTBL B
WHERE U.userID=B.userID);
2.2.세 테이블의 내부 조인
학교 동아리를 생각해보자. 한 학생은 여러 개의 동아리에 가입해서 활동할 수 있고, 하나의 동아리에는 여러 학생이 가입할 수 있다. 따라서 학생 테이블과 동아리 테이블은 다대다 관계이다.
다대다 관계는 물리적으로 구성하려면 두 테이블 사이에 연결 테이블을 두고 연결 테이블과 두 테이블이 일대다 관계를 맺도록 구성해야한다.
SELECT S.stdName, S.addr, C.clubName, C.roomNo
FROM stdTBL S
INNER JOIN stdclubTBL SC
ON S.stdName=SC.stdName
INNER JOIN clubTBL C
ON SC.clubName=C.clubName
ORDER BY S.stdName;
학생테이블과 학생동아리 테이블을 조인 --> 학생동아리 테이블과 동아리 테이블을 조인
🧐 외부조인
SELECT <열 이름>
FROM <첫번째 테이블(LEFT 테이블)>
<LEFT | RIGHT> OUTER JOIN <두번째 테이블(RIGHT 테이블)>
ON <조인될 조건>
[WHERE 검색조건];
USE cookDB;
SELECT U.userID, U.userName, B.prodName, U.addr, CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM userTBL U
LEFT OUTER JOIN buyTBL B
ON U.userID=B.userID
ORDER BY U.userID;
🔼 결과 테이블은 동일 🔽
USE cookDB;
SELECT U.userID, U.userName, B.prodName, U.addr, CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM buyTBL B
RIGHT OUTER JOIN userTBL U
ON U.userID=B.userID
ORDER BY U.userID;
SELECT U.userID, U.userName, B.prodName, U.addr, CONCAT(U.mobile1, U.mobile2) AS '연락처'
FROM userTBL U
LEFT OUTER JOIN buyTBL B
ON U.userID=B.userID
WHERE B.prodName IS NULL
ORDER BY U.userID;
동아리에 가입하지 않은 학생도 출력되는 쿼리문=모든 학생 출력
USE cookDB;
SELECT S.stdName, S.addr, C.clubName, C.roomNo,
FROM stdTBL S
LEFT OUTER JOIN stdclubTBL SC
ON S.stdName=SC.stdName
LEFT OUTER JOIN clubTBL C
ON SC.clubName=C.clubName
ORDER BY S.stdName;
동아리를 기준으로 가입 학생을 출력하되, 가입 학생이 한 명도 없는 동아리도 출력하는 쿼리문=모든 동아리 출력
USE cookDB;
SELECT C.clubName, C.roomNo, S.stdName, S.addr
FROM stdTBL S
LEFT OUTER JOIN stdclubTBL SC
ON SC.stdName=S.stdName
RIGHT OUTER JOIN clubTBL C
ON SC.clubName=C.clubName
ORDER BY C.clubName;
UNION을 사용하면
USE cookDB;
SELECT S.stdName, S.addr, C.clubName, C.roomNo,
FROM stdTBL S
LEFT OUTER JOIN stdclubTBL SC
ON S.stdName=SC.stdName
LEFT OUTER JOIN clubTBL C
ON SC.clubName=C.clubName
UNION
SELECT C.clubName, C.roomNo, S.stdName, S.addr
FROM stdTBL S
LEFT OUTER JOIN stdclubTBL SC
ON SC.stdName=S.stdName
RIGHT OUTER JOIN clubTBL C
ON SC.clubName=C.clubName
동아리에 가입하지 않은 학생도 출력하고, 학생이 한 명도 없는 동아리도 출력하는 쿼리문 작성 가능
USE cookDB;
SELECT *
FROM buyTBL
CROSS JOIN userTBL;
두 테이블의 행수를 곱한 값
상호조인을 하는 다른 방법
USE cookDB; SELECT * FROM buyTBL, userTBL;
CROSS JOIN을 쓰지 않고 테이블을 나열해도 되지만, 호환성 문제로 이는 권장하지 않습니다.
1) 조직도 테이블 만들기
2) 조직도 테이블 데이터 입력
3) 자체 조인하기
# emp: 직원이름, manager: 직속상관이름, empTel: 연락처
SELECT A.emp AS '부하직원', B.emp AS '직속상관', B.empTel AS '직속상관연락처'
FROM empTBL A
INNER JOIN empTBL B
# 직원의 상관이름(A.manager)과 직원의 이름(B.emp)이 같다면
ON A.manager=B.emp
WHERE A.emp='우대리';
부하직원 | 직속상관 | 직속상관연락처 |
---|---|---|
우대리 | 이부장 | 2222-2 |
직속상관이름과 직속상관연락처를 알 수 있음
UNION : 두 쿼리의 결과를 행으로 합치는 연산자
예제를 통해 알아봅시다.
stdTBML
stdName | addr |
---|---|
강호동 | 경북 |
김제동 | 경남 |
김용만 | 서울 |
이휘재 | 경기 |
박수홍 | 서울 |
clubTBL
clubName | roomNo |
---|---|
수영 | 101호 |
바둑 | 102호 |
축구 | 103호 |
봉사 | 104호 |
USE cookDB;
SELECT stdName, addr FROM stdTBL
UNION
SELECT clubName, roomNO FROM clubTBL;
🔽
UNION한 결과
stdName | addr |
---|---|
강호동 | 경북 |
김제동 | 경남 |
김용만 | 서울 |
이휘재 | 경기 |
박수홍 | 서울 |
수영 | 101호 |
바둑 | 102호 |
축구 | 103호 |
봉사 | 104호 |
NOT IN : 첫번째 쿼리의 결과 중 두 번째 쿼리에 해당하는 것을 제외하고 출력하는 연산자
cookDB의 사용자를 모두 출력하되 전화번호가 없는 사람을 제외하는 예제
SELECT userName, CONCAT(mobile1, '-', mobile2) AS '전화번호' FROM userTBL
WHERE userName NOT IN (SELECT userName FROM userTBL WHERE mobile1 IS NULL);
IN : NOT IN과 반대로 첫번째 쿼리의 결과 중에서 두 번째 쿼리에 해당되는 것만 조회하는 연산자
cookDB의 사용자를 모두 출력하되 전화번호가 없는 사람만 출력하는 예제
SELECT userName, CONCAT(mobile1, '-', mobile2) AS '전화번호' FROM userTBL
WHERE userName IN (SELECT userName FROM userTBL WHERE mobile1 IS NULL);
실습 진행한 모습!
참고링크
조인을 연습하기 좋은 블로그
다했다..! 얏호