Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

[SQL] 예제 소스 정리 - ORDER BY, JOIN, 서브 쿼리, GROUP BY, HAVING 본문

네이버클라우드 AIaaS 개발자 양성과정 1기/DBMS, SQL, JDBC, Servlet

[SQL] 예제 소스 정리 - ORDER BY, JOIN, 서브 쿼리, GROUP BY, HAVING

끈기JK 2023. 2. 7. 18:31

eomcs-docs\sql\Exam05~10_1.sql

 

 

예제 소스 정리

 

 

eomcs-docs\sql\Exam05.sql

 

 

 

-- # select 테스트 용 테이블 준비

-- 수강생
DROP TABLE IF EXISTS stnt RESTRICT;

-- 강사
DROP TABLE IF EXISTS tcher RESTRICT;

-- 강의
DROP TABLE IF EXISTS lect RESTRICT;

-- 강의실
DROP TABLE IF EXISTS room RESTRICT;

-- 매니저
DROP TABLE IF EXISTS mgr RESTRICT;

-- 수강신청
DROP TABLE IF EXISTS lect_appl RESTRICT;

-- 강의실사진
DROP TABLE IF EXISTS room_phot RESTRICT;

-- 멤버
DROP TABLE IF EXISTS memb RESTRICT;

-- 주소
DROP TABLE IF EXISTS addr RESTRICT;

-- 강의배정
DROP TABLE IF EXISTS lect_tcher RESTRICT;

-- 수강생
CREATE TABLE stnt (
    mno    INTEGER     NOT NULL COMMENT '수강생번호', -- 수강생번호
    work   CHAR(1)     NOT NULL COMMENT '재직여부', -- 재직여부
    acc_no VARCHAR(20) NULL     COMMENT '통장번호', -- 통장번호
    bank   VARCHAR(50) NULL     COMMENT '은행명' -- 은행명
)
COMMENT '수강생';

-- 수강생
ALTER TABLE stnt
    ADD CONSTRAINT PK_stnt -- 수강생 기본키
        PRIMARY KEY (
            mno -- 수강생번호
        );

-- 수강생 유니크 인덱스
CREATE UNIQUE INDEX UIX_stnt
    ON stnt ( -- 수강생
        acc_no ASC, -- 통장번호
        bank ASC    -- 은행명
    );

-- 강사
CREATE TABLE tcher (
    mno    INTEGER     NOT NULL COMMENT '강사번호', -- 강사번호
    acc_no VARCHAR(20) NULL     COMMENT '통장번호', -- 통장번호
    bank   VARCHAR(50) NULL     COMMENT '은행명', -- 은행명
    hr_pay INTEGER     NULL     COMMENT '시강료' -- 시강료
)
COMMENT '강사';

-- 강사
ALTER TABLE tcher
    ADD CONSTRAINT PK_tcher -- 강사 기본키
        PRIMARY KEY (
            mno -- 강사번호
        );

-- 강사 유니크 인덱스
CREATE UNIQUE INDEX UIX_tcher
    ON tcher ( -- 강사
        acc_no ASC, -- 통장번호
        bank ASC    -- 은행명
    );

-- 강의
CREATE TABLE lect (
    lno     INTEGER      NOT NULL COMMENT '강의번호', -- 강의번호
    titl    VARCHAR(255) NOT NULL COMMENT '강의명', -- 강의명
    sdt     DATE         NOT NULL COMMENT '시작일', -- 시작일
    edt     DATE         NOT NULL COMMENT '종료일', -- 종료일
    qnty    INTEGER      NOT NULL COMMENT '최대수용인원', -- 최대수용인원
    pric    INTEGER      NOT NULL COMMENT '강의료', -- 강의료
    nat_sup CHAR(1)      NOT NULL COMMENT '정부지원여부', -- 정부지원여부
    sup_typ VARCHAR(50)  NOT NULL COMMENT '지원타입', -- 지원타입
    dsct    TEXT         NOT NULL COMMENT '설명', -- 설명
    rno     INTEGER      NULL     COMMENT '강의실번호', -- 강의실번호
    mno     INTEGER      NULL     COMMENT '매니저번호' -- 매니저번호
)
COMMENT '강의';

-- 강의
ALTER TABLE lect
    ADD CONSTRAINT PK_lect -- 강의 기본키
        PRIMARY KEY (
            lno -- 강의번호
        );

-- 강의 인덱스
CREATE INDEX IX_lect
    ON lect( -- 강의
        titl ASC -- 강의명
    );

ALTER TABLE lect
    MODIFY COLUMN lno INTEGER NOT NULL AUTO_INCREMENT COMMENT '강의번호';

-- 강의실
CREATE TABLE room (
    rno  INTEGER     NOT NULL COMMENT '강의실번호', -- 강의실번호
    loc  VARCHAR(50) NOT NULL COMMENT '지점명', -- 지점명
    name VARCHAR(50) NOT NULL COMMENT '강의실명', -- 강의실명
    qnty INTEGER     NOT NULL COMMENT '수용인원' -- 수용인원
)
COMMENT '강의실';

-- 강의실
ALTER TABLE room
    ADD CONSTRAINT PK_room -- 강의실 기본키
        PRIMARY KEY (
            rno -- 강의실번호
        );

-- 강의실 유니크 인덱스
CREATE UNIQUE INDEX UIX_room
    ON room ( -- 강의실
        loc ASC,  -- 지점명
        name ASC  -- 강의실명
    );

ALTER TABLE room
    MODIFY COLUMN rno INTEGER NOT NULL AUTO_INCREMENT COMMENT '강의실번호';

-- 매니저
CREATE TABLE mgr (
    mno  INTEGER     NOT NULL COMMENT '매니저번호', -- 매니저번호
    dept VARCHAR(50) NULL     COMMENT '부서', -- 부서
    posi VARCHAR(50) NULL     COMMENT '직위', -- 직위
    fax  VARCHAR(20) NULL     COMMENT '팩스' -- 팩스
)
COMMENT '매니저';

-- 매니저
ALTER TABLE mgr
    ADD CONSTRAINT PK_mgr -- 매니저 기본키
        PRIMARY KEY (
            mno -- 매니저번호
        );

-- 수강신청
CREATE TABLE lect_appl (
    lano INTEGER  NOT NULL COMMENT '수강신청번호', -- 수강신청번호
    lno  INTEGER  NOT NULL COMMENT '강의번호', -- 강의번호
    mno  INTEGER  NOT NULL COMMENT '수강생번호', -- 수강생번호
    rdt  DATETIME NOT NULL COMMENT '신청일', -- 신청일
    stat INTEGER  NULL     COMMENT '진행상태' -- 진행상태
)
COMMENT '수강신청';

-- 수강신청
ALTER TABLE lect_appl
    ADD CONSTRAINT PK_lect_appl -- 수강신청 기본키
        PRIMARY KEY (
            lano -- 수강신청번호
        );

-- 수강신청 유니크 인덱스
CREATE UNIQUE INDEX UIX_lect_appl
    ON lect_appl ( -- 수강신청
        lno ASC, -- 강의번호
        mno ASC  -- 수강생번호
    );

ALTER TABLE lect_appl
    MODIFY COLUMN lano INTEGER NOT NULL AUTO_INCREMENT COMMENT '수강신청번호';

-- 강의실사진
CREATE TABLE room_phot (
    rpno INTEGER      NOT NULL COMMENT '강의실사진번호', -- 강의실사진번호
    rno  INTEGER      NOT NULL COMMENT '강의실번호', -- 강의실번호
    phot VARCHAR(255) NOT NULL COMMENT '사진' -- 사진
)
COMMENT '강의실사진';

-- 강의실사진
ALTER TABLE room_phot
    ADD CONSTRAINT PK_room_phot -- 강의실사진 기본키
        PRIMARY KEY (
            rpno -- 강의실사진번호
        );

ALTER TABLE room_phot
    MODIFY COLUMN rpno INTEGER NOT NULL AUTO_INCREMENT COMMENT '강의실사진번호';

-- 멤버
CREATE TABLE memb (
    mno      INTEGER      NOT NULL COMMENT '멤버번호', -- 멤버번호
    name     VARCHAR(50)  NOT NULL COMMENT '이름', -- 이름
    tel      VARCHAR(20)  NOT NULL COMMENT '전화', -- 전화
    email    VARCHAR(40)  NOT NULL COMMENT '이메일', -- 이메일
    pwd      VARCHAR(100) NOT NULL COMMENT '암호', -- 암호
    phot     VARCHAR(255) NULL     COMMENT '사진', -- 사진
    ano      INTEGER      NULL     COMMENT '주소번호', -- 주소번호
    det_addr VARCHAR(255) NULL     COMMENT '상세주소', -- 상세주소
    finl_edu VARCHAR(50)  NULL     COMMENT '최종학력', -- 최종학력
    maj      VARCHAR(50)  NULL     COMMENT '전공', -- 전공
    sch_nm   VARCHAR(50)  NULL     COMMENT '최종학교' -- 최종학교
)
COMMENT '멤버';

-- 멤버
ALTER TABLE memb
    ADD CONSTRAINT PK_memb -- 멤버 기본키
        PRIMARY KEY (
            mno -- 멤버번호
        );

-- 멤버 유니크 인덱스
CREATE UNIQUE INDEX UIX_memb
    ON memb ( -- 멤버
        email ASC -- 이메일
    );

-- 멤버 인덱스
CREATE INDEX IX_memb
    ON memb( -- 멤버
        name ASC -- 이름
    );

ALTER TABLE memb
    MODIFY COLUMN mno INTEGER NOT NULL AUTO_INCREMENT COMMENT '멤버번호';

-- 주소
CREATE TABLE addr (
    ano      INTEGER      NOT NULL COMMENT '주소번호', -- 주소번호
    pst_no   VARCHAR(5)   NOT NULL COMMENT '우편번호', -- 우편번호
    bas_addr VARCHAR(255) NOT NULL COMMENT '기본주소' -- 기본주소
)
COMMENT '주소';

-- 주소
ALTER TABLE addr
    ADD CONSTRAINT PK_addr -- 주소 기본키
        PRIMARY KEY (
            ano -- 주소번호
        );

-- 주소 인덱스
CREATE INDEX IX_addr
    ON addr( -- 주소
        pst_no ASC -- 우편번호
    );

ALTER TABLE addr
    MODIFY COLUMN ano INTEGER NOT NULL AUTO_INCREMENT COMMENT '주소번호';

-- 강의배정
CREATE TABLE lect_tcher (
    lno INTEGER NOT NULL COMMENT '강의번호', -- 강의번호
    mno INTEGER NOT NULL COMMENT '강사번호' -- 강사번호
)
COMMENT '강의배정';

-- 강의배정
ALTER TABLE lect_tcher
    ADD CONSTRAINT PK_lect_tcher -- 강의배정 기본키
        PRIMARY KEY (
            lno, -- 강의번호
            mno  -- 강사번호
        );

-- 수강생
ALTER TABLE stnt
    ADD CONSTRAINT FK_memb_TO_stnt -- 멤버 -> 수강생
        FOREIGN KEY (
            mno -- 수강생번호
        )
        REFERENCES memb ( -- 멤버
            mno -- 멤버번호
        );

-- 강사
ALTER TABLE tcher
    ADD CONSTRAINT FK_memb_TO_tcher -- 멤버 -> 강사
        FOREIGN KEY (
            mno -- 강사번호
        )
        REFERENCES memb ( -- 멤버
            mno -- 멤버번호
        );

-- 강의
ALTER TABLE lect
    ADD CONSTRAINT FK_mgr_TO_lect -- 매니저 -> 강의
        FOREIGN KEY (
            mno -- 매니저번호
        )
        REFERENCES mgr ( -- 매니저
            mno -- 매니저번호
        );

-- 강의
ALTER TABLE lect
    ADD CONSTRAINT FK_room_TO_lect -- 강의실 -> 강의
        FOREIGN KEY (
            rno -- 강의실번호
        )
        REFERENCES room ( -- 강의실
            rno -- 강의실번호
        );

-- 매니저
ALTER TABLE mgr
    ADD CONSTRAINT FK_memb_TO_mgr -- 멤버 -> 매니저
        FOREIGN KEY (
            mno -- 매니저번호
        )
        REFERENCES memb ( -- 멤버
            mno -- 멤버번호
        );

-- 수강신청
ALTER TABLE lect_appl
    ADD CONSTRAINT FK_stnt_TO_lect_appl -- 수강생 -> 수강신청
        FOREIGN KEY (
            mno -- 수강생번호
        )
        REFERENCES stnt ( -- 수강생
            mno -- 수강생번호
        );

-- 수강신청
ALTER TABLE lect_appl
    ADD CONSTRAINT FK_lect_TO_lect_appl -- 강의 -> 수강신청
        FOREIGN KEY (
            lno -- 강의번호
        )
        REFERENCES lect ( -- 강의
            lno -- 강의번호
        );

-- 강의실사진
ALTER TABLE room_phot
    ADD CONSTRAINT FK_room_TO_room_phot -- 강의실 -> 강의실사진
        FOREIGN KEY (
            rno -- 강의실번호
        )
        REFERENCES room ( -- 강의실
            rno -- 강의실번호
        );

-- 멤버
ALTER TABLE memb
    ADD CONSTRAINT FK_addr_TO_memb -- 주소 -> 멤버
        FOREIGN KEY (
            ano -- 주소번호
        )
        REFERENCES addr ( -- 주소
            ano -- 주소번호
        );

-- 강의배정
ALTER TABLE lect_tcher
    ADD CONSTRAINT FK_tcher_TO_lect_tcher -- 강사 -> 강의배정
        FOREIGN KEY (
            mno -- 강사번호
        )
        REFERENCES tcher ( -- 강사
            mno -- 강사번호
        );

-- 강의배정
ALTER TABLE lect_tcher
    ADD CONSTRAINT FK_lect_TO_lect_tcher -- 강의 -> 강의배정
        FOREIGN KEY (
            lno -- 강의번호
        )
        REFERENCES lect ( -- 강의
            lno -- 강의번호
        );

 

 

 

eomcs-docs\sql\Exam06.sql

 

 

-- # select 테스트 용 데이터 준비

/* 강의실 데이터 준비 */
insert into room(rno, loc, name, qnty) values(1, '강남', '501', 30);
insert into room(rno, loc, name, qnty) values(2, '강남', '502', 30);
insert into room(rno, loc, name, qnty) values(3, '강남', '503', 30);
insert into room(rno, loc, name, qnty) values(4, '종로', '301', 30);
insert into room(rno, loc, name, qnty) values(5, '종로', '302', 30);
insert into room(rno, loc, name, qnty) values(6, '종로', '303', 30);
insert into room(rno, loc, name, qnty) values(7, '서초', '301', 30);
insert into room(rno, loc, name, qnty) values(8, '서초', '302', 30);
insert into room(rno, loc, name, qnty) values(9, '서초', '501', 30);
insert into room(rno, loc, name, qnty) values(10, '서초', '601', 30);

/* 학생 데이터 입력 */
insert into memb(mno, name, tel, email, pwd)
values(100, 's100', '111-1111', 's100@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(101, 's101', '111-1111', 's101@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(102, 's102', '111-1111', 's102@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(103, 's103', '111-1111', 's103@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(104, 's104', '111-1111', 's104@', sha2('1111', 224));

insert into stnt(mno, work, acc_no, bank)
values(100, 'N', '1000', '비트은행');
insert into stnt(mno, work, acc_no, bank)
values(101, 'Y', '1001', '비트은행');
insert into stnt(mno, work, acc_no, bank)
values(102, 'N', '1002', '캠프은행');
insert into stnt(mno, work, acc_no, bank)
values(103, 'Y', '1003', '우리은행');
insert into stnt(mno, work, acc_no, bank)
values(104, 'N', '1004', '국민은행');

/* 강사 데이터 입력 */
insert into memb(mno, name, tel, email, pwd)
values(200, 't200', '111-1111', 't200@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(201, 't201', '111-1111', 't201@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(202, 't202', '111-1111', 't202@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(203, 't203', '111-1111', 't203@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(204, 't204', '111-1111', 't204@', sha2('1111', 224));

insert into tcher(mno, hr_pay, acc_no, bank)
values(200, 10000, '2000', '신한은행');
insert into tcher(mno, hr_pay, acc_no, bank)
values(201, 20000, '2001', '농협');
insert into tcher(mno, hr_pay, acc_no, bank)
values(202, 15000, '2002', '기업은행');
insert into tcher(mno, hr_pay, acc_no, bank)
values(203, 25000, '2003', '우리은행');
insert into tcher(mno, hr_pay, acc_no, bank)
values(204, 30000, '2004', '국민은행');

/* 매니저 데이터 입력 */
insert into memb(mno, name, tel, email, pwd)
values(300, 'm300', '111-1111', 'm300@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(301, 'm301', '111-1111', 'm301@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(302, 'm302', '111-1111', 'm302@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(303, 'm303', '111-1111', 'm303@', sha2('1111', 224));
insert into memb(mno, name, tel, email, pwd)
values(304, 'm304', '111-1111', 'm304@', sha2('1111', 224));

insert into mgr(mno, dept, posi)
values(300, '시설관리', '주임');
insert into mgr(mno, dept, posi)
values(301, '시설관리', '대리');
insert into mgr(mno, dept, posi)
values(302, '회계', '과장');
insert into mgr(mno, dept, posi)
values(303, '교육', '주임');
insert into mgr(mno, dept, posi)
values(304, '교육', '과장');

/* 200번 강사는 교육팀 과장이기도 하다.*/
insert into mgr(mno, dept, posi)
values(200, '교육', '과장');

/* 강의 데이터 입력 */
insert into lect(lno, titl, sdt, edt, qnty, pric, nat_sup, sup_typ, dsct)
values(1, '자바프로그래밍', '2018-1-1', '2018-5-30', 30, 100, 'Y', '미취업자', 'ok');
insert into lect(lno, titl, sdt, edt, qnty, pric, nat_sup, sup_typ, dsct)
values(2, 'IoT프로그래밍', '2018-3-1', '2018-7-30', 30, 200, 'Y', '미취업자', 'ok');
insert into lect(lno, titl, sdt, edt, qnty, pric, nat_sup, sup_typ, dsct)
values(3, '윈도우프로그래밍', '2018-5-1', '2018-10-30', 30, 300, 'Y', '노동부', 'ok');


/* 강의에 매니저 배정 */ 
update lect set mno=303 where lno=1;
update lect set mno=304 where lno=3;

/* 강의에 강의실 배정 */
update lect set rno=1 where lno=1;
update lect set rno=4 where lno=2;

/* 수강신청 데이터 입력 */
insert into lect_appl(lano, lno, mno, rdt) values(1, 1, 100, '2017-11-2');
insert into lect_appl(lano, lno, mno, rdt) values(2, 1, 101, '2017-11-3');
insert into lect_appl(lano, lno, mno, rdt) values(3, 1, 102, '2017-11-4');
insert into lect_appl(lano, lno, mno, rdt) values(4, 2, 104, '2017-12-6');
insert into lect_appl(lano, lno, mno, rdt) values(5, 2, 100, '2017-12-7');
insert into lect_appl(lano, lno, mno, rdt) values(6, 3, 101, '2017-10-8');
insert into lect_appl(lano, lno, mno, rdt) values(7, 3, 102, '2017-11-9');
insert into lect_appl(lano, lno, mno, rdt) values(8, 3, 104, '2017-11-11');

 

 

 

eomcs-docs\sql\Exam07_1.sql

 

 

distinct 와 all

 

/* 모든 데이터를 가져온다.*/
select all loc from room;

/* all은 생략할 수 있다*/
select loc from room;

/* 중복 값을 한 개만 추출할 때 distinct 를 붙인다.*/
select distinct loc from room;  

/* 컬럼이 2 개 이상일 때 
    그 컬럼들의 값이 중복될 경우만 한 개로 간주한다.*/
select distinct loc, name from room;

 

 

 

eomcs-docs\sql\Exam07_2.sql

 

 

order by

 

/* 기본 인덱스 컬럼을 기준으로 정렬한다.*/
select rno, loc, name
from room;

/* 이름의 오름 차순(ascending)으로 정렬하기 */
select rno, loc, name
from room
order by name asc;

/* asc는 생략 가능하다. */
select rno, loc, name
from room
order by name;

/* 이름의 내림 차순(desceding)으로 정렬하기 */
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;

select rno, name
from room
order by loc asc, name asc;

-- 실행 순서: from -> where -> select -> order by
select 
  concat(name,'-',loc) as class_name
from 
  room
where 
  loc <> '강남'
order by 
  class_name; -- select 절에 있는 컬럼 또는 테이블 컬럼

select 
  concat(name,'-',loc) as class_name
from 
  room
where 
  loc <> '강남'
order by 
  loc desc; -- select 절에 있는 컬럼 또는 테이블 컬럼

 

 

 

eomcs-docs\sql\Exam07_3.sql

 

 

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='서초';

/* count()를 호출할 때 컬럼 이름을 지정하면 
   해당 컬럼의 값이 null 이 아닌 데이터만 카운트한다. */
select count(mno) cnt
from lect;

 

 

 

eomcs-docs\sql\Exam07_4.sql

 

 

union 과 union all

 

/* select 결과 합치기
   union : 중복 값 자동 제거*/
select distinct bank from stnt
union
select distinct bank from tcher;

/* union all: 중복 값 제거 안함*/
select distinct bank from stnt
union all
select distinct bank from tcher;

/* 차집합
   mysql 은 차집합 문법을 지원하지 않는다.
   따라서 다음과 기존의 SQL 문법을 사용해서 처리해야 한다.
*/
select distinct bank
from stnt
where not bank in (select distinct bank from tcher);

/* 교집합
   mysql 은 교집합 문법을 지원하지 않는다.
   따라서 다음과 기존의 SQL 문법을 사용해서 처리해야 한다.
*/
select distinct bank
from stnt
where bank in (select distinct bank from tcher);

 

 

 

eomcs-docs\sql\Exam08_1.sql

 

 

조인

 

=> 서로 관련된 테이블의 데이터를 연결하여 추출하는 방법
=> 기법
1) CROSS 조인(=Cartesian product)
2) NATURAL 조인
3) JOIN ~ ON
4) OUTER JOIN

 

 

1) CROSS 조인(=Cartesian product)

두 테이블의 데이터를 1:1로 모두 연결한다.

create table board1 (
  bno int primary key auto_increment,
  title varchar(255) not null,
  content text
);

create table attach_file1 (
  fno int primary key auto_increment,
  filepath varchar(255) not null,
  bno int not null
);

alter table attach_file1
  add constraint attach_file1_fk foreign key (bno) references board1 (bno);

insert into board1 values(1, '제목1', '내용');
insert into board1 values(2, '제목2', '내용');
insert into board1 values(3, '제목3', '내용');

insert into attach_file1 values(101, 'a1.gif', 1);
insert into attach_file1 values(102, 'a2.gif', 1);
insert into attach_file1 values(103, 'c1.gif', 3);

select bno, title from board1;
select fno, filepath, bno from attach_file1;

-- bno 컬럼이 두 테이블에 모두 존재한다.
-- 따라서 어떤 테이블의 컬럼인지 지정하지 않으면 실행 오류!
select
  bno, title, content, fno, filepath
from board1 cross join attach_file1;

-- select 컬럼이 두 테이블에 모두 있을 경우,
-- 컬럼명 앞에 테이블명을 명시하여 구분하라!
select board1.bno, title, content, fno, filepath, attach_file1.bno
from board1 cross join attach_file1;

-- cross join 고전 문법
select board1.bno, title, content, fno, filepath, attach_file1.bno
from board1, attach_file1;

-- 컬럼명 앞에 테이블명을 붙이면 너무 길다.
-- 테이블에 별명을 부여하고 그 별명을 사용하여 컬럼을 지정하라.
select b.bno, title, content, fno, filepath, a.bno
from board1 as b cross join attach_file1 as a;

-- as는 생략 가능
select b.bno, title, content, fno, filepath, a.bno
from board1 b cross join attach_file1 a;

-- 고전 문법
select b.bno, title, content, fno, filepath, a.bno
from board1 b, attach_file1 a;

 

 

 

eomcs-docs\sql\Exam08_2.sql

 

 

2) NATURAL 조인

같은 이름을 가진 컬럼 값을 기준으로 레코드를 연결한다.

select b.bno, title, content, fno, filepath, a.bno
from board1 b natural join attach_file1 a;

-- 고전 문법
select b.bno, title, content, fno, filepath, a.bno
from board1 b, attach_file1 a
where b.bno = a.bno;

/* natural join 의 문제점
 * 가. 두 테이블의 조인 기준이 되는 컬럼 이름이 다를 때 연결되지 못한다.
   나. 상관 없는 컬럼과 이름이 같을 때 잘못 연결된다.
   다. 같은 이름의 컬럼이 여러 개 있을 경우 잘못 연결된다.
        모든 컬럼의 값이 일치할 경우에만 연결되기 때문이다. */


-- 가. 두 테이블의 조인 기준이 되는 컬럼 이름이 다를 때:
--
create table board2 (
  no int primary key auto_increment,
  title varchar(255) not null,
  content text
);

create table attach_file2 (
  fno int primary key auto_increment,
  filepath varchar(255) not null,
  bno int not null
);

alter table attach_file2
  add constraint attach_file2_fk foreign key (bno) references board2 (no);

insert into board2 values(1, '제목1', '내용');
insert into board2 values(2, '제목2', '내용');
insert into board2 values(3, '제목3', '내용');

insert into attach_file2 values(101, 'a1.gif', 1);
insert into attach_file2 values(102, 'a2.gif', 1);
insert into attach_file2 values(103, 'c1.gif', 3);

-- natural join의 기준이 되는 같은 이름을 가진 컬럼이 양 테이블에 존재하지 않는다.
-- => cross join 처럼 실행된다.
select no, title, content, fno, filepath, bno
from board2 b natural join attach_file2 a;

-- 고전 문법 : 
-- 고전 문법에서는 where 절의 조건으로 두 테이블의 조인 기준이 되는 컬럼 값을 검사하기 때문에
-- 실행 결과는 정상적으로 나온다.
select no, title, content, fno, filepath, bno
from board2 b, attach_file2 a
where b.no = a.bno;


-- 나. 같은 이름을 가진 컬럼이 있지만 서로 상관(PK와 FK 관계)이 없는 컬럼일 때:
--
create table board3 (
  no int primary key auto_increment,
  title varchar(255) not null,
  content text
);

create table attach_file3 (
  no int primary key auto_increment,
  filepath varchar(255) not null,
  bno int not null
);

alter table attach_file3
  add constraint attach_file3_fk foreign key (bno) references board3 (no);

insert into board3 values(1, '제목1', '내용');
insert into board3 values(2, '제목2', '내용');
insert into board3 values(3, '제목3', '내용');

insert into attach_file3 values(1, 'a1.gif', 1);
insert into attach_file3 values(2, 'a2.gif', 1);
insert into attach_file3 values(3, 'c1.gif', 3);

-- board3의 no와 attach_file3의 no는 PK/FK 관계가 아니다.
-- 그럼에도 불구하고 이름이 같기 때문에 이 컬럼을 기준으로 데이터를 연결한다.
select b.no, title, content, a.no, filepath, bno
from board3 b natural join attach_file3 a;

-- 고전 문법 : 
-- 고전 문법에서는 where 절의 조건으로 두 테이블의 조인 기준이 되는 컬럼 값을 검사하기 때문에
-- 실행 결과는 정상적으로 나온다.
select b.no, title, content, a.no, filepath, bno
from board3 b, attach_file3 a
where b.no = a.bno;


-- 다. 같은 이름을 가진 컬럼이 여러개 있을 때:
--
create table board4 (
  bno int primary key auto_increment,
  title varchar(255) not null,
  content text
);

create table attach_file4 (
  fno int primary key auto_increment,
  title varchar(255) not null,
  bno int not null
);

alter table attach_file4
  add constraint attach_file4_fk foreign key (bno) references board4 (bno);

insert into board4 values(1, '제목1', '내용');
insert into board4 values(2, '제목2', '내용');
insert into board4 values(3, '제목3', '내용');

insert into attach_file4 values(1, 'a1.gif', 1);
insert into attach_file4 values(2, 'a2.gif', 1);
insert into attach_file4 values(3, 'c1.gif', 3);

-- board4와 attach_file4에 같은 이름을 가진 컬럼이 여러개 있다.
-- 해당 컬럼들의 값이 같을 때만 두 테이블의 데이터를 연결한다.
-- 따라서 실행 결과 데이터는 없을 것이다.
select b.bno, b.title, content, a.fno, a.title, a.bno
from board4 b natural join attach_file4 a;

-- 고전 문법 : 
-- 고전 문법에서는 where 절의 조건으로 두 테이블의 조인 기준이 되는 컬럼 값을 검사하기 때문에
-- 실행 결과는 정상적으로 나온다.
select b.bno, b.title, content, a.fno, a.title, a.bno
from board4 b, attach_file4 a
where b.bno = a.bno;

 

 

 

eomcs-docs\sql\Exam08_3.sql

 

 

3) JOIN ~ USING

같은 이름을 가진 컬럼이 여러개 있을 경우 USING을 사용하여 컬럼을 명시할 수 있다.

select b.bno, b.title, content, a.fno, a.title, a.bno
from board4 b join attach_file4 a using (bno);

-- join ~ using 의 한계
-- => 두 테이블에 같은 이름의 컬럼이 없을 경우 연결하지 못한다.

create table board5 (
  no int primary key auto_increment,
  title varchar(255) not null,
  content text
);

create table attach_file5 (
  fno int primary key auto_increment,
  filepath varchar(255) not null,
  bno int not null
);

alter table attach_file5
  add constraint attach_file5_fk foreign key (bno) references board5 (no);

insert into board5 values(1, '제목1', '내용');
insert into board5 values(2, '제목2', '내용');
insert into board5 values(3, '제목3', '내용');

insert into attach_file5 values(1, 'a1.gif', 1);
insert into attach_file5 values(2, 'a2.gif', 1);
insert into attach_file5 values(3, 'c1.gif', 3);

-- 두 테이블의 데이터를 연결할 때 기준이 되는 컬럼의 이름이 같지 않다.
-- 이런 경우 using을 사용할 수 없다.
select no, title, content, fno, filepath, bno
from board5 b join attach_file5 a using (bno);

 

 

 

eomcs-docs\sql\Exam08_4.sql

 

 

4) JOIN ~ ON

조인 조건을 on에 명시할 수 있다.

select no, title, content, fno, filepath, bno
from board5 b join attach_file5 a on b.no = a.bno;

-- 조건에 일치하는 경우에만 두 테이블의 데이터를 연결한다.
-- 이런 조인을 'inner join' 이라 부른다.
-- SQL 문에서도 inner join 이라 기술할 수 있다.
-- 물론 inner를 생략할 수도 있다.
select no, title, content, fno, filepath, bno
from board5 b inner join attach_file5 a on b.no = a.bno;

/* [inner] join ~ on 의 문제점
   => 반드시 on 에서 지정한 컬럼의 값이 같을 경우에만
        두 테이블의 데이터가 연결된다.
   => 같은 값을 갖는 데이터가 없다면 연결되지 않고, 결과로 출력되지 않는다.
   => 위 SQL의 실행 결과를 보라!
      첨부파일이 없는 2번 게시글은 결과에 포함되지 않는다.
*/

 

 

5) OUTER JOIN

조인 조건에 일치하는 데이터가 없더라도 두 테이블 중에서 한 테이블의 데이터를
결과로 포함시키는 명령이다.

--    문법:
--         select 컬럼명, 컬럼명, ...
--         from 테이블1 t1 [left/right] outer join 테이블2 t2 on t1.컬럼=t2.컬럼
--    left outer join => 왼쪽 테이블의 데이터는 반드시 포함시키라는 뜻이다.
--    right outer join => 오른쪽 테이블의 데이터를 반드시 포함시키라는 뜻이다.
--
select no, title, content, fno, filepath, bno
from board5 b left outer join attach_file5 a on b.no = a.bno
order by no desc;

-- 실무
-- 1) 여러 테이블을 조인하여 컬럼을 projection 할 때
--    각 컬럼이 어떤 테이블의 컬럼인지 명시한다.
-- 2) 컬럼을 나열할 때 한 줄에 한 컬럼을 나열한다.
select
  b.no,
  b.title,
  b.content,
  a.fno,
  a.filepath,
  a.bno
from board5 b
  left outer join attach_file5 a on b.no = a.bno
order by
  no desc;



-- [inner join의 문제점 예1]

-- 1) 전체 강의 목록
select lno, titl, rno, mno from lect;

-- 2) 전체 강의실 목록
select rno, loc, name from room;

-- 3) 강의 정보를 출력할 때 센터 이름과 강의실 이름도 함께 출력해보자
--    강의 테이블(lect)에서 강의명을 가져오고,
--    강의실 테이블(room)에서 지점명과 강의실명을 가져오자.
select
    l.lno,
    l.titl,
    l.rno,
    r.rno,
    r.loc,
    r.name
from lect l 
    inner join room r on l.rno=r.rno;
/* inner join의 문제는 위의 경우처럼
   강의실이 아직 지정되지 않은 강의의 경우 강의실 테이블의 데이터와 연결하지 못해
   결과로 출력되지 않는 문제가 있다. */


-- [inner join의 문제점 예2]
-- 1) 모든 강의장 이름을 출력하라.
--    단 강의장에 강의가 배정된 경우 그 강의 이름도 출력하라.
--
select
  r.rno,
  r.name,
  r.loc,
  l.titl
from room r 
    inner join lect l on r.rno = l.rno;

-- 위의 경우 처럼 만약 기준 컬럼의 값과 일치하는 데이터가 없어서
-- 다른 테이블의 데이터와 연결되지 않았다 하더라도
-- 결과로 뽑아내고 싶다면 outer join을 사용하라!
-- 즉 아직 강의실이 배정되지 않은 강의 데이터도 출력하고 싶을 때
-- 출력하고 싶은 테이블을 바깥쪽 테이블로 지정하라!
--
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 데이터를 출력한다!

select
    l.lno,
    l.titl,
    r.rno,
    r.loc,
    r.name
from lect l 
    right outer join room r on l.rno=r.rno;
-- 오른쪽 테이블인 lect를 기준으로 lect 데이터를 연결한다.
-- 만약 room과 일치하는 데이터가 lect에 없더라도
-- room 데이터를 출력한다!

 

 

 

eomcs-docs\sql\Exam08_5.sql

 

 

조인 연습

요구사항:
   모든 멤버의 번호와 이름을 출력하라!
   단 학생의 경우 재직여부도 출력하라!

-- 1) 모든 멤버 데이터 출력하기
select mno, name
from memb;

-- 2) 학생 데이터를 가져와서 연결하기
select mno, name, work
from memb natural join stnt;

-- 3) join ~ using으로 연결하기
select mno, name, work
from memb join stnt using(mno);

-- 4) 고전 문법으로 연결하기
select memb.mno, name, work
from memb, stnt
where memb.mno=stnt.mno;

-- 5) inner join ~ on 연결하기
select memb.mno, name, work
from memb inner join stnt on memb.mno=stnt.mno;

-- 6) inner 생략하기
select memb.mno, name, work
from memb join stnt on memb.mno=stnt.mno;

-- 7) 테이블에 별명 부여하기
select m.mno, name, work
from memb m join stnt s on m.mno=s.mno;

/*
안타깝게도 위의 SQL문은 학생 목록만 출력한다.
왜? 
- memb테이블의 데이터와 stnt 테이블의 데이터를
  연결할 때 mno가 같은 데이터만 연결하여 추출하기 때문이다.
해결책!
- 상대 테이블(stnt)에 연결할 대상(데이터)이 없더라도
  select에서 추출하는 방법 
*/
-- 8) outer join ~ on 으로 연결하기
select m.mno, name, work
from memb m left outer join stnt s on m.mno=s.mno;

 

 

 

eomcs-docs\sql\Exam08_6.sql

 

 

=> 다음의 결과가 출력될 수 있도록 수강 신청 데이터를 출력하시오!
     수강신청번호, 강의명, 학생명, 재직여부, 수강신청일, 강의실명, 매니저명, 직위

/* 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;

/* 3단계: 수강 신청한 학생의 재직 여부 출력
 * => inner join 에서 inner는 생략 가능
 */
select la.lano, la.lno, m.name, s.work, la.rdt
from lect_appl la
        join memb m on la.mno=m.mno
        join stnt s on la.mno=s.mno;

/* 4단계: 수강신청한 강의 번호 대신 강의명을 출력 */
select la.lano, l.titl, m.name, s.work, la.rdt, l.rno
from lect_appl la
        join memb m on la.mno=m.mno
        join stnt s on la.mno=s.mno
        join lect l on la.lno=l.lno;

/* 5단계: 강의실 이름을 출력한다.
 * => 강의실 번호는 lect 테이블 데이터에 있다.
 * => 강의실 이름은 room 테이블 데이터에 있다.
 */
select la.lano, l.titl, m.name, s.work, la.rdt, r.name, l.mno
from lect_appl la
        join memb m on la.mno=m.mno
        join stnt s on la.mno=s.mno
        join lect l on la.lno=l.lno
        left outer join room r on l.rno=r.rno;

/* 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
        join memb m on la.mno=m.mno
        join stnt s on la.mno=s.mno
        join lect l on la.lno=l.lno
        left outer join room r on l.rno=r.rno
        left outer join memb m2 on l.mno=m2.mno;

/* 7단계: 매니저의 직위 출력
 * => 매니저 번호는 lect 테이블 있다.
 * => 매니저 직위는 mgr 테이블에 있다.
 */
select
  la.lano,
  l.titl,
  m.name snm,
  s.work,
  la.rdt,
  r.name rnm,
  m2.name mnm,
  mr.posi
from lect_appl la
        join memb m on la.mno=m.mno
        join stnt s on la.mno=s.mno
        join lect l on la.lno=l.lno
        left outer join room r on l.rno=r.rno
        left outer join memb m2 on l.mno=m2.mno
        left outer join mgr mr on l.mno=mr.mno;

 

 

 

eomcs-docs\sql\Exam09_1.sql

 

 

서브 쿼리

 

=> 쿼리문 안에 쿼리문을 실행하는 기법
=> 성능 문제를 생각하면서 사용해야 한다.

/* join이용하여 데이터를 추출한 방법 */
select la.lano, l.titl, m.name, s.work, la.rdt, r.name, m2.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
        left outer join room r on l.rno=r.rno
        left outer join memb m2 on l.mno=m2.mno
        left outer join mgr mr on l.mno=mr.mno;

/* select 절에 서브쿼리 사용하기 */

/* 수강신청 데이터를 출력 */
select
    la.lano,
    la.lno,
    la.mno,
    la.rdt
from lect_appl la;

/* => 1단계: 수강신청 데이터를 출력 */
select
  la.lano,
  la.lno,
  la.mno,
  date_format(la.rdt, '%m/%d/%Y') 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;

 

 

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;

/* lect_appl 테이블 대신에 서브 쿼리의 결과를 테이블로 사용할 수 있다. */
select
    la2.lano,
    la2.rdt,
    la2.sname,
    la2.work,
    l2.titl,
    l2.rname,
    l2.mname,
    l2.posi
from (
        select
            la.lano,
            la.lno,
            la.rdt,
            m.name sname,
            s.work
        from lect_appl la
            inner join memb m on la.mno=m.mno
            inner join stnt s on la.mno=s.mno) la2
     inner join (
        select
            l.lno,
            l.titl,
            r.name rname,
            m.name mname,
            mr.posi
        from lect l
            left outer join room r on l.rno=r.rno
            left outer join memb m on l.mno=m.mno
            left outer join mgr mr on l.mno=mr.mno
     ) l2 on la2.lno=l2.lno;

/* 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;

 

 

where 절에 서브쿼리 사용하기
/* 과장 또는 대리 매니저가 담당하고 있는 수강 신청만 추출하기 */
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 ('과장', '주임'));

 

 

서브쿼리 예
-- 서브쿼리 예1 : select 절에 서브쿼리를 둘 때 + where 절에 서브쿼리를 둘 때
select
  la.lano,
  (select titl from lect where lno=la.lno) lect_title,
  (select name from memb where mno=la.mno) student_name,
  (select work from stnt where mno=la.mno) student_work,
  to_char(la.rdt, 'YYYY-MM-DD') reg_date,
  ifnull((select name from room where rno=(select rno from lect where lno=la.lno)), '') room_name,
  ifnull((select name from memb where mno=(select mno from lect where lno=la.lno)), '') mgr_name,
  ifnull((select posi from mgr where mno=(select mno from lect where lno=la.lno)), '') mgr_posi
from lect_appl la;


-- 서브쿼리 예2 : from 절에 서브쿼리를 둘 때
-- 1) 강의 정보
select
  l.lno lect_no,
  l.titl lect_title,
  ifnull((select name from room where rno=l.rno), '') room_name,
  ifnull((select name from memb where mno=l.mno), '') mgr_name,
  ifnull((select posi from mgr where mno=l.mno), '') mgr_posi
from
  lect l;

-- 2) 수강생 정보
select
  s.mno std_no,
  (select name from memb where mno=s.mno) std_name,
  s.work std_work
from
  stnt s;

-- 3) 수강신청 정보
select
  la.lano,
  la.lno,
  la.mno,
  to_char(la.rdt, 'YYYY-MM-DD') reg_date
from lect_appl la;

-- 4) 수강신청 정보 + 강의 정보 + 수강생 정보
select
  la.lano,
  le.lect_no,
  le.lect_title,
  le.room_name,
  le.mgr_name,
  le.mgr_posi,
  st.std_no,
  st.std_name,
  st.std_work,
  to_char(la.rdt, 'YYYY-MM-DD') reg_date
from lect_appl la
  inner join (
      select
        l.lno lect_no,
        l.titl lect_title,
        ifnull((select name from room where rno=l.rno), '') room_name,
        ifnull((select name from memb where mno=l.mno), '') mgr_name,
        ifnull((select posi from mgr where mno=l.mno), '') mgr_posi
      from
        lect l
    ) le on la.lno=le.lect_no
  inner join (
    select
      s.mno std_no,
      (select name from memb where mno=s.mno) std_name,
      s.work std_work
    from
      stnt s
    ) st on la.mno=st.std_no;

 

 

 

eomcs-docs\sql\Exam10_1.sql

 

 

데이터를 특정 컬럼을 기준으로 그룹으로 묶어 질의하기

 

=> group by ~ having ~

/* 각 지점별 강의실 수 구하기*/
-- 1단계: 강의실 목록 구하기
select
    r.rno,
    r.loc,
    r.name
from
    room r;

-- 2단계: 지점정보를 저장한 컬럼을 기준으로 그룹으로 묶는다.
select
    r.rno, -- 그룹으로 묶인 경우 그 그룹의 첫 번째 데이터 값만 출력한다.
    r.loc,
    r.name -- 그룹으로 묶인 경우 그 그룹의 첫 번째 데이터 값만 출력한다.
from
    room r
group by
    r.loc;

-- 3단계: group by를 통해 데이터를 그룹으로 묶은 경우
--        개별 항목의 값을 나타내는 컬럼의 값은 의미가 없기 때문에 제거한다.
select
    r.loc
from
    room r
group by
    r.loc;

-- 4단계: 그룹으로 묶은 경우 그룹 관련 함수를 사용할 수 있다.
select
    r.loc,
    count(*) as cnt
from
    room r
group by
    r.loc;

-- 5단계: group by의 결과에서 최종 결과를 선택할 조건을 지정하고 싶다면
--       having절을 사용한다.

-- 예1) having 절에서 집합 함수 사용
select
    r.loc,
    count(*) as cnt
from
    room r
group by
    r.loc
having
    count(*) > 3; -- 집합 함수, group by 조건 컬럼, select 절의 컬럼 사용 가능

-- 예2) having 절에서 group by 조건 컬럼 사용
select
    count(*) as cnt
from
    room r
group by
    r.loc
having
    r.loc = '강남'; -- 집합 함수, group by 조건 컬럼, select 절의 컬럼 사용 가능

-- 예3) having 절에서 select 절 컬럼 사용
select
    count(*) as cnt
from
    room r
group by
    r.loc
having
    cnt > 3; -- 집합 함수, group by 조건 컬럼, select 절의 컬럼 사용 가능

select
    r.name,
    count(*) as cnt
from
    room r
group by
    r.loc
having
    r.name = '302'; -- 집합 함수, group by 조건 컬럼, select 절의 컬럼 사용 가능

-- 오류) select 절에 선언되지 않은 컬럼을 지정할 수 없다.
select
    r.loc,
    count(*) as cnt
from
    room r
group by
    r.loc
having
    r.name = '302'; -- 집합 함수, group by 조건 컬럼, select 절의 컬럼 사용 가능

-- 실행 순서: from --> where --> group by --> select --> having --> order by
-- => MySQL이 아닌 다른 DBMS에서 실행하여 실행 순서를 확인할 필요가 있음!
select
    'okok' as test,
    r.loc as location,
    count(*) as cnt
from
    room r
where 
    r.loc <> '강남'
group by
    r.loc
having
    cnt > 2 and test = 'okok'
order by 
    cnt desc;