MySQL08 Select문 활용하기
비트캠프 서초본원 엄진영 강사님의 수업을 듣고 정리했습니다.
1. distinct 와 all
all
- 모든 데이터를 가져올 때는 all을 붙인다.
select all 컬럼명 from 테이블명;
select all loc from room;
- all은 생략할 수 있다.
select loc from room;
distinct
- 중복 값을 한 개만 추출할 때 distinct 를 붙인다.
select distinct 컬럼명 from 테이블명;
select distinct loc from room;
- 컬럼이 2 개 이상일 때 그 컬럼들의 값이 모두 같은 경우에만 한 개로 간주한다.
select distinct loc, name from room;
2. order by
- 기본 인덱스 컬럼을 기준으로 정렬한다.
select rno, loc, name
from room;
(1) order by ~ asc
- 이름의 오름 차순으로 정렬하기 (ascendant)
select rno, loc, name
from room
order by name asc;
- asc는 생략 가능하다.
select rno, loc, name
from room
order by name;
- 위의 코드에서 select rno, loc만 하면?
- 실행 순서가 from → order by → select이므로 OK!
select rno, loc
from room
order by name;
(2) order by ~ desc
- 이름의 내림 차순으로 정렬하기 (descendant)
select rno, loc, name
from room
order by name desc;
- 이름은 오름 차순, 지점명도 오름 차순으로 정렬하기
- 이름으로 먼저 정렬하고, 그 안에서 지점명으로 정렬한다.
select rno, loc, name
from room
order by name asc, loc asc;
- 이름은 오름 차순, 지점명은 내림 차순으로 정렬하기
- 이름으로 먼저 정렬하고, 그 안에서 지점명으로 정렬한다.
select rno, loc, name
from room
order by name asc, loc desc;
- 지점명은 오름 차순으로, 이름은 오름 차순 정렬하기
- 지점명으로 먼저 정렬하고, 그 안에서 이름으로 정렬한다.
select rno, loc, name
from room
order by loc asc, name asc;
3. 라벨명
(1) 라벨명 붙이기
- as로 컬럼에 라벨명(별명)을 붙일 수 있다.
- 라벨명을 지정하지 않으면 컬럼명이 라벨명이 된다.
select rno as room_no, loc as location, name
from room;
- as는 생략 가능하다.
select rno room_no, loc location, name
from room;
- 라벨명에 공백을 넣고 싶으면 ‘ ‘ 안에 작성한다.
select rno 'room no', loc location, name
from room;
- 복잡한 형식으로 출력할 경우 라벨명(별명)을 부여한다.
/* 예) 강의실명(지점명) */
select concat(name, '(', loc, ')')
from room;
select concat(name, '(', loc, ')') title
from room;
- 컬럼에 저장된 데이터의 개수를 알 수 있다.
select count(*)
from room;
select count(*) cnt
from room;
select count(*) cnt
from room
where loc='서초';
4. union 과 union all
- select 결과 합치기
(1) union
- 중복 값 자동 제거 (union은 합집합을 의미)
select distinct bank from stnt
union
select distinct bank from tcher;
(2) union all
- 중복 값 제거 안함 (all 생략 불가)
select distinct bank from stnt
union all
select distinct bank from tcher;
5. join
- 서로 관련된 테이블의 데이터를 연결하여 추출하는 방법
-
기법
1) CROSS 조인
2) NATURAL 조인
3) JOIN ~ ON
4) OUTER JOIN
(1) CROSS JOIN
- 두 테이블의 데이터를 1:1로 모두 연결한다.
- cross join은 레코드의 상관 관계에 없이 연결되기 때문에 실무에서 거의 사용되지 않는다.
select 컬럼명, 컬럼명, 컬럼명
from 테이블명 cross join 테이블명;
- mno가 두 테이블에 있을 경우 어떤 테이블의 컬럼인지 지정하지 않으면 실행 오류!
- memb 테이블의 데이터와 stnt 테이블의 데이터를 cross join하여 레코드를 출력하라.
select mno, name, mno, work, bank
from memb cross join stnt;
/* ERROR 1052 (23000): Column 'mno' in field list is ambiguous */
- select 컬럼이 두 테이블 모두 있을 경우, 컬럼명 앞에 테이블명을 명시하여 구분하라!
select memb.mno, name, stnt.mno, work, bank
from memb cross join stnt;
- 예전 문법
select memb.mno, name, stnt.mno, work, bank
from memb, stnt;
- 컬럼명 앞에 테이블명을 붙이면 너무 길다.
- 테이블에 별명을 부여하고 그 별명을 사용하여 컬럼을 지정하라.
select 테이블별명.컬럼명, 컬럼명, 테이블별명.컬럼명, 컬럼명
from 테이블명 테이블별명 cross join 테이블명 테이블별명;
select m.mno, name, s.mno, work, bank
from memb m cross join stnt s;
- 예전 문법
select m.mno, name, s.mno, work, bank
from memb m, stnt s;
(2) NATURAL JOIN
- Natural Join : 같은 이름을 가진 컬럼 값을 기준으로 레코드를 연결한다.
select 테이블별명.컬럼명, 컬럼명, 테이블별명.컬럼명, 컬럼명
from 테이블명 테이블별명 natural join 테이블명 테이블별명;
- mno라는 이름을 가진 컬럼 값을 기준으로 레코드를 연결한다.
select m.mno, name, s.mno, work, bank
from memb m natural join stnt s;
- 예전 문법
select m.mno, name, s.mno, work, bank
from memb m, stnt s
where m.mno=s.mno;
natural join 의 문제점 I
1) 두 테이블의 조인 기준이 되는 컬럼 이름이 다를 때 연결되지 못한다.
2) 상관 없는 컬럼과 이름이 우연히 같을 때 잘못 연결된다.
3) 같은 이름의 컬럼이 여러 개 있을 경우 잘못 연결된다. 모든 컬럼의 값이 일치할 경우에만 연결되기 때문이다.
- 만약에 두 테이블에 같은 이름을 가진 컬럼이 여러 개 있다면, join ~ using (기준컬럼) 을 사용하여 두 테이블의 데이터를 연결할 때 기준이 될 컬럼을 지정한다.
select m.mno, name, s.mno, work, bank
from memb m join stnt s using (mno);
natural join 의 문제점 II
- 두 테이블에 같은 이름의 컬럼이 없을 경우 연결하지 못한다.
- 만약 두 테이블에 같은 이름을 가진 컬럼이 없으면, natural join을 수행하지 못한다. 또한 join using 으로도 해결할 수 없다. 이럴 경우 (inner) join ~ on 컬럼a=컬럼b 문법을 사용하여 각 테이블의 어떤 컬럼과 값을 비교할 것인지 지정하라!
select m.mno, name, s.mno, work, bank
from memb m inner join stnt s on m.mno=s.mno;
(3) INNER JOIN
- Iner Join : 각 테이블의 어떤 컬럼과 비교할 것인지 지정하여 레코드를 연결한다.
select 테이블별명A.컬럼명, 컬럼명, 테이블별명B.컬럼명, 컬럼명
from 테이블명 테이블별명A inner join 테이블명 테이블별명B on A.컬럼=B.컬럼 ;
- inner는 생략 가능하다.
select m.mno, name, s.mno, work, bank
from memb m join stnt s on m.mno=s.mno;
/* 즉 inner join 은 기준 컬럼의 값이 일치할 때만 데이터를 연결한다. */
- 예전 조인 문법 = inner join
select m.mno, name, s.mno, work, bank
from memb m, stnt s
where m.mno=s.mno;
[inner] join ~ on 의 문제점 I
- 반드시 on 에서 지정한 컬럼의 값이 같을 경우에만 두 테이블의 데이터가 연결된다.
-
같은 값을 갖는 데이터가 없다면 연결되지 않고, 결과로 출력되지 않는다.
- 전체 강의 목록
select lno, titl, rno, mno from lect;
- 전체 강의실 목록
select rno, loc, name from room;
- 강의 테이블에서 강의명을 가져오고, 강의실 테이블에서 지점명과 강의실명을 가져오자.
select
l.lno,
l.titl,
r.rno,
r.loc,
r.name
from lect l inner join room r on l.rno=r.rno;
- inner join의 경우, 강의실이 아직 지정되지 않은 강의의 경우 강의실 테이블의 데이터와 연결하지 못해 결과로 출력되지 않는 문제가 있다.
inner join의 문제점 예 II
- 모든 강의장 이름을 출력하라.
- 단 강의장에 강의가 배정된 경우 그 강의 이름도 출력하라.
select
r.rno,
r.name,
r.loc,
l.titl
from room r inner join lect l on r.rno = l.rno;
- 만약 기준 컬럼의 값과 일치하는 데이터가 없어서 다른 테이블의 데이터와 연결되지 않았다 하더라도 결과로 뽑아내고 싶다면 outer join을 사용하라!
- 즉 아직 강의실이 배정되지 않은 강의 데이터도 출력하고 싶을 때 출력하고 싶은 테이블을 바깥쪽 테이블로 지정하라!
(4) OUTER JOIN
right outer join ~ on
- 오른쪽(right) 테이블을 기준으로 왼쪽 데이터를 붙여서 볼 때 사용
select
l.lno,
l.titl,
r.rno,
r.loc,
r.name
from lect l right outer join room r on l.rno=r.rno;
/* room을 기준으로 lect 데이터를 연결한다.
* 만약 room과 일치하는 데이터가 lect에 없더라도 room 데이터를 출력한다!
*/
left outer join ~ on
- 왼쪽(left) 테이블을 기준으로 오른쪽의 데이터를 붙여서 볼때 사용
select
l.lno,
l.titl,
r.rno,
r.loc,
r.name
from lect l left outer join room r on l.rno=r.rno;
/* lect을 기준으로 room 데이터를 연결한다.
* 만약 lect와 일치하는 데이터가 room에 없더라도 lect 데이터를 출력한다!
*/
- 요구사항
- 모든 멤버의 번호와 이름을 출력하라!
- 단 학생의 경우 재직여부도 출력하라!
-- 1) 모든 멤버 데이터 출력하기
select mno, name
from memb;
-- 2) 학생 데이터를 가져와서 연결하기
select mno, name, work /* mno는 fk이면서도 pk */
from memb natural join stnt;
select mno, name, work
from memb join stnt using(mno);
select memb.mno, name, work
from memb, stnt
where memb.mno=stnt.mno;
select memb.mno, name, work
from memb inner join stnt on memb.mno=stnt.mno;
select memb.mno, name, work
from memb join stnt on memb.mno=stnt.mno; /* inner 생략됨 */
select m.mno, name, work
from memb m join stnt s on m.mno=s.mno;
/* 안타깝게도 위의 SQL문은 학생 목록만 출력한다.
왜?
memb테이블의 데이터와 stnt 테이블의 데이터를
연결할 때 mno가 같은 데이터만 연결하여 추출하기 때문이다.
해결책!
상대 테이블(stnt)에 연결할 대상(데이터)이 없더라도
select에서 추출하는 방법 */
select m.mno, name, work
from memb m left outer join stnt s on m.mno=s.mno;
(5) 여러 테이블 연결하기
-
여러 번의 joining을 통해 여러 테이블의 데이터를 연결할 수 있다.
-
다음의 결과가 출력될 수 있도록 수강 신청 데이터를 출력하시오.
수강신청번호, 강의명, 학생명, 재직여부, 수강신청일, 강의실명, 매니저명, 직위
/* 1단계: 수강신청 데이터를 출력 */
select la.lano, la.lno, la.mno, la.rdt
from lect_appl la;
/* 2단계: 수강신청한 학생의 번호 대신 이름을 출력 */
select la.lano, la.lno, m.name, la.rdt
from lect_appl la
inner join memb m on la.mno=m.mno;
/* lect_appl 테이블에 memb 테이블을 연결 */
/* 3단계: 수강 신청한 학생의 재직 여부 출력
* => inner join 에서 inner는 생략 가능
*/
select la.lano, la.lno, m.name, s.work, la.rdt
from lect_appl la
inner join memb m on la.mno=m.mno
inner join stnt s on la.mno=s.mno;
/* lect_appl 테이블에 memb, stnt 테이블을 연결 */
/* 4단계: 수상신청한 강의 번호 대신 강의명을 출력 */
select la.lano, l.titl, m.name, s.work, la.rdt, l.rno
from lect_appl la
inner join memb m on la.mno=m.mno
inner join stnt s on la.mno=s.mno
inner join lect l on la.lno=l.lno;
/* lect_appl 테이블에 memb, stnt, lect 테이블을 연결 */
/* 5단계: 강의실 이름을 출력한다.
* => 강의실 번호는 lect 테이블 데이터에 있다.
* => 강의실 이름은 room 테이블 데이터에 있다.
*/
select la.lano, l.titl, m.name, s.work, la.rdt, r.name, l.mno
from lect_appl la
inner join memb m on la.mno=m.mno
inner join stnt s on la.mno=s.mno
inner join lect l on la.lno=l.lno
left outer join room r on l.rno=r.rno;
/* lect 테이블을 기준으로 room 데이터를 연결 */
/* 6단계: 매니저 이름을 출력
* => 매니저 번호는 lect 테이블에 있다.
* => 매니저 이름은 memb 테이블에 있다.
*/
select
la.lano,
l.titl,
m.name member_name, /* 데이터를 보기 편하도록 별명 추가 */
s.work,
la.rdt,
r.name room_name,
m2.name manager_name
from lect_appl la
inner join memb m on la.mno=m.mno
inner join stnt s on la.mno=s.mno
inner join lect l on la.lno=l.lno
inner left outer join room r on l.rno=r.rno
left outer join memb m2 on l.mno=m2.mno;
/* lect 테이블을 기준으로 memb 데이터를 연결 */
/* 7단계: 매니저의 직위 출력
* => 매니저 번호는 lect 테이블 있다.
* => 매니저 직위는 mgr 테이블에 있다.
*/
select
la.lano,
l.titl,
m.name student_name,
s.work,
la.rdt,
r.name room_name,
m2.name manager_name,
mr.posi
from lect_appl la
inner join memb m on la.mno=m.mno
inner join stnt s on la.mno=s.mno
inner join lect l on la.lno=l.lno
inner left outer join room r on l.rno=r.rno
inner left outer join memb m2 on l.mno=m2.mno
left outer join mgr mr on l.mno=mr.mno;
/* lect 테이블을 기준으로 mgr 데이터를 연결 */
6. 서브 쿼리
- 쿼리문 안에 쿼리문을 실행하는 기법
- 직관적이기 때문에 사용하기가 편리하다.
- 하지만, 성능 문제를 생각하면서 사용해야 한다.
(1) select 절에 서브쿼리 사용하기
/* 1단계: 수강신청 데이터를 출력 */
select
la.lano,
la.lno,
la.mno,
date_format(la.rdt, '%Y-%m-%d') reg_dt
from lect_appl la;
/* 2단계 : 서브 쿼리를 이용하여 강의명을 가져오기
* => 단, 컬럼 자리에 사용할 때는 결과값이 한 개여야 한다.
* 결과 값이 여러 개가 리턴된다면 컬럼 값으로 사용할 수 없기 때문에 오류이다.
* 또한 컬럼 개수도 한 개여야 한다.
*/
select
la.lano,
select titl from lect where lno=la.lno as lect_title,
la.mno,
la.rdt
from lect_appl la;
/* 3단계 : 서브 쿼리를 이용하여 학생명을 가져오기 */
select
la.lano,
(select titl from lect where lno=la.lno) as lect_title,
(select name from memb where mno=la.mno) as stud_name,
la.rdt
from lect_appl la;
(2) from 절에 서브쿼리 사용하기
/* 0단계 : 강의 정보를 가져온다. */
select
l.lno,
l.titl,
l.rno,
l.mno
from lect l;
/* 1단계 : 강의 상세 정보를 가져오는 select를 준비한다.
* => 서브 쿼리를 이용하여 강의실 이름과 매니저 이름, 직위 정보를 가져오기 */
select
l.lno,
l.titl,
(select name from room where rno=l.rno) as room_name,
(select name from memb where mno=l.mno) as manager_name,
(select posi from mgr where mno=l.mno) as manager_posi
from lect l;
/* 2단계: 위에서 준비한 select 결과를 가상 테이블로 사용하여
기존의 lect_appl 테이블과 조인한다. */
select
la.lano,
/*(select titl from lect where lno=la.lno) as lect_title,*/
(select name from memb where mno=la.mno) as stud_name,
lec.titl,
lec.room_name,
lec.manager_name,
lec.manager_posi
from lect_appl la
join (select /* 가상 테이블 */
l.lno,
l.titl,
(select name from room where rno=l.rno) as room_name,
(select name from memb where mno=l.mno) as manager_name,
(select posi from mgr where mno=l.mno) as manager_posi
from lect l) as lec on la.lno=lec.lno;
- from 절에서 반복적으로 사용하는 서브쿼리가 있다면, 가상 테이블인 view로 정의해놓고 사용하는 것이 편하다.
/* from 절에서 반복적으로 사용하는 서브 쿼리를 가상 테이블인 view로 정의하기 */
create view lect2 as
select
l.lno,
l.titl,
(select name from room where rno=l.rno) as room_name,
l.mno as manager_no,
(select name from memb where mno=l.mno) as manager_name,
(select posi from mgr where mno=l.mno) as manager_posi
from lect l;
/* 위의 질의문을 view를 사용하여 다시 작성해보자! */
select
la.lano,
(select name from memb where mno=la.mno) as stud_name,
lec.titl,
lec.room_name,
lec.manager_name,
lec.manager_posi
from lect_appl la
join lect2 lec on la.lno=lec.lno;
(3) where 절에 서브쿼리 사용하기
- where ~ in ~
- where xxx in (‘aaa’, ‘bbb’) : xxx가 ‘aaa’이거나 ‘bbb’인 경우
/* 과장 또는 대리 매니저가 담당하고 있는 수강 신청만 추출하기 */
select
la.lano,
/* (select titl from lect where lno=la.lno) as lect_title, */
(select name from memb where mno=la.mno) as stud_name,
lec.titl,
lec.room_name,
/* lec.manager_no, */
lec.manager_name,
lec.manager_posi
from lect_appl la
join lect2 as lec on la.lno=lec.lno
where
lec.manager_no in (select mno from mgr where posi in ('과장', '주임'));