본문 바로가기
개발 교육

11.13 MySQL join/서브쿼리

by azure05 2023. 11. 13.

오늘도 이어서 MySQL을 사용한 join과 서브쿼리에 대해서 배워봤다. 

 

핵심키워드는 교차조인, 내부조인, 등가조인, 자연조인, using 조인이다.

 

-- 인물 테이블
drop table tableA;
CREATE TABLE tableA (
  id	INT NOT NULL AUTO_INCREMENT,
  pno	int,			-- 직업번호
  name 	VARCHAR(10), 	-- 이름
  PRIMARY KEY (id));

INSERT INTO tableA VALUES  ( null, 101, '이순신');
INSERT INTO tableA VALUES  ( null, 102, '홍길동');
INSERT INTO tableA VALUES  ( null, 100, '안중근');
INSERT INTO tableA VALUES  ( null, 102, '임꺽정');
INSERT INTO tableA VALUES  ( null, 100, '윤봉길');
INSERT INTO tableA VALUES  ( null, 101, '강감찬');
select * from tableA;

-- 직업 테이블
drop table tableB;
CREATE TABLE tableB (
  id	INT NOT NULL AUTO_INCREMENT,
  cno	int,			-- 직업번호
  name 	VARCHAR(10), 	-- 직업
  PRIMARY KEY (id));

INSERT INTO tableB VALUES  ( null, 100, '의사');
INSERT INTO tableB VALUES  ( null, 101, '장군');
INSERT INTO tableB VALUES  ( null, 102, '의적');
select * from tableB;

 

 

 

drop table tableA;
CREATE TABLE tableA (
  id	INT NOT NULL AUTO_INCREMENT,
  pno	int,				-- 교수 번호
  name 	VARCHAR(10), 		-- 교수 이름
  PRIMARY KEY (id));
INSERT INTO tableA VALUES  ( null, 100, '홍길동');
INSERT INTO tableA VALUES  ( null, 101, '이순신');
INSERT INTO tableA VALUES  ( null, 102, '안중근');
INSERT INTO tableA VALUES  ( null, 103, '임꺽정');
INSERT INTO tableA VALUES  ( null, 104, '강감찬');

drop table tableB;
CREATE TABLE tableB (
  id	INT NOT NULL AUTO_INCREMENT,
  name 	VARCHAR(10), 		-- 과목명
  num	INT,				-- 학점
  pno 	int,				-- 담당 교수번호
  PRIMARY KEY (id));
  
INSERT INTO tableB VALUES  ( null, '국어', 4, 103);
INSERT INTO tableB VALUES  ( null, '영어', 3, 104);
INSERT INTO tableB VALUES  ( null, '수학', 2, 102);
INSERT INTO tableB VALUES  ( null, '사회', 1, 101);
INSERT INTO tableB VALUES  ( null, '체육', 2, 103);
INSERT INTO tableB VALUES  ( null, '생물', 2, 102);
select * from tableB; 

-- 2학점 과목을 강의하는 교수를 검색하세요. 
-- 서브쿼리 또는 조인을 생각해보기 1st 조인!
select distinct a.name from tableA a,tableB b
where a.pno = b.pno
and b.num=2;

 

 

drop table tableA;
CREATE TABLE tableA (
  id	INT NOT NULL AUTO_INCREMENT,
  eno	int, 				-- 학번
  name 	VARCHAR(10), 		-- 학생 이름
  major VARCHAR(10), 		-- 학과명
  year	int,				-- 학년
  PRIMARY KEY (id));
  
INSERT INTO tableA VALUES  ( null, 1000,'홍길동','국어',1);
INSERT INTO tableA VALUES  ( null, 1001,'이순신','화학',1);
INSERT INTO tableA VALUES  ( null, 1002,'안중근','화학',2);
INSERT INTO tableA VALUES  ( null, 1003,'임꺽정','국어',2);
INSERT INTO tableA VALUES  ( null, 1004,'강감찬','화학',1);
select * from tableA;

-- 기말고사 점수 테이블
drop table tableB;
CREATE TABLE tableB (
  id	INT NOT NULL AUTO_INCREMENT,
  eno	int, 		-- 학번
  cno   int, 		-- 시험과목번호
  result int,		-- 점수
  PRIMARY KEY (id));
  
INSERT INTO tableB VALUES  ( null, 1000, 10, 59);
INSERT INTO tableB VALUES  ( null, 1000, 20, 34);
INSERT INTO tableB VALUES  ( null, 1001, 10, 80);
INSERT INTO tableB VALUES  ( null, 1001, 20, 79);
INSERT INTO tableB VALUES  ( null, 1001, 30, 33);
INSERT INTO tableB VALUES  ( null, 1002, 20, 48);
INSERT INTO tableB VALUES  ( null, 1003, 30, 55);
INSERT INTO tableB VALUES  ( null, 1004, 10, 99);

select * from tableB; 

-- 화학과 1학년 학생들의 성적(점수)을 검색하세요.
select a.eno,a.name,b.result
from tableA a, tableB b
where a.eno = b.eno and major='화학'and year = 1;

 

교차조인,내부조인, using조인

-- join의 종류
-- 교차조인 , cross join
select *
from tableA a, tableB b
where a.eno = b.eno;

-- 자연 조인 
select * from tableA
natural join tableB;

-- 등가조인(내부 조인)
select * 
from tableA 
inner join tableB
on tableA.eno = tableB.eno;

-- using 조인
select * from tableA
join tableB using(eno);

 

 

내부조인 과 between

 

between A and B

=> A와 B사이 모든 값을 조건으로 합니다!

 

-- 내부조인
select name, grade from tableA t1
	inner join tableB t2
    on t1.salary >= t2.losalary
    and t1.salary <= t2.hisalary;
    
-- between
select name, grade from tableA t1
	inner join tableB t2
    on t1.salary between t2.losalary and t2.hisalary;
    
-- 일반조인
select name,grade from tableA t1, tableB t2
where t1.salary between t2.losalary and t2.hisalary;

-- 등급이 A 인 사람 이름 보여주기
select name,grade 
from tableA t1, tableB t2
where t2.grade='A'
and t1.salary between t2.losalary and t2.hisalary;

 

셀프 조인

자기자신 테이블끼리 조인하기!

 

DROP TABLE table01;
CREATE TABLE table01 (
  id 		INT NOT NULL AUTO_INCREMENT,
  name 	VARCHAR(45) NULL,
  eno 	INT NULL,	-- 사원번호
  mgr 	INT NULL,	-- 사수번호, 멘토번호
  salary 	INT NULL,
  PRIMARY KEY (`id`)); -- 위에 변경하면 같이 변경한다.

insert into table01 values (null, '홍길동', 1000, null, 100); 
insert into table01 values (null, '이순신', 1001, 1000, 200); 
insert into table01 values (null, '안중근', 1002, 1001, 300); 
insert into table01 values (null, '임꺽정', 1003, 1002, 400); 


-- self join
select * from table01 a, table01 b
where a.mgr = b.eno;

 

 

전형적으로 동명이인 검색문제는 셀프 조인 입니다! (self join - 자기자신 테이블 안에서 join하는 것)

 

동명이인 검색?

1. t1.id!=t2.id

2. name끼리 같은 것

3. distinct t1.name

-- 동명이인을 검색하세요! 셀프조인 문제
-- cross join한 결과
select distinct t1.name
from tableA t1, tableA t2
where t1.id!=t2.id 
and t1.name=t2.name;

 

세미조인

-- DROP TABLE menu;
CREATE TABLE menu( 
    foodnum int,
    name varchar(20)
);
-- DROP TABLE sell;
CREATE TABLE sell( 
    no int, -- 인덱스
    count int, -- 판매수량
    foodnum int -- 판매음식 번호
);

insert into menu values (1, '짜장'); 
insert into menu values (2, '우동'); 
insert into menu values (3, '냉면'); 
insert into menu values (4, '탕슉'); 
insert into menu values (5, '양장'); 

insert into sell values (1, 2, 1); 
insert into sell values (2, 3, 2); 
insert into sell values (3, 4, 2); 
insert into sell values (4, 2, 2); 
insert into sell values (5, 2, 1); 

-- 오늘 판매된 음식을 출력하세요.

-- 판매된 음식 번호(1,2,2,2,1)
select distinct (s.foodnum)
from sell s;

select * from menu d -- main쿼리에서 사용된 테이블 
-- where d.foodnum = 1 or d.foodnum = 2
-- where d.foodnum in(1,2);
-- where d.foodnum in(1,2,2,2,1);
where d.foodnum in(select distinct (s.foodnum) -- 서브쿼리에서 사용된 테이블
from sell s);

-- 1. menu에서 row 1개 가져온다
-- 2. 가져온 row가 exists 안의 문장에서 참인지 거짓인지 평가한다.
-- 3. 참인 경우는 가져온 row를 출력한다.
--    거짓인 경우는 다음 row를 가져온다. 
-- 4. 이러한 조인방식을 세미조인이라고 한다.
-- 5. 세미조인의 반대는 안티조인( not exist)

-- performance 좋음, 수행속도 굉장히 빠름
select * 
from menu d
where not exists (select * 
					from sell s 
                    where d.foodnum = s.foodnum);

 

 

 

outer join

 

outerjoin 중에서도 Left join에 대해서 배워보겠다.

DROP TABLE table01;
CREATE TABLE table01
( 
    ID		int, 
    name	VARCHAR(20)
);
DROP TABLE table02;
CREATE TABLE table02
( 
    ID	int, 
    age     int
);
INSERT INTO table01(ID, name) VALUES (1, 'tiger1'); 
INSERT INTO table01(ID, name) VALUES (2, 'tiger2'); 
INSERT INTO table01(ID, name) VALUES (3, 'tiger3'); 
INSERT INTO table01(ID, name) VALUES (4, 'tiger5'); 
INSERT INTO table01(ID, name) VALUES (5, 'tiger6'); 

INSERT INTO table02(ID, age) VALUES (3, 30); 
INSERT INTO table02(ID, age) VALUES (4, 40); 
INSERT INTO table02(ID, age) VALUES (5, 50); 
INSERT INTO table02(ID, age) VALUES (6, 60); 
INSERT INTO table02(ID, age) VALUES (7, 80); 

select *
from table01, table02
where table01.ID = table02.ID;


-- outer join
select *
from table01
left join table02 using (ID);

 

 

병합 union, union all

drop table table01;
CREATE TABLE `table01` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `eno` int,
  `name` VARCHAR(45) NULL,
  `salary` INT NULL,
  PRIMARY KEY (`id`));
drop table table02;
CREATE TABLE `table02` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `eno` int,
  `name` VARCHAR(45) NULL,
  `salary` INT NULL,
  PRIMARY KEY (`id`));
  

INSERT INTO table01 VALUES (null, 10, 'tiger1', 100); 
INSERT INTO table01 VALUES (null, 20, 'tiger2', 200); 
INSERT INTO table01 VALUES (null, 30, 'tiger3', 300); 
SELECT * FROM table01;


INSERT INTO table02 VALUES (null, 10, 'tiger3', 100); 
INSERT INTO table02 VALUES (null, 20, 'tiger4', 200); 
INSERT INTO table02 VALUES (null, 30, 'tiger5', 300); 
SELECT * FROM table02; 

-- 중복 데이터값 제거
select name from table01
union 
select name from table02;

-- 	중복 데이터값 제거하지 않음
select name from table01
union all
select name from table02;

-- 병합의 결과 
select name from table01
where eno <15
union
select name from table02
where eno>25;

 

upper lower, length 관련 쿼리

DROP TABLE table01;
CREATE TABLE table01
( 
    eno     int,
    ename   VARCHAR(20),
    sal     int
);
INSERT INTO table01 values(10, '이순신', 100 );
INSERT INTO table01 values(20, '홍길동', null );
INSERT INTO table01 values(30, '김신', 300 );
INSERT INTO table01 values(40, '한글을만들자abc', 100 ); 

select * from table01;

select upper(ename) from table01;
select lower(ename) from table01;
-- select initcap(ename) from table01;

select * 
from table01
where upper(ename) = upper('TIGER1');

select length(ename) from table01;


select ename from table01 where length(ename)=9;

 

 


 

오후수업

 

DROP TABLE table01;
CREATE TABLE table01
( 
    eno     int,
    tel      varchar(20),
    ename   varchar(20)
);

insert into table01 values(10, '02)200-300', '호랑이만세' );
insert into table01 values(20, '051)800-900', '만호랑이세' );
insert into table01 values(30, '1234)500-600', '만세호랑이' );
insert into table01 values(40, '1234)500-600', ' 호 랑 이 ' );
insert into table01 values(null, '1234)500-600', null );
select * from table01; 

-- 5번 인덱스부터 3자리를 얻어온다.
select substr(tel,5,3) from table01;

-- 9번 인덱스 부터 끝까지
select substr(tel,9) from table01;
-- 끝에서 부터 인덱스를 카운팅하여 끝까지 
select substr(tel,-4) from table01;

select substr(tel,5,3), substr(tel,9),substr(tel,-4)  from table01;

-- 문자열 찾았을 때의 인덱스 번호를 리턴한다.
select instr(ename,'호랑이') from table01;
select instr(tel,')' ) from table01;
select substr(tel,1,instr(tel,')')-1) from table01;

select replace(ename,'호랑이','코끼리') from table01;
select replace(tel,'-',' ') from table01;

-- right pad
select rpad(ename,20,'*') from table01;
-- left pad
select lpad(ename,20,'*') from table01;

select substr(tel,-7,4) from table01;  -- 끝에서부터 7번째 인덱스에서 4개 취함
select rpad(substr(tel,-7,4),7,'*') from table01;

 

문자열 관련

DROP TABLE table01;
CREATE TABLE table01
( 
    eno     int,
    tel      varchar(20),
    ename   varchar(20)
);

insert into table01 values(10, '02)200-300', '호랑이만세' );
insert into table01 values(20, '051)800-900', '만호랑이세' );
insert into table01 values(30, '1234)500-600', '만세호랑이' );
insert into table01 values(40, '1234)500-600', ' 호 랑 이 ' );
insert into table01 values(null, '1234)500-600', null );
select * from table01; 

-- 5번 인덱스부터 3자리를 얻어온다.
select substr(tel,5,3) from table01;

-- 9번 인덱스 부터 끝까지
select substr(tel,9) from table01;
-- 끝에서 부터 인덱스를 카운팅하여 끝까지 
select substr(tel,-4) from table01;

select substr(tel,5,3), substr(tel,9),substr(tel,-4)  from table01;

-- 문자열 찾았을 때의 인덱스 번호를 리턴한다.
select instr(ename,'호랑이') from table01;
select instr(tel,')' ) from table01;
select substr(tel,1,instr(tel,')')-1) from table01;

select replace(ename,'호랑이','코끼리') from table01;
select replace(tel,'-',' ') from table01;

-- right pad
select rpad(ename,20,'*') from table01;
-- left pad
select lpad(ename,20,'*') from table01;

select substr(tel,-7,4) from table01;  -- 끝에서부터 7번째 인덱스에서 4개 취함
select rpad(substr(tel,-7,4),7,'*') from table01;


select concat(eno, ' : ', ename) from table01;
select concat(eno, concat( ' : ', ename)) from table01;

-- 맨 끝 공백을 제거시켜주는 역할
select trim(ename) from table01;

select 900 + ifnull(eno,99), ifnull(ename,'익명') from table01;

select eno,
	case eno
	when 10 then '호랑이1'
    when 20 then '호랑이2'
    when 30 then '호랑이3'
    end as '결과'
    from table01;

select eno,
	case eno
	when 10 then eno*2
    when 20 then eno*3
    when 30 then eno*4
    end as '결과'
    from table01;
    
    
select round(1234.4) from dual;
select round(1234.5) from dual;
select round(1234.5678) from dual;
select round(1235,-1) from dual; -- 1240
select round(1234,-1) from dual; -- 1230

select ceil(3.14) from dual;
select floor(3.14) from dual;

select ceil(-3.14) from dual;
select floor(-3.14) from dual;

select ceil(7/3) from dual;

select floor(7/3), mod(7,3) from dual; 

select 10+20 as 결과 from dual; -- 결과 이름의 column 값? 30
select 10+'20' as 결과 from dual; -- 숫자로 인식해, 구태여 문자를 숫자로 변환 시킬 필요 없음
select '10'+20 as 결과 from dual;

select 'tiger'+ 'lion' as '결과' from dual; -- 문자로 인식해 합산? 합치기? 되지 않음

 

 

집계함수

DROP TABLE table01;
CREATE TABLE table01( 
    eno     int,
    sal int
);
insert into table01 values(10,100);
insert into table01 values(20,null); 
insert into table01 values(30,300);
insert into table01 values(40,300); 

-- 집계함수
select * from table01;
select sum(sal), 
	   sum(distinct sal),
       count(eno),
       max(eno),
       min(eno),
       variance(eno), -- 분산 
       stddev(eno) -- 표준편차
       from table01;
       
select count(*) from table01
where sal = 300;

 

 

DROP TABLE table01;
CREATE TABLE table01
( 
    eno int,
    address varchar(20),
    salary int
);

INSERT INTO table01 VALUES (10, '서울', 100); 
INSERT INTO table01 VALUES (20, '부산', 200); 
INSERT INTO table01 VALUES (20, '대구', 300); 
INSERT INTO table01 VALUES (10, '서울', 400); 
select * from table01; 

select avg(salary) from table01 where eno=20;

-- 부서번호가 20인 사원중에 급여가 가장 많은 사원의 금액은 얼마인가?
select max(salary) from table01
where eno=20;

-- 부서번호가 20인 사원중에 급여가 가장 많은 사람의 이름은 무엇인가?
select address from table01
where salary=(select max(salary)from table01 where eno=20);

 

 

 

통계함수 Group by 와 관련지어서

DROP TABLE table01;
CREATE TABLE table01( 
    eno    int,
    sal     int);

insert into table01 values(1, 800);
insert into table01 values(2, 200);
insert into table01 values(2, 400);
insert into table01 values(1, 500);
insert into table01 values(2, 300);


select * from table01; 

select eno, sum(sal) from table01
group by eno;

select eno, sum(sal)
from table01
where sal>300
group by eno;

select eno,sum(sal)
from table01
where sal>300
group by eno
having sum(sal)>1000;

 

'개발 교육' 카테고리의 다른 글

11.17 리눅스  (1) 2023.11.17
11.15 DB  (2) 2023.11.15
11.10 수업 MySQL  (0) 2023.11.10
11.9 수업 MySQL  (0) 2023.11.09
11.03 <자바FX>  (0) 2023.11.04