certification/SQLD

[SQLD] SQL 기본 및 활용

gaing 2024. 11. 12. 23:48

관계형 데이터베이스의 개요

데이터베이스

데이터들을 저장하는 공간

 

관계형 데이터베이스(RDB; Relational DataBase)

관계형 데이터 모델에 기초를 둔 데이터베이스

 

관계형 데이터베이스에서의 설계는 모든 데이터를 2차원 테이블 형태로 표현한 뒤 각 테이블 간의 관계를 정의하는 것으로 시작된다.

 

관계형 데이터베이스 관리 시스템(RDBMS; Relational DataBase Management System)

관계형 데이터베이스(RDB)를 관리/감독하기 위한 시스템

 

Oracle, SQL Server(MSSQL), MySQL, MariaDB, PostgreSQL 등이 속한다.

 

TABLE

관계형 데이터베이스의 기본 단위
  • 항목을 나타내는 각각의 세로 열을 칼럼(Column)이라 하고 가로 행을 로우(Row)라고 한다.
    • 속성 - 칼럼 / 인스턴스 - 로우
  • 데이터 모델에서 엔터티에 해당한다.
  • 데이터베이스는 일반적으로 여러 개의 테이블로 구성된다.

 

SQL(Structured Query Language)

관계형 데이터베이스에서 데이터를 다루기 위해 사용하는 언어

 

컬럼을 업데이트시키고 싶을 때 우리는 데이터베이스에 명령을 전달해야 한다.

하지만 데이터베이스는 오케이구글, 헤이카카오가 아니기 때문에 우리가 사용하는 언어로는 제대로 된 의사전달을 할 수 없다.

그래서 데이터베이스가 이해할 수 있는 언어인 SQL을 사용하여 명령어를 실행시켜야 하는 것이다.


SELECT 문

저장되어 있는 데이터를 조회하고자 할 때 사용하는 명령어
/* 테이블에서 WHERE 조건을 만족하는 SELECT된 컬럼들을 조회
WHERE 절이 없으면 테이블의 전체 ROW를 조회 */
SELECT 컬럼1, 컬럼2, ... FROM 테이블 WHERE 컬럼1 = '아무개';

/* *(asterisk)를 이용해 모든 컬럼 조회 가능 */
SELECT * FROM 테이블;

/* 테이블명이나 컬럼명에 별도의 별칭(Alias)을 붙여줄 수 있음
여러 개의 테이블을 JOIN하거나 서브쿼리가 있을 때
컬럼명 앞에 테이블명을 같이 명시해야 하는 경우 짧게 줄여 쓰기 위함 */

# 줄여쓰지 않는 경우
SELECT BAND.BAND_NAME, BAND_MEMBER.MEMBER_NAME
  FROM BAND, BAND_MEMBER
 WHERE BAND.BAND_CODE = BAND_MEMBER.BAND_CODE;

# 줄여쓰는 경우
SELECT B.BAND_NAME, BM.MEMBER_NAME
  FROM BAND B, BAND_MEMBER BM
 WHERE B.BAND_CODE = BM.BAND_CODE;
  • 테이블명에 Alias를 설정했을 경우 테이블명 대신 Alias를 사용해야 한다.

 

산술 연산자

수학에서 사용하는 사칙연산의 기능을 가진 연산자
연산자 의미 우선순위
( ) 괄호로 우선순위를 조정할 수 있음 1
* 곱하기 2
/ 나누기(0으로 나눌 경우 에러 발생)
+ 더하기 3
- 빼기
% (SQL Server) 나머지(0으로 나눌 경우 NULL 반환)

 

합성 연산자

문자와 문자를 연결할 때 사용하는 연산자
SELECT 'S'||'Q'||'L'||'개'||'발'||'자' AS SQLD
  FROM DUAL;
/* 'SQL개발자'가 DUAL 칼럼에 만들어짐 */

SELECT COL1||' '||'SQLD'||' '||COL2 AS RESULT
  FROM SAMPLE;
/* 'COL1문장 SQLD COL2문장'이 RESULT 칼럼에 만들어짐 

SELECT CONCAT(COUNTRY_ID, COUNTRY_NAME) FROM COUNTRIES;
/* 두 문자열이 합쳐짐 */

함수

문자 함수

  • CHR(ASCII 코드)
    • ASCII 코드 : 총 128개의 문자를 숫자로 표현할 수 있도록 정의해 놓은 코드
    • CHR 함수 : ASCII 코드를 인수로 입력했을 때 매핑되는 문자가 무엇인지를 알려주는 함수
# CHR(65) -> A
SELECT CHR(65) FROM DUAL;

 

  • LOWER(문자열)
    • 문자열을 소문자로 변환해주는 함수
# LOWER('JENNIE') -> jennie
SELECT LOWER('JENNIE') FROM DUAL;

 

  • UPPER(문자열)
    • 문자열을 대문자로 변환해주는 함수
# UPPER('jennie') -> JENNIE
SELECT UPPER('JENNIE') FROM DUAL;

 

  • LTRIM(문자열, [,특정 문자]) *[]는 옵션
    • 특정 문자를 따로 명시해주지 않으면 문자열의 왼쪽 공백을 제거하고, 명시해주었을 경우 문자열을 왼쪽부터 한 글자씩 특정 문자와 비교하여 특정 문자에 포함되어 있으면 제거하고 포함되지 않았으면 멈춘다.
# LTRIM('    JENNIE') -> JENNIE
SELECT LTRIM('     JENNIE') FROM DUAL;

# LTRIM('블랙핑크', '블랙') -> 핑크
SELECT LTRIM('블랙핑크', '블랙') FROM DUAL;

 

  • RTRIM(문자열, [,특정 문자]) *[]는 옵션
    • 특정 문자를 따로 명시해주지 않으면 문자열의 오른쪽 공백을 제거하고, 명시해주었을 경우 문자열을 오른쪽부터 한 글자씩 특정 문자와 비교하여 특정 문자에 포함되어 있으면 제거하고 포함되지 않았으면 멈춘다.
# RTRIM('JENNIE    ') -> JENNIE
SELECT RTRIM('JENNIE    ') FROM DUAL;

# RTRIM('블랙핑크', '핑크') -> 핑크
SELECT RTRIM('블랙핑크', '핑크') FROM DUAL;

 

  • TRIM([위치] [특정문자] [FROM] 문자열) *[]는 옵션
    • 옵션이 하나도 없을 경우 문자열의 왼쪽과 오른쪽 공백을 제거하고, 그렇지 않을 경우 문자열을 위치(LEADING or TRAILING or BOTH)로 지정된 곳부터 한 글자씩 특정 문자와 비교하여 같으면 제거하고 같지 않으면 멈춘다.
    • LTRIM, RTRIM과는 달리 특정 문자는 한 글자만 지정할 수 있다.
# TRIM(' JENNIE  ') -> JENNIE
SELECT TRIM(' JENNIE  ') FROM DUAL;

# TRIM(LEADING '블' FROM '블랙핑크') -> 랙핑크
SELECT TRIM(LEADING '블' FROM '블랙핑크') FROM DUAL;

# TRIM(TRAILING '크' FROM '블랙핑크') -> 블랙핑
SELECT TRIM(TRAILING '크' FROM '블랙핑크') FROM DUAL;

 

  • SUBSTR(문자열, 시작점, [, 길이]) *[]는 옵션
    • 문자열의 원하는 부분만 잘라서 반환해주는 함수
    • 길이를 명시하지 않았을 경우 문자열의 시작점부터 문자열의 끝까지 반환된다.
# SUBSTR('블랙핑크제니', 3, 2) -> 핑크
SELECT SUBSTR('블랙핑크제니', 3, 2) FROM DUAL;

# SUBSTR('블랙핑크제니', 3, 4) -> 핑크제니
SELECT SUBSTR('블랙핑크제니', 3, 4) FROM DUAL;

# SUBSTR('블랙핑크제니', 3) -> 핑크제니
SELECT SUBSTR('블랙핑크제니', 3) FROM DUAL;

 

  • LENGTH(문자열)
    • 문자열의 길이를 반환해주는 함수
# LENGTH('JENNIE') -> 6
SELECT LENGTH('JENNIE') FROM DUAL;

# LENGTH('블랙핑크') -> 4
SELECT LENGTH('블랙핑크') FROM DUAL;

 

  • REPLACE(문자열, 변경 전 문자열, [,변경 후 문자열]) *[]는 옵션
    • 문자열에서 변경 전 문자열을 찾아 변경 후 문자열로 바꿔주는 함수
    • 변경 후 문자열을 명시해주지 않으면 문자열에서 변경 전 문자열을 제거한다.
# REPLACE('블랙핑크제니', '제니', '지수') -> 블랙핑크지수
SELECT REPLACE('블랙핑크제니', '제니', '지수') FROM DUAL;

# REPLACE('블랙핑크제니', '블랙') -> 핑크제니
SELECT REPLACE('블랙핑크제니', '블랙') FROM DUAL;

 

  • LPAD(문자열, 길이, 문자)
    • 문자열이 설정한 길이가 될 때까지 왼쪽을 특정 문자로 채우는 함수
# LPAD('JENNIE', 10, 'V') -> VVVVJENNIE
SELECT LPAD('JENNIE', 10, 'V') FROM DUAL;

 

 

숫자 함수

  • ABS(수)
    • 수의 절댓값을 반환해주는 함수
# ABS(-1) -> 1
SELECT ABS(-1) FROM DUAL;

# ABS(2) -> 2
SELECT ABS(2) FROM DUAL;

 

  • SIGN(수)
    • 수의 부호를 반환해주는 함수
    • 양수이면 1, 음수이면 -1, 0이면 0을 반환한다.
# SIGN(-7) -> -1
SELECT SIGN(-7) FROM DUAL;

# SIGN(7) -> 1
SELECT SIGN(7) FROM DUAL;

 

  • ROUND(수, [,자릿수]) *[]는 옵션
    • 수를 지정된 소수점 자릿수까지 반올림하여 반환해주는 함수
    • 자릿수를 명시하지 않았을 경우 기본값은 0이며 반올림된 정수로 반환한다.
    • 자릿수가 음수일 경우 지정된 정수부를 반올림하여 반환한다.
# ROUND(163.76, 1) -> 163.8
SELECT ROUND(163.76, 1) FROM DUAL;

# ROUND(163.76, -2) -> 200
SELECT ROUND(163.76, -2) FROM DUAL;

 

  • TRUNC(수, [, 자릿수]) *[]는 옵션
    • 수를 지정된 소수점 자릿수까지 버림하여 반환해주는 함수
    • 자릿수를 명시하지 않았을 경우 기본값은 0이며 버림된 정수로 반환한다.
    • 자릿수가 음수일 경우 지정된 정수부에서 버림하여 반환한다.
# TRUNC(54.29, 1) -> 54.2
SELECT TRUNC(54.29, 1) FROM DUAL;

# TRUNC(54.29, -1) -> 50
SELECT TRUNC(54.29, -1) FROM DUAL;

 

  • CEIL(수)
    • 소수점 이하의 수를 올림한 정수를 반환해주는 함수
# CEIL(72.86) -> 73
SELECT CEIL(72.86) FROM DUAL;

# CEIL(-33.4) -> -33
SELECT CEIL(-33.4) FROM DUAL;

 

  • FLOOR(수)
    • 소수점 이하의 수를 버림한 정수를 반환해주는 함수
# FLOOR(22.3) -> 22
SELECT FLOOR(22.3) FROM DUAL;

# FLOOR(-22.3) -> -23
SELECT FLOOR(-22.3) FROM DUAL;

 

  • MOD(수1, 수2)
    • 수1을 수2로 나눈 나머지를 반환해주는 함수
    • 단, 수2가 0일 경우 수1을 반환한다.
# MOD(15, 7) -> 1
SELECT MOD(15, 7) FROM DUAL;

# MOD(15, -4) -> 3
SELECT MOD(15, -4) FROM DUAL;

# MOD(-15, 0) -> -15
SELECT MOD(15, 0) FROM DUAL;

# MOD(-15, -4) -> -3
SELECT MOD(-15, -4) FROM DUAL;

 

 

날짜 함수

  • SYSDATE
    • 현재의 연, 월, 일, 시, 분, 초를 반환해주는 함수
    • nls_data_format에 따라서 sysdate의 출력 양식은 달라질 수 있다.
# SYSDATE -> 2024-11-11 22:08:08
SELECT SYSDATE FROM DUAL;

 

  • EXTRACT(특정 단위 FROM 날짜 데이터)
    • 날짜 데이터에서 특정 단위(YEAR, MONTH, DAY, HOUR, MINUTE, SECOND)만을 출력해서 반환해주는 함수
# EXTRACT(YEAR FROM SYSDATE) -> 2024
# EXTRACT(MONTH FROM SYSDATE) -> 11
# EXTRACT(DAY FROM SYSDATE) -> 11
SELECT EXTRACT(YEAR FROM SYSDATE) AS YEAR,
	   EXTRACT(MONTH FROM SYSDATE) AS MONTH,
       EXTRACT(DAY FROM SYSDATE) AS DAY
  FROM DUAl;

 

  • ADD_MONTHS(날짜 데이터, 특정 개월 수)
    • 날짜 데이터에서 특정 개월 수를 더한 날짜를 반환해주는 함수
    • 날짜의 이전 달이나 다음 달에 기준 날짜의 일자가 존재하지 않으면 해당 월의 마지막 일자가 반환된다.
# ADD_MONTHS(TO_DATE('2021-12-31', 'YYYY-MM-DD'), -1) -> 2021-11-30
# ADD_MONTHS(TO_DATE('2021-12-31', 'YYYY-MM-DD'), 1) -> 2022-01-31

SELECT ADD_MONTHS(TO_DATE('2021-12-31', 'YYYY-MM-DD'), -1) AS PREV_MONTH
	   ADD_MONTHS(TO_DATE('2021-12-31', 'YYYY-MM-DD') 1) AS NEXT_MONTH
  FROM DUAL;

 

변환 함수

명시적 형변환과 암시적 형변환

  • 명시적 형변환
    • 변환 함수를 사용하여 데이터 유형 변환을 명시적으로 나타낸다.
  • 암시적 형변환
    • 데이터베이스가 내부적으로 알아서 데이터 유형을 변환한다.

예를 들어 조건절에서 VARCHAR 유형의 BIRTHDAY 컬럼을 숫자와 비교할 경우, 데이터베이스는 오류를 뱉지 않고 내부적으로 BIRTHDAY 컬럼을 NUMBER 형으로 변환하게 되는데 이럴 때 쓰이는 것이 암시적 형변환이다.

하지만 암시적 형변환이 가능하다고 해서 컬럼의 데이터 유형을 고려하지 않고 SQL 작성을 하게 되면 성능 저하를 불러올 수도 있고 때에 따라서는 에러를 뱉는 경우도 있기 때문에 되도록 명시적 형변환을 사용하는 것이 좋다.

 

명시적 형변환에 쓰이는 함수

  • TO_NUMBER(문자열)
    • 문자열을 숫자형으로 변환해주는 함수
# TO_NUMBER('1234') -> 1234
SELECT TO_NUMBER('1234') FROM DUAL;

# TO_NUMBER('abc') -> Error발생(숫자로 변환할 수 없는 문자열)
SELECT TO_NUMBER('abc') FROM DUAL;

 

  • TO_CHAR(수 or 날짜 [,포맷]) *[]는 옵션
    • 수나 날짜형의 데이터를 포맷 형식의 문자형으로 변환해주는 함수
# TO_CHAR(1234) -> '1234'
SELECT TO_CHAR(1234) FROM DUAL;

# 현재 2021년 9월 21일 22시 33분 21초일 경우
# TO_CHAR(SYSDATE, 'YYYYMMDD HH24MISS') -> 20210921 223321
SELECT TO_CHAR(SYSDATE, 'YYYYMMDD HH24MISS') FROM DUAL;

 

  • TO_DATE(문자열, 포맷)
    • 포맷 형식의 문자형의 데이터를 날짜형으로 변환해주는 함수
포맷 표현 의미
YYYY
MM
DD
HH 시(12)
HH24 시(24)
MI
SS

 

# TO_DATE('20210602', 'YYYYMMDD') -> 2021-06-02
SELECT TO_DATE('20210602', 'YYYYMMDD') FROM DUAL;

 

NULL 관련 함수

  • NVL(인수1, 인수2)
    • 인수1의 값이 NULL일 경우 인수2를 반환하고 NULL이 아닐 경우 인수1을 반환해주는 함수
# NULLIF(REVIEW_SCORE, 0)
# REVIEW_SCORE(컬럼) 데이터가 NULL일 경우 0을 반환하고
# NULL이 아닐 경우 REVIEW_SCORE 값을 반환함

SELECT * FROM REVIEW;

SELECT MEMBER_NO,
       NVL(REVIEW_SCORE, 0) AS REVIEW_SCORE
  FROM REVIEW;

 

  • NULLIF(인수1, 인수2)
    • 인수1과 인수2가 같으면 NULL을 반환하고 같지 않으면 인수1을 반환해주는 함수
# NULLIF(REVIEW_SCORE, 0)
# REVIEW_SCORE(컬럼) 데이터가 0일 경우 NULL을 반환하고
# 0이 아닐 경우 REVIEW_SCORE 값을 반환함

SELECT MEMBER_NO,
       NULLIF(REVIEW_SCORE, 0) AS REVIEW_SCORE,
       REVIEW
  FROM REVIEW
 WHERE PRODUCT_ID = '100001';

 

  • COALESCE(인수1, 인수2, 인수3 ...)
    • NULL이 아닌 최초의 인수를 반환해주는 함수
# COALESCE(PHONE, EMAIL, FAX)
SELECT NAME,
	   COALESCE(PHONE, EMAIL, FAX) AS CONTACT
  FROM MEMBERINFO;

 

CASE

  • 함수와 성격이 같기는 하지만 표현 방식이 함수라기보다는 구문에 가깝다고 할 수 있다.
  • 문장으로는 '~이면 ~이고, ~이면 ~이다'식으로 표현되는 구문이다.
    • 필요에 따라 각 CASE를 여러 개로 늘릴 수도 있다.
  • 같은 기능을 하는 함수로는 Oracle의 DECODE 함수가 있다.
  • CASE 문에서는 ELSE 뒤의 값이 DEFAULT 값이 되고 별도의 ELSE가 없을 경우 NULL 값이 DEFAULT 값이 된다.
# 아래 구문은 모두 같은 결과를 반환함
SELECT SUBWAY_LINE,
	   CASE WHEN SUBWAY_LINE = '1' THEN 'BLUE'
       		WHEN SUBWAY_LINE = '2' THEN 'GREEN'
                WHEN SUBWAY_LINE = '3' THEN 'ORANGE'
                ELSE 'GRAY'
       END AS LINE_COLOR
  FROM SUBWAY_INFO;
  
SELECT SUBWAY_LINE,
	   CASE SUBWAY_LINE
       		WHEN '1' THEN 'BLUE'
                WHEN '2' THEN 'GREEN'
                WHEN '3' THEN 'ORANGE'
                ELSE 'GRAY'
       END AS LINE_COLOR
  FROM SUBWAY_INFO;
  
SELECT SUBWAY_LINE,
       DECODE(SUBWAY_LINE, '1', 'BLUE', '2', 'GREEN', '3', 'ORANGE', 'GRAY') AS LINE_COLOR
  FROM SUBWAY_INFO;

 

 

문제 - 다음 SQL의 결과로 가장 적절한 것은 무엇인가?

COL1 COL2
가나다
2 123
C ABC

 

SELECT CASE 
         WHEN COL1 = 'C' THEN SUBSTR(COL2, 2, 1) 
         WHEN COL1 = '가' THEN 'C' 
         WHEN COL1 = '1' THEN '10' 
         ELSE 'B' 
       END AS RESULT 
FROM SAMPLE;

 

결과

COL1 COL2 결과
가나다 COL = '가' 조건을 만족하므로 RESULT는 'C'
2 123 위의 모든 조건을 만족하지 않으므로 RESULT는 'B'
C ABC COL1 = 'C' 조건을 만족하므로 COL2에서 두 번째 글자 'B'를 가져와 RESULT는 'B'

 

해설

  1. CASE 구문은 COL1의 값에 따라 조건을 확인한다.
  2. 조건이 만족되면 해당 조건의 값을 RESULT로 반환하고, 다음 조건은 확인하지 않는다.
  3. 각 조건은 아래와 같다.
    • COL1이 'C'일 때 : COL2에서 두 번째 위치의 한 글자를 가져온다. (SUBSTR(COL2, 2, 1))
    • COL1이 '가'일 때 : RESULT로 'C'를 반환한다.
    • COL1이 '1'일 때 : RESULT로 '10'을 반환한다.
    • 위의 조건을 모두 만족하지 않으면 RESULT로 'B'를 반환한다.

WHERE 절

INSERT를 제외한 DML문을 수행할 때 원하는 데이터만 골라 수행할 수 있도록 해주는 구문

 

SELECT *(컬럼명)
  FROM ENTERTAINER(테이블이름)
 WHERE NAME = '이지은'; (조건절)
 
SELECT *
  FROM ENTERTAINER
 WHERE NAME <> '이지은'; (이지은이 아닌 것만)
 
# UPDATE 테이블명 SET 컬럼명 = 새로운 데이터 WHERE 조건절;
UPDATE ENTERTAINER SET AGENCY_NAME = '빅히트뮤직' WHERE NAME = '김태형';

# DELETE FROM 테이블명 WHERE 조건절;
DELETE FROM ENTERTAINER WHERE JOB = '배우';

 

비교 연산자

연산자 의미 예시
= 같음 where col = 10
< 작음 where col < 10
<= 작거나 같음 where col <= 10
> where col > 10
>= 크거나 같음 where col >= 10
# CITY가 Paris인 행 조회
SELECT FIRST_NAME, LAST_NAME, CITY
  FROM MEMBER
 WHERE CITY = 'Paris';
 
 # MEMBER_NO이 10보다 작은 행 조회
 SELECT MEMBER_NO, FIRST,NAME, LAST_NAME
   FROM MEMBER
  WHERE MEMBER_NO < 10;
# FIRST_NAME과 Mark의 데이터 타입이 맞지 않아 에러 발생
SELECT FIRST_NAME, LAST_NAME, EMAIL
  FROM MEMBER
 WHERE FIRST_NAME = Mark;

 

부정 비교 연산자

연산자 의미 예시
!= 같지 않음 where col != 10
^= 같지 않음 where col ^= 10
<> 같지 않음 where col <> 10
not 컬럼명 = 같지 않음 where not col = 10
not 컬럼명 > 크지 않음 where not col > 10

 

# FAVORITES이 Y가 아닌 행 조회
SELECT PLAY_ID, NAME, FAVORITES
  FROM PLAY_LIST
 WHERE FAVORITES <> 'Y';

 

SQL 연산자

연산자 의미 예시
BETWEEN A AND B A와 B의 사이 (A, B 포함) where col between 1 and 10
LIKE '비교 문자열' 비교 문자열을 포함
'%'는 문자열을 의미
'_'는 하나의 문자를 의미
'_' 혹은 '%' 기호가 포함된 문자 검색 시
ESCAPE 지정
where col like '방탄%'
where col like '%소년단'
where col like '%탄소년%'
where col like '방_소%'
where col like '%#%%' escape '#'
IN (LIST) LIST 중 하나와 일치 where col in (1, 3, 5)
IS NULL NULL 값 where col is null
# PLAY_ID가 1 이상, 5 이하인 행 조회
SELECT PLAY_ID, NAME, FAVORITES
  FROM PLAY_LIST
 WHERE PLAY_ID BETWEN 1 AND 5;
 # 같은 표현
 WHERE PALY_ID >= 1 AND PLAY_ID <= 5;
 
# NAME이 Classical로 시작되는 행 조회
SELECT PLAY_ID, NAME, FAVORITES
  FROM PLAY_LIST
 WHERE NAME LIKE 'Classical%';
 
# NAME이 M으로 시작하고 S로 끝나는 행 조회
SELECT PLAY_ID, NAME, FAVORITES
  FROM PLAY_LIST
 WHERE NAME LIKE 'M%S';
 
# TITLE이 IT Staff이거나 IT Manager인 행 조회
SELECT LAST_NAME, FIRST_NAME, TITLE
  FROM EMPLOYEE
 WHERE TITLE IN ('IT Staff', 'IT Manager');
 # 다른 표현
 WHERE (TITLE = 'IT Staff' OR TITLE = 'IT Manager');

 

 

부정 SQL 연산자

연산자 의미 예시
NOT BETWEEN A AND B A와 B의 사이가 아님(A, B 미포함) where col not between 1 and 10
NOT IN (LIST) LIST 중 일치하는 것이 없음 where col not in (1, 3, 5)
IS NOT NULL NULL 값이 아님 where col is not null
SELECT PLAY_ID, NAME, FAVORITES
  FROM PLAY_LIST
 WHERE PLAY_ID NOT BETWEEN 1 AND 5;
 # 같은 결과 다른 표현
 WHERE NOT (PLAY_ID BETWEEN 1 AND 5);
 WHERE NOT (PLAY_ID >= 1 AND PLAY_ID <= 5);
 WHERE PALY_ID < 1 OR PLAY_ID > 5;
 
# 리스트에 없는 거 조회
WHERE TITLE NOT IN ('A', 'B');
WHERE NOT (TITLE IN ('A', 'B'));
WHERE NOT (TITLE = 'A' OR TITLE = 'B');
WHERE (TITLE <> 'A' AND TITLE <> 'B');

 

논리 연산자

연산자 의미 예시
AND 모든 조건이 TRUE여야 함 where col > 1 and col < 10
OR 하나 이상의 조건이 TRUE여야 함 where col = 1 or col = 10
NOT TRUE면 FALSE이고 FALSE이면 TRUE where not col > 10

 

논리 연산자에는 처리 순서가 존재하는데 SQL에 명시된 순서와는 관계없이 ( ) → NOT → AND → OR 순으로 처리된다.

# TITLE이 A거나 CITY가 B가 아닌 행 조회
SELECT LAST_NAME, FIRST_NAME, TITLE, CITY
  FROM EMPLOYEE
 WHERE NOT (TITLE = 'A' OR CITY = 'B');
 # 다른 표현
 WHERE TITLE <> 'A' AND CITY <> 'B';

GROUP BY, HAVING 절

GROUP BY

데이터를 그룹별로 묶을 수 있도록 해주는 절

 

GROUP 뒤에 수단의 전치사인 BY가 붙었기 때문에 GROUP BY 뒤에는 그룹핑의 기준이 되는 컬럼이 오게 된다.

컬럼은 하나가 될 수도 있고 그 이상이 될 수도 있다.

 

집계 함수

데이터를 그룹별로 나누면 그룹별로 집계 데이터를 도출하는 것이 가능해진다.

  • COUNT(*)
    • 전체 ROW를 Count하여 반환
  • COUNT(컬럼)
    • 컬럽값이 Null인 Row를 제외하고 Count하여 반환
  • COUNT(DISTINCT 컬럼)
    • 컬럼값이 Null이 아닌 Row에서 중복을 제거한 Count를 반환
  • SUM(컬럼)
    • 컬럼값들의 합계를 반환 (NULL 제외)
  • AVG(컬럼)
    • 컬럼값들의 평균을 반환 (NULL 제외)
  • MIN(컬럼)
    • 컬럼값들의 최솟값을 반환
  • MAX(컬럼)
    • 컬럼값들의 최댓값을 반환

 

HAVING

GROUP BY 절을 사용할 때 WHERE 절처럼 사용하는 조건절

 

주로 데이터를 그룹핑한 후 특정 그룹을 골라낼 때 사용한다.

# SELECT문의 논리적 수행 순서
SELECT	 # 5
FROM	 # 1
WHERE	 # 2
GROUP BY # 3
HAVING   # 4
ORDER BY # 6

 

HAVING 절은 논리적으로 GROUP BY 절 이후에 수행되기 때문에 그룹핑 후에 가능한 집계 함수로 조건을 부여할 수 있다.

예를 들어 2021년 7월 한 달 동안의 판매 데이터를 상품 코드로 그룹핑해서 COUNT를 구하면 상품별 판매량이 나오는데

HAVING 절을 이용하면 한 달간 1000개 이상 팔린 상품만 출력할 수 있게 되는 것이다.

 

# GROUP BY를 통해 상품별로 묶고 HAVING을 통해 상품별로 조건 확인
SELECT PRODUCT_CODE,
	   COUNT (ORDER_CNT) AS ORDER_CNT
  FROM ORDER_PRODUCT
 WHERE ORDER_DATE BETWEEN '20210701' AND '20210731'
 GROUP BY PRODUCT_CODE
HAVING COUNT(ORDER_CNT) >= 1000;

 

또한 HAVING 절은 논리적으로도 SELECT 절 전에 수행되기 때문에 SELECT 절에 명시되지 않은 집계 함수로도 조건을 부여할 수 있다.

주의할 점은 WHERE 절을 사용해도 되는 조건까지 HAVING 절로 써버리면 성능상 불리할 수 있다는 것이다. (수행 시 오류가 나지는 않는다.)

왜냐하면 WHERE 절에서 필터링 선행되어야 GROUP BY를 할 데이터량이 줄어들기 때문이다.

GROUP BY는 비교적 많은 비용이 드는 작업이므로 수행 전에 데이터량을 최소로 줄여놓는 것이 바람직하다.


ORDER BY 절

SELECT 문에서 논리적으로 맨 마지막에 수행된다.

ORDER BY 절을 사용하여 SELECT한 데이터를 정렬할 수 있으며 ORDER BY 절을 따로 명시하지 않으면 데이터는 임의의 순서대로 출력된다.

ORDER BY 절 뒤에는 정렬의 기준이 되는 컬럼이 오게 되는데 컬럼은 하나가 될 수도 있고 그 이상이 될 수도 있다.

또한 ORDER BY 절 뒤에 오는 컬럼에는 옵션이 붙을 수 있으며 종류는 다음과 같다.

  • ASC(Ascending) : 오름차순
  • DESC(Descending) : 내림차순
  • 옵션 생략 시 ASC가 기본값
SELECT NAME, MEMBER_NO FROM MEMBERINFO ORDER BY NAME;
SELECT NAME, MEMBER_NO FROM MEMBERINFO ORDER BY NAME DESC;

# 등급인 내림차순, 이름은 올림차순으로 정렬
SELECT GRADE, NAME, MEMBER_NO
  FROM MEMBERINFO
 ORDER BY GRADE DESC, NAME;

 

논리적으로 SELECT 절 다음에 수행되기 때문에 SELECT 절에서 정의한 ALIAS를 사용할 수 있다.

정렬의 기준이 되는 컬럼에 NULL 데이터가 포함되어 있을 경우 데이터베이스 종류에 따라 정렬의 위치가 달라지는데

Oracle의 경우에는 NULL을 최댓값으로 취급하기 때문에 오름차순을 했을 경우 맨 마지막에 위치하게 된다. (SQL Server는 반대.)

만약 순서를 변경하고 싶다면 ORDER BY 절에 NULLS FIRST, NULLS LAST 옵션을 써서 NULL의 정렬상 순서를 변경할 수 있다.


JOIN

각기 다른 테이블을 한 번에 보여줄 때 쓰는 쿼리

 

실무에서 SQL을 작성할 때 8할이 JOIN 쿼리라고 얘기해도 무방할 정도로 많이 쓰이는 쿼리이다.

 

EQUI JOIN

Equal(=) 조건으로  JOIN하는 것으로 가장 흔히 볼 수 있는 JOIN 방식
SELECT A.CODE
       B.NAME
       B.MEMBER_ID
  FROM PRODUCT A,
       PRODUCT_REVIEW B
 WHERE A.PRODUC_CODE = B.PRODUCT_CODE;
       # AND A.PRODUCT_CODE = '1001' 추가하면 1001상품코드만 조회

 

Non EQUI JOIN

Equal(=) 조건이 아닌 다른 조건(BETWEEN, >, >=, <, <=)으로 JOIN하는 방식
SELECT A.EVENT_NAME,
       B.MEMBER+ID,
       B.CONTENT,
       B.FEG_DATE
  FROM EVENT A,
       PRODUCT_REVIEW B
 WHERE B.REG_DATE BETWEEN A.START_DATE AND A.END_DATE;

 

3개 이상 TABLE JOIN

JOIN 쿼리를 좀 더 확장해서 3개 이상의 테이블을 JOIN할 수도 있다.

 

SELECT A.PRODUCT_NAME,
       B.MEMBER_ID,
       B.CONTENT,
       C.EVENT_NAME
  FROM PRODUCT A,
       PRODUCT_REVIEW B,
       EVENT C
 WHERE A.PRODUCT_CODE = B.PRODUCT_CODE
   AND B.REG_DATA BETWEEN C.START_DATE AND C.END_DATE;

 

OUTER JOIN

JOIN 조건에 만족하지 않는 행들도 출력되는 형태

  • LEFT OUTER JOIN
    • LEFT TABLE과 RIGHT TABLE의 데이터 중 JOIN에 성공하지 못한 나머지 LEFT TABLE의 데이터가 함께 출력
SELECT ~
  FROM ~
 WHERE A.PRODUCT_CODE = B.PRODUCT_CODE(+);
 # A테이블 모두 조회됨

 

STANDARD JOIN

벤더마다 SQL 문법에 차이가 너무 클 경우 호환성 이슈가 발생하고 SQL을 사용하는 사람들 입장에서도 효율성이 떨어지기 때문에 표준이 되는 ANSI SQL을 지정하게 되었다.

STANDARD JOIN은 ANSI SQL 중 하나로 쉽게 말해 Oracle에서도 돌아가고 MySQL에서도 돌아가는 JOIN 쿼리라고 생각하면 된다.

STANDARD JOIN보다는 ANSI JOIN, 표준 조인이라는 말이 많이 쓰인다.

 

INNER JOIN

JOIN 조건에 충족하는 데이터만 출력되는 방식

 

 

앞서 본 SQL과의 차이점은 JOIN 조건을 ON 절을 사용하여 작성해야 한다는 점이다.

SELECT * FROM PRODUCT;
SELECT * FROM PRODUCT_REVIEW;

SELECT A.PRODUCT_CODE,
       A.PRODUCT_NAME,
       B.MEMBER_ID,
       B.CONTENT,
       B.REG_DATE
  FROM PRODUCT A INNER JOIN PRODUCT_REVIEW B
    ON A.PRODUCT_CODE = B.PRODUCT_CODE;

 

OUTER JOIN

JOIN 조건에 충족하는 데이터가 아니어도 출력될 수 있는 방식

 

  • LEFT OUTER JOIN
    • SQL에서 왼쪽에 표기된 테이블의 데이터는 무조건 출력되는 JOIN
    • 오른쪽 테이블에 JOIN되는 데이터가 있든지 말든지 일단 나는 출력되고 본다는 식이라고 이해하면 쉽다.
    • 대신 오른쪽 테이블에 JOIN되는 데이터가 없는 Row들은 오른쪽 테이블 컬럼의 값이 NULL로 출력된다.

 

SELECT ~
  FROM PRODUCT A LEFT OUTER JOIN PRODUCT_REVIEW B
    ON A.PRODUCT_CODE = B.PRODUCT_CODE;

 

  • RIGHT OUTER JOIN
    • SQL에서 오른쪽에 표기된 테이블의 데이터는 무조건 출력되는 방식
    • 왼쪽 테이블에 JOIN되는 데이터가 있든지 말든지 일단 나는 출력되고 본다는 식이라고 이해하면 쉽다.
    • 대신 왼쪽 테이블에 JOIN되는 데이터가 없는 Row들은 왼쪽 테이블 컬럼의 값이 NULL로 출력된다.

SELECT ~
  FROM PRODUCT_REVIEW A RIGHT OUTER JOIN PRODUCT B
    ON A.PRODUCT_CODE = B.PRODUCT_CODE;

 

  • FULL OUTER JOIN
    • 왼쪽, 오른쪽 테이블의 데이터가 모두 출력되는 방식
    • LEFT OUTER JOIN과 RIGHT OUTER JOIN의 합집합이라고 이해하면 쉽다. (단, 중복값은 제거된다.)

SELECT A.CAST AS R_CAST,
       B.CAST AS I_CAST
  FROM RUNNING_MAN A FULL OUTER JOIN INFINITE_CHALLENGE B
    ON A.CAST = B.CAST;

 

NATURAL JOIN

A 테이블과 B 테이블에서 같은 이름을 가진 컬럼들이 모두 동일한 데이터를 가지고 있을 경우 JOIN이 되는 방식
(*SQL Server(MSSQL)에서는 지원되지 않는다.)
SELECT *
  FROM RUNNING_MAN A NATURAL JOIN INFINITE_CHALLEGE B;
  # Oracle에서는 USING 조건절을 이용
  # 같은 이름을 가진 컬럼 중 원하는 컬럼만 JOIN에 이용할 수 있음
  # 단, SELECT절에서 USING 절로 정의된 컬럼 앞에는 ALIAS 같은거 안 됨
  # USING (CAST, GENDER);

 

CROSS JOIN(Cartesian Product)

A 테이블과 B 테이블 사이에 JOIN 조건이 없는 경우, 조합할 수 있는 모든 경우를 출력하는 방식

SELECT ~
  FROM ENTERTAINER A CROSS JOIN DRINK B;

 

'certification > SQLD' 카테고리의 다른 글

[SQLD] 데이터 모델과 SQL  (2) 2024.11.11
[SQLD] 데이터 모델링의 이해  (0) 2024.10.28