본문 바로가기

(09.10) 데이터베이스 - SQL, SELECT, WHERE, LIKE, ORDER BY, GROUP BY, HAVING, JOIN

@starweb2025. 9. 10. 19:56

[ 5주차 - 0910 ] 

    금일 커리큘럼
        ├ 09:00 ~ 12:00 관계형 데이터베이스 (SQL 설명, SELECT, WHERE, LIKE, NULL, LIMIT)
        └ 13:00 ~ 18:00 관계형 데이터베이스 (SQL 함수, ORDER BY, GROUP BY, HAVING, JOIN)

1. SQL

SQL(Structured Query Language)은 관계형 데이터베이스를 조작하기 위한 표준 언어

SQL 문법 특징

  • 대부분의 SQL 명령은 세미콜론(;)으로 끝남
  • 키워드는 대소문자를 구분하지 않음
  • 데이터는 대소문자를 구분
-- 예시
SELECT 컬럼명,컬럼명 FORM 테이블명,테이블명;

-- DBMS 함수 실행
SELECT version();

-- 수식계산
SELECT 10 * 2.5;
SELECT sin(pi()/4), (4+1)*5;

-- 여러줄
SELECT
    user(),
    current_date;

2. SELECT 문

기본형태

SELECT [DISTINCT] 컬럼명 [AS 별칭]
FROM 테이블명
WHERE 조건
ORDER BY 컬럼 [ASC|DESC];

DESC 테이블명; -- 스키마 설정 확인
  • ORDER BY : 기준컬럼
    • ASC 오름차순, DESC 내림차순
  • DESC 테이블명; 같은 경우 스키마 설정 확인임. (정수,null 등)

데이터 조회

-- 전체 조회
SELECT * FROM emp;

-- 특정 컬럼 조회
SELECT job FROM emp;

-- 특정 컬럼 중복값 제외 조회
SELECT DISTINCT job FROM emp; 
SELECT DISTINCT job, empno FROM emp;

-- 컬럼 별칭 부여 방식
SELECT empno 사번,
       ename AS "사원 이름",
       mgr AS 매니저
FROM emp;

컬럼 결합과 연산

-- 문자열 결합
SELECT concat(ename, ' [', job, ']') AS "이름 [직원]" FROM emp;

-- 산술 연산
SELECT
    ename,
    sal,
    sal / 12 AS "월급"
FROM emp;

중복 제거

-- 중복된 값 제거
SELECT DISTINCT department_id 
FROM employees;

-- 여러 컬럼의 조합으로 중복 제거
SELECT DISTINCT department_id, job_id
FROM employees;

3. WHERE 문과 조건 검색

WHERE 심플 예시

SELECT * FROM emp WHERE deptno = 10;
SELECT * FROM emp WHERE job = 'manager';
SELECT * FROM emp WHERE sal > 2500;
SELECT * FROM emp WHERE sal > 2500 and job = 'manager';
SELECT * FROM emp
    WHERE (deptno = 10 or deptno = 20)
    and sal >= 2000;

논리연산

  • and연산은 or보다 우선순위 (먼저필터)
-- and 연산
SELECT * FROM emp
    WHERE sal >= 1000 and deptno = 10;

-- or 연산
SELECT * FROM emp
    WHERE deptno = 10 or deptno = 20;

-- not 연산
SELECT * FROM emp
    WHERE not deptno = 30;

-- and or 우선순위 확인
SELECT * FROM emp
WHERE deptno = 10 or deptno = 20
    and sal >= 2000;
-- deptno 10, 20 중 2000 이상인 것만 추출 목적이였으나,
-- 결과는 dept 10 전부 나오고, 20에서 2000이상만 나옴
-- 원하는 결과 나올려면
-- (deptno = 10 or deptno = 20) and sal >= 2000;

in 연산

-- IN 사용
SELECT * FROM emp 
    WHERE deptno in (10, 20); -- 10, 20 인 것

-- NOT IN 사용
SELECT * FROM emp 
    WHERE deptno not in (20, 30); -- 20, 30 아닌 것

between 연산

-- 일반 범위
SELECT * FROM emp 
    WHERE sal BETWEEN 2300 and 3000; -- 2300이상 3000이하

-- 날짜 범위
SELECT * FROM emp 
    WHERE hiredate BETWEEN '1981-01-01' and '1981-12-31';

4. 패턴 매칭 - LIKE

와일드카드 문자형태

  • % : 0개 이상의 문자
  • _ : 정확히 1개의 문자

!! 포맷형식 아님

LIKE 사용 방식

-- 이름이 F로 시작
SELECT * FROM emp
    WHERE ename like 'F%';

-- 이름이 두번째 O 
SELECT * FROM emp
    WHERE ename like '_O%';

-- 1981년 입사
SELECT * FROM emp
    WHERE hiredate like '1981%';
-- 함수형 방식
SELECT * FROM emp
    WHERE year(hiredate) = 1981;


-- 9월 입사
SELECT * FROM emp
    WHERE hiredate like '____-09%'; -- '_' 4개
-- 함수형 방식
SELECT * FROM emp
    WHERE month(hiredate) = 9;

5. NULL 처리

null 확인 방법

NULL은 = 또는 != 연산자로 비교할 수 없음. 함수로 써야함

-- null 인 경우
SELECT * FROM emp
    WHERE comm is NULL;

-- null 아닌 경우
SELECT * FROM emp
    WHERE comm is not NULL;

null 처리 함수 (ifnull, coalesce)


-- ifnull : null인 경우 대체 처리
SELECT comm, ifnull(comm, -1) -- null를 -1로 대체
    FROM emp;


SELECT ename, sal, comm, 
    sal + ifnull(comm, 0) as 상여금포함연봉 
    FROM emp;
    -- sal 연봉 | comm 상여금 (null있음) 

SELECT ename, comm, job, 
    coalesce(comm, job, '하하') 
    FROM emp;
    -- comm 이 null이면 job을 쓰고, 
    -- job도 null이면 '하하'

6. 정렬 - ORDER BY

  • FROM 구절에 붙음

단일 컬럼 정렬

-- 오름차순 asc
SELECT * FROM emp 
    ORDER BY ename asc;

-- 내림차순 desc
SELECT * FROM emp 
    ORDER BY ename desc;

다중 컬럼

SELECT deptno, ename, sal FROM emp
    ORDER BY deptno, sal asc;
    -- 부서번호, 연봉 오름차순


SELECT deptno, ename, sal FROM emp
    ORDER BY deptno asc, sal desc;
    -- 부서번호 오름차순, 연봉 내림차순

SELECT empno, mgr*22 as 월급, ename FROM emp
    ORDER BY 월급 desc, ename asc;
    -- 별칭기준 내림차순, 이름 오름차순

7. 제한걸기 - LIMIT

SELECT * FROM emp 
    ORDER BY ename asc 
    LIMIT 5; -- 5개까지만 노출

-- 페이징  LIMIT (페이지번호-1) * 페이지크기, 페이지크기
SELECT deptno, job, ename, sal
    FROM emp
    ORDER BY deptno asc
    LIMIT 0, 5;  -- 1 페이지

SELECT deptno, job, ename, sal
    FROM emp
    ORDER BY deptno asc
    LIMIT 5, 5;  -- 2 페이지

8. SQL 함수

문자열


-- 대소문자
SELECT upper('hello'), lower('WORLD'); -- HELLO | world
-- 문자열 결합
SELECT concat('Hello', ' ', 'World'); -- Hello World
-- 부분 문자열
SELECT substring('Hello World', 1, 5); -- Hello
SELECT substr('Hello World', 1, 5); -- Hello
-- 문자열 길이
SELECT length('Hello World'); -- 11
-- 공백 제거
SELECT trim('  양쪽 공백  '),
    ltrim('  좌측 공백'), 
    rtrim('우측 공백  ');
-- 문자열 치환
SELECT REPLACE('Hello World', 'World', 'MySQL'); -- Hello MySQL
-- 패딩
SELECT lpad('test', 5, '*'), -- *test 
    rpad('test', 7, '?');  -- tset???

날짜


-- 현재날짜, 시간 등
SELECT curdate(), curtime(), now();
-- yyyy-mm-dd | hh:mm:ss | yyyy-mm-dd hh:mm:ss

-- 년월일 포맷팅
SELECT date_format(now(), '%y년 %m월 %d일');


-- 오늘 날짜와 시간
SELECT curdate() as 금일날짜, curtime() as 현재시간;

-- date_format : 몇년, 몇월, 몇일
SELECT 
    date_format(curdate(), '%Y년') as 년도, -- 2025년
    date_format(curdate(), '%m월') as 월, -- 09일
    date_format(curdate(), '%d일') as 일; -- 10일

-- 날짜연산 : date_add, date_sub
SELECT
    curdate() as 오늘날짜, 
    date_add(curdate(), interval 200 day) as '200일 후',
    date_sub(curdate(), interval 100 day) as '100일 전'; 
    -- 2025-09-10 | 2026-03-29 | 2025-06-02


-- 날짜차이 : datediff (일), timestampdiff(기준)
SELECT
    curdate() as 오늘날짜,
    abs(datediff('2024-01-01', now())) as '일차이',
    abs(timestampdiff(month, '2024-01-01', curdate())) as '월차이',
    abs(timestampdiff(year, '2024-01-01', curdate())) as '년도차이';
-- 618 | 20 | 1

활용 예시

-- 활용
SELECT
    deptno as 부서번호,
    empno as 사번,
    ifnull(mgr, 7839) as 책임자,
    job as 직급,
    concat(ename, '님') as 사원명,
    hiredate as 입사일,
    concat(
            abs(timestampdiff(year, hiredate, curdate())),
            "년"
    ) as 년차,
    concat(floor(sal), '만원') as 연봉,
    concat(floor(ifnull(comm, 0)), '만원') as 상여금
    FROM emp
    ORDER BY deptno asc;

9. 그룹 함수

-- count: 행 개수
SELECT count(*) FROM emp; -- 전체 행 개수 : 14
SELECT count(comm) FROM emp; -- NULL 제외 행 개수

-- sum: 합계
SELECT sum(sal) as 총인건비 FROM emp;
-- 29025.00

-- avg: 평균
SELECT avg(sal) as 평균인건비 FROM emp;
-- 2073.214286

-- max, min: 최대값, 최소값
SELECT max(sal) as 최고연봉액,
       min(sal) as 최저연금액 FROM emp;
-- 5000.00 | 800.00

group by

  • group by 컬럼 : 해당 컬럼의 값이 같은 행들을 하나의 그룹으로 묶음
  • FROM 이후 구절에 붙음
-- 직급별 평균연봉액
SELECT job, avg(sal) FROM emp
    GROUP BY job;

-- 부서 내 직급별 평균연봉액
SELECT deptno, job, avg(sal) FROM emp
    GROUP BY deptno, job
    ORDER BY 1, 2; -- 첫째,두번째열 (deptno,job) 오름차순

having

  • where : 행(row) 단위 조건 (그룹핑 전)
    • 데이터를 필터링한 후 그룹을 만듬
  • having : 그룹(group) 단위 조건 (그룹핑 후)
    • 그룹을 만든 뒤, 집계 결과를 조건으로 걸러냄
  • group by 이후 구절에 붙음
-- 10번 부서 제외 모든 사원의 부서별, 직급별 평균 연봉액
-- 단 평균 1000이상

SELECT deptno, job, avg(sal) as 평균연봉액 FROM emp
    WHERE deptno != 10
    GROUP BY deptno, job
    HAVING avg(sal) >= 1000;

10. JOIN

JOIN은 하나 이상 테이블로부터 연관 데이터 검색하는 방식

  • Cross Join
    • 두 테이블의 모든 가능한 쌍이 나타남 (Cartesian Product, 데카르트 곱)
    • 조건이 없으면 행 개수 = A행 × B행 → 데이터 폭발 가능
    • 단점: 실제로는 잘 안 쓰이며, 실수로 사용하면 성능 저하와 불필요한 대량 결과 발생
  • Inner Join
    • 조인 조건을 만족하는 튜플(행)만 나타남
    • 가장 많이 사용하는 일반적인 조인 방식
  • Outer Join
    • 조인 조건을 만족하지 않는 튜플도 NULL과 함께 나타남
    • 종류: LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN
  • Self Join
    • 자기 자신과 조인
    • 같은 테이블을 두 번 별칭(alias) 주어 사용
    • 예: 직원 테이블에서 직원과 상사를 매칭할 때

연습 db 스키마 훑어보기

CREATE TABLE dept ( -- 부서 테이블
    deptno  int  NOT NULL  AUTO_INCREMENT,
    dname   varchar(20),
    loc     varchar(20),
    CONSTRAINT pk_dept PRIMARY KEY ( deptno )
) engine=InnoDB;

CREATE TABLE emp ( -- 직원 테이블
    empno    int  NOT NULL  AUTO_INCREMENT,
    ename    varchar(20),
    job        varchar(20),
    mgr        smallint,
    hiredate    date,
    sal        numeric(7,2),
    comm    numeric(7,2),
    deptno    int,
    CONSTRAINT pk_emp PRIMARY KEY ( empno )
) engine=InnoDB;
DEPT  -- 부서 테이블
+-----------+-----------------------+
| deptno    | int PK AUTO_INCREMENT |
| dname     | varchar(20)           |
| loc       | varchar(20)           |
+-----------+-----------------------+
PK: pk_dept(deptno)


EMP  -- 직원 테이블
+-----------+-----------------------+
| empno     | int PK AUTO_INCREMENT |
| ename     | varchar(20)           |
| job       | varchar(20)           |
| mgr       | smallint              |  # 상사 empno (자기참조)
| hiredate  | date                  |
| sal       | numeric(7,2)          |
| comm      | numeric(7,2)          |
| deptno    | int                   |  # FK → dept.deptno
+-----------+-----------------------+
PK: pk_emp(empno)
FK: fk_emp_dept (deptno → dept.deptno)
INDEX: idx_emp (deptno)
  • 기본키 (Primary Key, PK)
    • 각 테이블의 행을 유일하게 식별
    • dept.deptno, emp.empno
  • 외래키 (Foreign Key, FK)
    • 다른 테이블의 PK(또는 Unique)를 참조
    • emp.deptno → dept.deptno

크로스조인과 이너조인 차이

/*  크로스 조인 | 권장하지 않음 -------- */
-- 모든 가능한 조합
SELECT ename, sal, dname FROM emp, dept;
SELECT e.ename, e.sal, d.deptno, d.dname FROM emp e, dept d;
-- -> 해당 결과 = emp 행수 × dept 행수 만큼 쌍이 생김

/*  이너 조인 | 추천함 -------- */
-- 두 테이블에서 deptno가 일치하는 데이터만
SELECT e.ename, e.sal, d.deptno, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno; -- 만족하는 경우만

이너조인 방식

/* 심플 조인 -------- */
SELECT e.ename, e.sal, d.deptno, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno;

/* ANSI 조인 -------- */
-- natural join (자동으로 같은 컬럼 기준 조인)
SELECT e.ename, e.sal, d.deptno, d.dname
FROM emp e NATURAL join dept d;

-- join ~ using (동일한 컬럼명일 때)
SELECT e.ename, e.sal, d.deptno, d.dname
FROM emp e join dept d USING(deptno);

-- join ~ on 
SELECT e.ename, e.sal, d.deptno, d.dname
FROM emp e join dept d ON e.deptno = d.deptno;

조인 추가 조건

-- 심플조인은 where 절 이미 써있으니 and 추가 사용
SELECT e.ename, e.sal, d.deptno, d.dname 
FROM emp e, dept d
WHERE e.deptno = d.deptno
and e.deptno = 20;

-- ansi 조인은 where 절 추가 사용
SELECT e.ename, e.sal, d.deptno, d.dname 
FROM emp e NATURAL join dept d
WHERE e.deptno = 20;

etc.

hr DB 관련 연습풀이

-- 1. 모든 직원 이름 , 이메일
SELECT concat(first_name, ' ', last_name) as '이름+성',
    email as 이메일
    FROM employees;

-- 2. 급여 1500 이상 직원
SELECT * FROM employees
    WHERE salary >= 1500 is not NULL;

-- 3. 2005년에 입사한 직원
SELECT * FROM employees
    WHERE year(hire_date) = 2005;


-- 4. 이름 an 직원
SELECT * FROM employees
    WHERE first_name LIKE '%an%' or last_name LIKE '%an%';

-- 5 이메일 S 시작, 급여 내림차순
SELECT * FROM employees
    WHERE email LIKE 'S%'
    ORDER BY salary desc;

-- 6. 부서별 직원 수, 평균급여
SELECT 
    department_id as 부서번호,
    COUNT(*) as '부서별 직원 수',
    avg(salary) as 평균급여
    FROM employees
    GROUP BY department_id;

-- 7. 직원이 5명 이상 부서
SELECT 
    department_id as 부서번호,
    COUNT(department_id) as '부서별 직원 수'
    FROM employees
    GROUP BY department_id
    HAVING COUNT(*) >= 5;
starweb
@starweb :: starweb 님의 블로그

starweb 님의 블로그 입니다.

공감하셨다면 구독도 환영합니다!

목차