📗 self-study/📗 KH정보교육원 당산지원

[Oracle] 02_DML(SELECT)_함수(Function)

천재강쥐 2022. 8. 24. 10:45

02_DML(SELECT)_함수(Function)

 

 함수(Function)

: 자바에서의 메소드와 같은 역할이라고 생각하면 됨

: 매개변수로 전달된 값들을 읽어서 내부적으로 계산한 결과를 리턴

:  하나의 큰 프로그램에서 반복적으로 사용되는 부분들을 분리하여 작성해 놓은 프로그램

: 호출하며 값을 전달하면 결과를 리턴하는 방식으로 사용

 

함수의 유형

단일 행 함수: 한 줄씩, 한 행씩 결과물이 있음(매개변수가 n개라면 리턴값도 n개)

=> 매 행마다 함수 실행 후 매 행에 대한 결과를 모두 반환

그룹 함수: 한 뭉텅이를 전달하면 결과값이 1개로 축약되어 반환됨

=> 모든 행에 대해 하나의 그룹으로 묶어서 함수 실행 후 결과를 하나로 반환

 

    *주의할 점: 단일행 함수와 그룹 함수는 함께 사용할 수 없음
                    애초에 결과의 행 개수가 다르게 나오기 때문!

 

   <단일행 함수>
    
    <문자열과 관련된 함수>


    <LENGTH / LENGTHB>
    
    - LENGTH(STR): 해당 전달된 문자열의 글자 수 반환
    - LENGTHB(STR): 해당 전달된 문자열의 바이트 수 반환
    
    결과값은 NUMBER 타입으로 반환
    STR: '문자열' 리터럴/문자열 타입의 컬럼명
    
    오라클에서의 문자 사이즈
    숫자, 영문, 특수문자: '!', '~', 'A', 'a', '1', ... => 한 글자당 1BYTE로 취급
    한글: 'ㄱ', '기', '김', 'ㅐ', ... => 한 글자당 3BYTE로 취급

SELECT LENGTH('오라클!'), LENGTHB('오라클!')
FROM DUAL; -- DUAL 테이블 == 가상 테이블 == DUMMY TABLE
                   -- 산술연산이나 가상 컬럼 등의 값을 단 한 번만 출력하고 싶을 때 FROM절에 작성하는 테이블명
                   
SELECT EMAIL
        ,  LENGTH(EMAIL)
         , LENGTHB(EMAIL)
         , EMP_NAME
         , LENGTH(EMP_NAME)
         , LENGTHB(EMP_NAME)
FROM EMPLOYEE;

 

 <INSTR>
    
    - INSTR(STR): 문자열로부터 특정 문자의 위치값을 반환
    
    [ 표현법 ]
    INSTR(STR, '특정문자', 찾을위치의시작값(생략가능), 순번(생략가능))
    
    결과값은 NUMBER 타입으로 반환
    찾을 위치의 시작값과 순번은 생략 가능
    
    찾을 위치의 시작값
     n : 앞에서 n번째에서부터 찾겠다 (생략 시 기본값 1)
    -n : 뒤에서 n번째에서부터 찾겠다
    => ex) 3 기재 시 3번째(포함)부터 찾음
    => -n을 기재하더라도 앞에서부터 세서 돌려 주기 때문에 이 구문은 "찾을 범위"를 지정해 놓는 거라고 생각하면 쉬울 듯

SELECT INSTR('AABAACAABBAA', 'B')
FROM DUAL; -- 3: 찾고자 하는 위치, 몇 번째 해당 문자를 찾을 것인지에 대한 순번을 명시하지 않았음에도
                   --    기본적으로 앞에서부터 첫 번째 글자의 위치를 찾아 주고 있음
                   
SELECT INSTR('AABAACAABBAA', 'B', 1)
FROM DUAL; -- 3: 앞에서부터 첫 번째에 위치하는 B의 위치값을 알려 주겠음 (1부터 세기 시작함)

SELECT INSTR('AABAACAABBAA', 'B', -1)
FROM DUAL; -- 10: 뒤에서부터 첫 번째에 위치하는 B의 위치값을 알려 주겠음 (앞에서부터 세서 돌려줌)

SELECT INSTR('AABAACAABBAA', 'B', 1, 2)
FROM DUAL; -- 9: 앞에서부터 두 번째에 위치하는 B의 위치값을 알려 주겠음

SELECT INSTR('AABAACAABBAA', 'B', -1, 2)
FROM DUAL; -- 9: 뒤에서부터 두 번째에 위치하는 B의 위치값을 알려 주겠음

SELECT INSTR('AABAACAABBAA', 'E', 1, 1)
FROM DUAL; -- 0: 내가 찾고자 하는 값이 없을 경우 0을 반환해 줌

-- 실제 테이블에 적용해 보기
-- EMAIL에서 @의 위치를 찾아보자
SELECT EMAIL, INSTR(EMAIL, '@') AS "@의 위치"
FROM EMPLOYEE;

 

   <SUBSTR>
    
    - SUBSTR(STR, POSITION, LENGTH): 문자열로부터 특정 문자열을 추출해서 반환
                                                      (자바로 치면 문자열.substring() 메소드와 유사)
                                                      
    결과값은 CHARACTER 타입으로 반환(문자열 형태)
    LENGTH는 생략 가능
 
    - STR: '문자열' 리터럴 / 문자열 타입 컬럼명
    - POSITION: 문자열을 추출할 시작 위치값 (음수도 제시 가능), POSITION번재 위치부터 추출
    - LENGTH: 추출할 문자의 개수 (생략 시 끝까지 추출)

SELECT SUBSTR('SHOWMETHEMONEY', 7)
FROM DUAL; -- THEMONEY: 시작 위치는 1부터 시작해서 셈

SELECT SUBSTR('SHOWMETHEMONEY', 5, 2)
FROM DUAL; -- ME

SELECT SUBSTR('SHOWMETHEMONEY', 1, 6)
FROM DUAL; -- SHOWME

SELECT SUBSTR('SHOWMETHEMONEY', -8, 3)
FROM DUAL; -- THE: 시작 위치가 음수일 경우 뒤에서부터 N번째 위치로부터 문자를 추출하겠다는 뜻

-- 주민번호에서 성별 부분을 추출해서 남자(1), 여자(2)를 체크
SELECT EMP_NAME, EMP_NO, SUBSTR(EMP_NO, 8, 1) AS "성별"
FROM EMPLOYEE;

-- 남자 사원들의 사원명, 급여 조회
SELECT EMP_NAME, SALARY
FROM EMPLOYEE
-- WHERE SUBSTR(EMP_NO, 8, 1) = '1' OR SUBSTR(EMP_NO, 8, 1) = '3'; 
WHERE SUBSTR(EMP_NO, 8, 1) IN ('1', '3');

-- 여자 사원들만 조회
SELECT EMP_NAME, SALARY
FROM EMPLOYEE
-- WHERE SUBSTR(EMP_NO, 8, 1) IN ('2', '4');
WHERE SUBSTR(EMP_NO, 8, 1) NOT IN ('1', '3');

-- EMAIL에서 ID부분만 추출해서 조회
SELECT EMP_NAME, EMAIL, SUBSTR(EMAIL, 1, INSTR(EMAIL, '@') - 1) "ID"
FROM EMPLOYEE;

 

  <LPAD / RPAD>
    - LPAD/RPAD(STR, 최종적으로반환할문자의길이(바이트), 덧붙이고자하는문자)
    : 제시한 문자열에 임의의 문자열을 왼쪽(LPAD) 또는 오른쪽(RPAD)에 덧붙여서 최종 N 길이만큼의 문자열을 반환
    
    결과값은 CHARACTER 타입으로 반환
    덧붙이고자 하는 문자는 생략 가능
    
    - STR: '문자열' 리터럴 / 문자열 타입의 컬럼명

SELECT LPAD(EMAIL, 16, ' ') -- 덧붙이고자 하는 문자 생략 시 기본값이 공백
FROM EMPLOYEE;

SELECT RPAD(EMAIL, 20, '#') -- 덧붙이고자 하는 문자 생략 시 기본값이 공백
FROM EMPLOYEE; -- bang_ms@kh.or.kr####

-- 850918-2******* 주민등록번호 조회 => 뒷 6자리가 노출되지 않도록 마스킹 후 보여 줄 것
-- 1단계. SUBSTR을 이용해서 주민번호 앞 8자리까지 추출
SELECT EMP_NAME, SUBSTR(EMP_NO, 1, 8)
FROM EMPLOYEE;

-- 2단계. RPAD를 중첩하여 주민번호 뒤 6자리에 *을 붙이기
SELECT EMP_NAME, RPAD(SUBSTR(EMP_NO, 1, 8), 14, '*')
FROM EMPLOYEE;

 

   <LTRIM / RTRIM>
    
    - LTRIM / RTRIM(STR, 제거시키고자하는문자)
    : 문자열의 왼쪽 또는 오른쪽에서 제거시키고자 하는 문자들을 찾아서 제거한 나머지 문자열을 반환
    
    결과값은 CHARACTER 타입으로 반환
    제거시키고자 하는 문자는 생략 가능 (생략 시 기본값은 공백으로 설정됨)
    
    - STR: '문자열' 리터럴 / 문자열 타입 컬럼명

SELECT LTRIM('        K      H')
FROM DUAL; -- K      H

SELECT RTRIM('        K      H       ')
FROM DUAL; --         K      H

SELECT LTRIM('0001230456000', '0')
FROM DUAL; --1230456000

SELECT LTRIM('123123KH123', '123')
FROM DUAL; --KH123

SELECT LTRIM('ACABACCKH', 'ABC')
FROM DUAL; --KH
--> 제거시키고자 하는 문자열을 통으로 지워 주는 게 아니라 문자 하나하나로 비교해서 존재하면 다 지워 주는 원리

 

    <TRIM>
    
    - TRIM(BOTH/LEADING/TRAILING '제거시키고자하는문자' FROM STR)
    : 문자열의 양쪽/앞쪽/뒤쪽에 있는 특정 문자를 제거한 나머지 문자열을 반환
    
    결과값은 CHARACTER 타입으로 반환
    BOTH/LEADING/TRAILING은 생략 가능! 단, 생략 시 기본값 BOTH
    
    - STR: '문자열' 리터럴 / 문자열 타입 컬럼명

-- 참고: TRIM은 가운데에 있는 문자는 제거 못함 (REPLACE 활용)
SELECT TRIM('      K              H       ') -- BOTH ' ' FROM '      K              H       '과 같은 의미
FROM DUAL; -- K              H

SELECT TRIM('Z' FROM 'ZZZKHZZZ')
FROM DUAL; -- KH

SELECT TRIM(LEADING 'Z' FROM 'ZZZKHZZZ') --- LEADING 앞쪽 제거( == LTRIM)
FROM DUAL; -- KHZZZ

SELECT TRIM(TRAILING 'Z' FROM 'ZZZKHZZZ') --- LEADING 앞쪽 제거( == LTRIM)
FROM DUAL; -- ZZZKH

 

  <LOWER / UPPER / INITCAP>
    
    - LOWER(STR): 다 소문자로 변경
    - UPPER(STR): 다 대문자로 변셩
    - INITCAP(STR): 띄어쓰기 기준으로 각 단어 앞글자만 대문자로 변경
    
    결과값은 CHARACTER 타입으로 변경
    
    - STR: '문자열' 리터럴 / 문자열 타입 컬럼명

SELECT LOWER('Welcome To My World!')
FROM DUAL;

SELECT UPPER('Welcome To My World!')
FROM DUAL;

SELECT INITCAP('Welcome To My World!')
FROM DUAL;

 

    <CONCAT>
    
    - CONCAT(STR1, STR2)
    : 전달된 두 개의 문자열을 하나로 합친 결과를 반환
    
    결과값은 CHARACTER 타입으로 반환
    
    - STR1, STR2: '문자열' 리터럴 / 문자열 타입 컬럼

SELECT CONCAT('가나다', 'ABC')
FROM DUAL; -- 가나다ABC

SELECT '가나다' || 'ABC'
FROM DUAL; -- 가나다ABC

SELECT CONCAT('가나다', 'ABC', '123')
FROM DUAL;
--> 오류: CONCAT 함수는 한 번에 두 개의 문자열만 합치는 것이 가능

SELECT CONCAT(CONCAT('가나다', 'ABC'), '123')
FROM DUAL; -- 가나다ABC123
--> 만약 여러 개의 문자열을 합칠 때 CONCAT만 이용해야 한다면 중첩해서 사용 가능

 

  <REPLACE>
    
    - REPLACE(STR, 찾을문자, 바꿀문자)
    : STR로부터 찾을 문자를 찾아서 바꿀 문자로 바꾼 문자열을 반환
    
    결과값은 CHARACTER 타입으로 반환
    
    - STR: '문자열' 리터럴 / 문자열 타입 컬럼

SELECT REPLACE('서울시 강남구 역삼동', '역삼동', '삼성동')
FROM DUAL; -- 서울시 강남구 삼성동

SELECT EMP_NAME, EMAIL, REPLACE(EMAIL, 'kh.or.kr', 'ioi.or.kr')
FROM EMPLOYEE;

-- 참고: TRIM은 가운데에 있는 문자는 제거 못함 (REPLACE 활용)
SELECT REPLACE('      K              H       ', ' ', '') 
FROM DUAL; -- KH

 

 <숫자와 관련된 함수>

    
    <ABS>
    
    - ABS(NUMBER): 절대값을 구해 주는 함수

SELECT ABS(-10)
FROM DUAL; -- 10

SELECT ABS(-10.9)
FROM DUAL; -- 10.9

 

 <MOD>
    - MODULER의 약자로 나머지를 구해 주는 함수
    
    - MOD(NUMBER1, NUMBER2): 두 수를 나눈 나머지 값을 반환해 주는 함수

SELECT MOD(10, 3)
FROM DUAL; -- 1

SELECT MOD(-10, 3)
FROM DUAL; -- -1

SELECT MOD(-10.9, 3)
FROM DUAL; -- -1.9

 

  <ROUND>
    - ROUND(NUMBER, 위치값): 반올림 처리해 주는 함수
    
    위치: 소수점 아래 N번째 수에서 반올림
    위치값은 생략 가능! 단, 위치 생략 시 기본값은 0, 위치값은 음수로도 제시 가능

SELECT ROUND(123.456)
FROM DUAL; -- 123 

SELECT ROUND(123.456, 1)
FROM DUAL; -- 123.5

SELECT ROUND(123.456, 2)
FROM DUAL; -- 123.46

SELECT ROUND(123.456, 3)
FROM DUAL; -- 123.456, 3번째까지 나타내야 하므로 본연의 값 그대로

SELECT ROUND(123.456, 4)
FROM DUAL; -- 123.456, 연산할 위치가 없으므로 본연의 값 그대로

SELECT ROUND(123.456, -1)
FROM DUAL; -- 120

SELECT ROUND(123.456, -2)
FROM DUAL; -- 100

 

 <CEIL>
    
    - CEIL(NUMBER): 소수점 아래의 수를 무조건 올림 처리 해 주는 함수
    
    - 위치값 지정 불가!

SELECT CEIL(123.156)
FROM DUAL; -- 124

SELECT CEIL(249.012)
FROM DUAL; -- 250

 

 <FLOOR>
    
    - FLOOR(NUMBER): 소수점 아래의 수를 무조건 버림 처리 해 주는 함수
    
    - 위치값 지정 불가!

SELECT FLOOR(123.956)
FROM DUAL; -- 123

SELECT FLOOR(207.68)
FROM DUAL; -- 207

-- 각 직원별로 고용일로부터 오늘까지 근무일수를 조회
-- 근무일수 = 오늘날짜 - 고용일
SELECT EMP_NAME, CONCAT(FLOOR(SYSDATE - HIRE_DATE), '일') AS "근무일수"
FROM EMPLOYEE;

 

  <TRUNC>
    
    - TRUNC(NUMBER, 위치값): 위치 지정 가능한 버림 처리를 해 주는 함수
    
    위치값 생략 가능! 단, 생략 시 기본값은 0 (== FLOOR 함수와 같은 역할)

SELECT TRUNC(123.756)
FROM DUAL; -- 123

SELECT TRUNC(123.756, 1)
FROM DUAL; -- 123.7

SELECT TRUNC(123.756, -1)
FROM DUAL; -- 120

 

 <날짜 관련 함수>    


    DATE 타입을 다루는 함수들
    DATE타입: "년, 월, 일, 시, 분, 초"를 다 포함한 자료형

-- SYSDATE: 현재 이 DB가 깔려 있는 컴퓨터 시스템 설정상의 날짜
SELECT SYSDATE
FROM DUAL; -- 22/08/24 형식으로 출력(날짜로써)

SELECT '22/08/24'
FROM DUAL; -- 22/08/24 형식으로 출력(문자열로써)

-- MONTHS_BETWEEN(DATE1, DATE2): 두 날짜 사이의 개월 수를 반환해 주는 함수
-- 각 직원별로 고용일로부터 오늘까지의 근무 일 수와 근무 개월 수를 조회
SELECT EMP_NAME
         , FLOOR(SYSDATE - HIRE_DATE) || '일' AS "근무일수"
         , FLOOR(MONTHS_BETWEEN(SYSDATE, HIRE_DATE)) || '개월' AS "근무개월수"
FROM EMPLOYEE;
--> 두 날짜 사이의 개월 수를 숫자 타입으로 반환해 주되,
--   첫 번째 매개변수에 해당되는 DATE1 부분이 더 과거일 경우 음수로 결과가 나옴!

-- ADD_MONTHS(DATE, NUMBER): 특정 날짜에 해당 숫자만큼의 개월 수를 더한 날짜를 반환해 주는 메소드 (DATE 타입 반환)
-- 오늘 날짜로부터 5개월 후
SELECT ADD_MONTHS(SYSDATE, 5)
FROM DUAL;

-- 오늘 날짜로부터 1개월 전(음수 제시 가능)
SELECT ADD_MONTHS(SYSDATE, -1)
FROM DUAL;

-- 전체 사원들의 직원명, 입사일, 입사 후 6개월이 흘렀을 때의 날짜
SELECT EMP_NAME, HIRE_DATE, ADD_MONTHS(HIRE_DATE, 6) AS "입사 후 6개월"
FROM EMPLOYEE;

-- NEXT_DAY(DATE, 요일(문자/숫자)): 특정 날짜에서 가장 가까운 해당 요일을 찾아서 그 날짜를 반환
SELECT NEXT_DAY(SYSDATE, '일요일')
FROM DUAL; -- 22/08/28

SELECT NEXT_DAY(SYSDATE, '일')
FROM DUAL; -- 22/08/28

-- 1: 일요일, 2: 월요일, 3: 화요일, ... , 6: 금요일, 7: 토요일
SELECT NEXT_DAY(SYSDATE, 1)
FROM DUAL; -- 22/08/28

-- 현재 이 시스템에 설정된 언어가 KOREAN이기 때문에 오류가 발생
SELECT NEXT_DAY(SYSDATE, 'SUNDAY')
FROM DUAL;

-- 언어 변경
ALTER SESSION SET NLS_LANGUAGE = AMERICAN;

SELECT NEXT_DAY(SYSDATE, 'SUNDAY')
FROM DUAL;

SELECT NEXT_DAY(SYSDATE, '일')
FROM DUAL; -- 22/08/28

ALTER SESSION SET NLS_LANGUAGE = KOREAN;

SELECT NEXT_DAY(SYSDATE, '일')
FROM DUAL; -- 22/08/28

-- LAST_DAY(DATE): 해당 특정 날짜 달의 마지막 날짜를 구해서 반환해 주는 함수 (DATE 타입 반환)
SELECT LAST_DAY(SYSDATE)
FROM DUAL;

-- 직원명, 입사일, 입사한 달의 마지막 날짜 조회
SELECT EMP_NAME, HIRE_DATE, LAST_DAY(HIRE_DATE)
FROM EMPLOYEE;

 

<EXTRACT>

: 년도 또는 월 또는 일 정보만 추출해서 변환(NUMBER 타입 반환)
    
    - EXTRACT(YEAR FROM DATE): 특정 날짜로부터 년도만 추출
    - EXTRACT(MONTH FROM DATE): 특정 날짜로부터 월만 추출
    - EXTRACT(DAY FROM DATE): 특정 날짜로부터 일만 추출

SELECT EXTRACT(YEAR FROM SYSDATE) -- 오늘 날짜 기준 년도 추출
          , EXTRACT(MONTH FROM SYSDATE) -- 오늘 날짜 기준 월 추출
          , EXTRACT(DAY FROM SYSDATE) -- 오늘 날짜 기준 일 추출
FROM DUAL;

-- 사원명, 입사년도, 입사월, 입사일 조회
SELECT EMP_NAME
         , EXTRACT(YEAR FROM HIRE_DATE) || '년' AS "입사년도"
         , EXTRACT(MONTH FROM HIRE_DATE) || '월' AS "입사월"        
         , EXTRACT(DAY FROM HIRE_DATE) || '일' AS "입사일"
FROM EMPLOYEE
ORDER BY "입사년도", "입사월", "입사일";

 

 <형변환 함수>
    


    <NUMBER/DATE => CHARACTER>


    -TO_CHAR(NUMBER/DATE, '포맷')
    : 숫자형 또는 날짜형 데이터를 문자형 타입으로 변환 (CHARACTER 타입 반환)

SELECT TO_CHAR(1234)
FROM DUAL; -- 1234 (숫자) => '1234' (문자열)

SELECT TO_CHAR(1234, '00000')
FROM DUAL; -- 1234 (숫자) => '01234' (문자열): 빈칸을 0으로 채워 주는 포맷

SELECT TO_CHAR(1234, '99999')
FROM DUAL; -- 1234 (숫자) => '1234' (문자열): 빈칸을 채우지 않는 포맷

SELECT TO_CHAR(1234, 'L00000')
FROM DUAL; -- 1234 (숫자) => \01234 (문자열): 현재 설정된 지역(LOCAL)의 화폐 단위를 붙여 주는 포맷

SELECT TO_CHAR(1234, 'L99999')
FROM DUAL; -- 1234 (숫자) => \1234 (문자열)

-- 만약에 달러로 표현하고 싶다면?
SELECT TO_CHAR(1234, '$99999')
FROM DUAL; -- 1234 (숫자) => $1234 (문자열)

-- 금액과 같은 큰 숫자의 경우 3자리마다 ,로 구문해서 출력
SELECT TO_CHAR(1234, 'L99,999')
FROM DUAL; -- 1234 (숫자) => \1,234 (문자열): 3자리마다 ,로 구분해 주는 포맷

-- 급여 정보를 3자리마다 ,로 구분하여 출력
SELECT EMP_NAME, TO_CHAR(SALARY, 'L999,999,999') "급여정보"
FROM EMPLOYEE
ORDER BY 급여정보;

-- DATE(년월일시분초) => CHARACTER
SELECT SYSDATE
FROM DUAL;

SELECT TO_CHAR(SYSDATE)
FROM DUAL; -- '22/08/24' (문자열)

SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD')
FROM DUAL; -- '2022-08-24'

SELECT TO_CHAR(SYSDATE, 'AM HH:MI:SS') -- AM 또는 PM: 오전/오후 판별해서 출력하라는 의미이지, AM이라고 오전, PM이라고 오후 고정 개념이 아님!
FROM DUAL; -- '오후 03:39:26'

SELECT TO_CHAR(SYSDATE, 'HH24:MI:SS')
FROM DUAL; -- '15:41:51'

SELECT TO_CHAR(SYSDATE, 'MON DY, YYYY') -- MON: N월, DY: '요일'을 뺀 형태의 요일
FROM DUAL; -- '8월 수, 2022'

-- 년도로써 쓸 수 있는 포맷
SELECT TO_CHAR(SYSDATE, 'YYYY') -- 2022
         , TO_CHAR(SYSDATE, 'RRRR') -- 2022
         , TO_CHAR(SYSDATE, 'YY') -- 22
         , TO_CHAR(SYSDATE, 'RR') -- 22
         , TO_CHAR(SYSDATE, 'YEAR') -- TWENTY TWENTY-TWO
FROM DUAL;

-- 월로써 쓸 수 있는 포맷
SELECT TO_CHAR(SYSDATE, 'MM') -- 08
         , TO_CHAR(SYSDATE, 'MON') -- 8월
         , TO_CHAR(SYSDATE, 'MONTH') -- 8월
         , TO_CHAR(SYSDATE, 'RM') -- VIII, 로마 숫자로 월을 출력해 주는 포맷
FROM DUAL;

-- 일로써 쓸 수 있는 포맷
SELECT TO_CHAR(SYSDATE, 'D') -- 4, 1주일 기준으로 (일요일부터) 며칠이 흘렀는지 알려 주는 포맷
         , TO_CHAR(SYSDATE, 'DD') -- 24, 1달 기준으로 (해당 달의 1일부터) 며칠이 흘렀는지 반환해 주는 포맷
         , TO_CHAR(SYSDATE, 'DDD') -- 236, 1년 기준으로 (해당 년도의 1월 1일부터) 며칠이 흘렀는지 반환해 주는 포맷
FROM DUAL;

-- 요일로써 쓸 수 있는 포맷
SELECT TO_CHAR(SYSDATE, 'DY') -- 수, DY에는 '요일'을 뺀 형식으로 출력해 주는 포맷
         , TO_CHAR(SYSDATE, 'DAY') -- 수요일, DAY는 '요일'을 붙인 형식으로 출력해 주는 포맷
FROM DUAL;

-- 2022년 08월 24일 (수) 포맷으로 적용시키고 싶음
SELECT TO_CHAR(SYSDATE, 'YYYY"년" MM"월" DD"일" "("DY")"')
FROM DUAL; -- 포맷이 아닌 다른 문자랑 같이 쓰고자 할 경우 ""로 묶어 줌

-- 사원명, 입사일(위의 포맷 적용) 조회
SELECT EMP_NAME, TO_CHAR(HIRE_DATE, 'YYYY"년" MM"월" DD"일" "("DY")"') AS "입사일"
FROM EMPLOYEE;

-- 2010년 이후에 입사한 사원들의 사원명, 입사일(위의 포맷 적용) 조회
SELECT EMP_NAME, TO_CHAR(HIRE_DATE, 'YYYY"년" MM"월" DD"일" "("DY")" ') AS "입사일"
FROM EMPLOYEE
WHERE EXTRACT(YEAR FROM HIRE_DATE) >= 2010;

 

  <NUMBER/CHARACTER => DATE>
    
    - TO_DATE(NUMBER/CHARACTER, '포맷'): 숫자형 또는 문자형 데이터를 날짜형으로 변환해 주는 함수 (DATE 타입 반환)

SELECT TO_DATE(20210101)
FROM DUAL; -- 기본 포맷은 YY/MM/DD로 변환됨

SELECT TO_DATE('20210101')
FROM DUAL; -- YY/MM/DD

-- 주의할 점: 매개변수로 NUMBER 타입을 넘길 경우 0으로 시작하는 년도는 반드시 문자열 타입으로 넘겨 줘야 함
SELECT TO_DATE(000101) -- 000101은 0으로 시작하는 숫자로 인식하여 에러 발생
FROM DUAL;

SELECT TO_DATE('000101')
FROM DUAL; -- YY/MM/DD

SELECT TO_DATE('20100101', 'YYYYMMDD')
FROM DUAL; -- YY/MM/DD 형식으로 나옴 (값 보기 버튼 눌러보기)

SELECT TO_DATE('041030 143021', 'YYMMDD HH24MISS')
FROM DUAL; -- YY/MM/DD 형식으로 나옴

-- 주의할 점: YY와 RR의 차이점
SELECT TO_DATE('140630', 'YYMMDD')
FROM DUAL; -- 2014년도

SELECT TO_DATE('980630', 'YYMMDD')
FROM DUAL; -- 2098년도
-- TO_DATE 함수를 이용해서 DATE 형식으로 변환 시
-- 두 자리 년도에 대해 YY 포맷을 적용할 경우 => 무조건 현재 세기

SELECT TO_DATE('140630', 'RRMMDD')
FROM DUAL; -- 2014년도

SELECT TO_DATE('980630', 'RRMMDD')
FROM DUAL; -- 1998년도
-- TO_DATE 함수를 이용해서 DATE 형식으로 변환 시
-- 두 자리 년도에 대해 RR 포맷을 적용할 경우 => 50 이상이면 이전 세기, 50 미만이면 현재 세기로

 

 <CHARACTER => NUMBER>
    
    - TO_NUMBER(CHARACTER, '포맷'): 문자형 데이터를 숫자형으로 변환 (NUMBER 타입 반환)

-- 자동형변환 되는 예시
SELECT '123' + '123'
FROM DUAL; -- 246: 자동형변환 후 산술연산까지 진행

-- 자동형변환이 안 되는 예시
SELECT '10,000,000' + '550,000'
FROM DUAL; -- 문자(,)가 포함되어 있기 때문에 자동형변환이 안 됨
--> 이런 경우에 사용할 수 있는 함수가 TO_NUMBER

-- 위의 예시를 TO_NUMBER 함수를 이용하여 변경 => 강제형변환
SELECT TO_NUMBER('10,000,000', '99,999,999') + TO_NUMBER('550,000', '999,999')
FROM DUAL; -- 10550000

-- 만약 위의 결과를 10,550,000 이렇게 보고 싶다면?
SELECT TO_CHAR(TO_NUMBER('10,000,000', '99,999,999') + TO_NUMBER('550,000', '999,999'), '99,999,999')
FROM DUAL; -- 10,550,000

-- 0123?
SELECT TO_NUMBER('0123')
FROM DUAL; -- 123

 

<NULL 처리 함수>

 

-- NVL(컬럼명, 해당컬럼값이NULL일경우반환할결과값)
  : 해당 컬럼값이 존재할 경우 기존의 컬럼값을 그대로 반환, 해당 컬럼값이 NULL일 경우 내가 제시한 결과값을 반환

-- 사원명, 보너스, 보너스가 없는 경우 0으로 출력
SELECT EMP_NAME, BONUS, NVL(BONUS, 0)
FROM EMPLOYEE;

NVL 적용 시 NULL 값이 0으로 대체됨

-- 보너스 포함 연봉을 조회
-- SELECT EMP_NAME, NVL((SALARY + (SALARY  * BONUS)) * 12, SALARY * 12)
SELECT EMP_NAME, (SALARY + (SALARY  * NVL(BONUS, 0))) * 12
FROM EMPLOYEE;

-- 사원명, 부서코드(부서코드가 없는 경우 '없음') 조회
SELECT EMP_NAME, NVL(DEPT_CODE, '없음')
FROM EMPLOYEE;

 

-- NLV2(컬럼명, 결과값1, 결과값2)
  : 해당 컬럼값이 존재할 경우 결과값1을 반환, 해당 컬럼값이 NULL일 경우 결과값2를 반환

-- 사원명, 보너스, 보너스가 있는 경우 0.7로 인상, 보너스가 없는 경우 0 출력
SELECT EMP_NAME, BONUS, NVL2(BONUS, 0.7, 0)
FROM EMPLOYEE;

-- 사원명, 부서코드(부서코드가 있는 경우 '부서배치완료', 없는 경우 '부서배치미완료') 조회
SELECT EMP_NAME, DEPT_CODE, NVL2(DEPT_CODE, '부서배치완료', '부서배치미완료')
FROM EMPLOYEE;

 

-- NULLIF(비교대상1, 비교대상2)
  : 두 매개변수의 값이 일치할 경우 NULL 반환, 두 매개변수의 값이 일치하지 않을 경우 비교대상1 반환

SELECT NULLIF('123', '123')
FROM DUAL; -- (NULL)

SELECT NULLIF('123', '456')
FROM DUAL; -- 123

 

    <선택 함수>


    DECODE(비교대상(컬럼명/산술연산/함수식), 조건값1, 결과값1, 조건값2, 결과값2, ..., 결과값)
    
    - 자바에서의 SWITCH문과 유사 (동등비교를 해 주는 조건문)
    switch(비교대상) {
        case 조건값1 : 결과값1; break;
        case 조건값2 : 결과값2; break;
        ...
        default : 결과값;
    }

-- 사번, 사원명, 성별(주민등록번호로부터 성별 자리를 추출)
SELECT EMP_ID, EMP_NAME, DECODE(SUBSTR(EMP_NO, 8, 1), '1', '남'
                                                    , '2' ,'여'
                                                    , '3', '남'
                                                    , '4', '여') "성별"
FROM EMPLOYEE;

-- 직원들의 급여를 인상시켜서 조회
-- 직급코드가 'J7'인 사원은 급여를 10% 인상해서 조회
-- 직급코드가 'J6'인 사원은 급여를 15% 인상해서 조회
-- 직급코드가 'J5'인 사원은 급여를 20% 인상해서 조회
-- 그 이외의 직급코드인 사원은 급여를 5% 인상해서 조회
SELECT EMP_NAME, JOB_CODE, SALARY, DECODE(JOB_CODE, 'J7', SALARY * 1.1
                                                  , 'J6', SALARY * 1.15
                                                  , 'J5', SALARY * 1.2
                                                        , SALARY * 1.05) "인상된급여"
FROM EMPLOYEE;

 

    <CASE WHEN THEN 구문>
    -- DECODE 선택함수와 비교하면 DECODE는 해당 조건 검사 시 동등비교만을 수행한다면
        CASE WHEN THEN 구문은 특정 조건 제시 시 내 마음대로 조건식을 기술 가능
        
        - 자바에서의 if-else if문 같은 느낌
        if(조건식1) {
            실행할구문;
        }
        else if(조건식2) {
            실행할구문;
        } else {
            실행할구문;
        }
        
        [ 표현법 ]
        CASE WHEN 조건식1 THEN 결과값1
                WHEN 조건식2 THEN 결과값2
                ...
                ELSE 결과값
        END   

-- 사번, 사원명, 주민번호로부터 추출한 성별(DECODE함수)
SELECT EMP_ID
         , EMP_NAME
         , DECODE(SUBSTR(EMP_NO, 8,1), '1', '남'
                                                     , '2', '여'
                                                     , '3', '남'
                                                     , '4', '여') "성별"
FROM EMPLOYEE;

-- CASE WHEN THEN 구문 버전
SELECT EMP_ID
         , EMP_NAME
         , CASE WHEN SUBSTR(EMP_NO, 8,1) = '1' THEN '남'
                WHEN SUBSTR(EMP_NO, 8,1) = '2' THEN '여'
                WHEN SUBSTR(EMP_NO, 8,1) = '3' THEN '남'
                WHEN SUBSTR(EMP_NO, 8,1) = '4' THEN '여'
            END "성별"
FROM EMPLOYEE;

-- 경우의 수를 줄여 보기
SELECT EMP_ID
         , EMP_NAME
         , CASE WHEN SUBSTR(EMP_NO, 8,1) = '1' OR SUBSTR(EMP_NO, 8,1) = '3' THEN '남'
                WHEN SUBSTR(EMP_NO, 8,1) = '2' OR SUBSTR(EMP_NO, 8,1) = '4' THEN '여'
            END "성별"
FROM EMPLOYEE;

-- 경우의 수를 줄여 보기 (IN 사용해 보기)
SELECT EMP_ID
         , EMP_NAME
         , CASE WHEN SUBSTR(EMP_NO, 8,1) IN ('1', '3') THEN '남'
                WHEN SUBSTR(EMP_NO, 8,1) IN ('2', '4') THEN '여'
            END "성별"
FROM EMPLOYEE;

-- 경우의 수를 줄여 보기
SELECT EMP_ID
         , EMP_NAME
         , CASE WHEN SUBSTR(EMP_NO, 8,1) IN ('1' , '3') THEN '남'
           	ELSE '여'
           END "성별"
FROM EMPLOYEE;

-- 사원명, 급여, 급여등급 (고급, 중급, 초급)
-- SALARY 값이 500만원 초과일 경우 '고급'
--             500만원 이하 350만원 초과일 경우 '중급'
--             350만원 이하일 경우 '초급'

SELECT EMP_NAME
         , SALARY
         , CASE WHEN SALARY > 5000000 THEN '고급'
                WHEN SALARY > 3500000 THEN '중급'
          	ELSE '초급'
           END "급여등급"
FROM EMPLOYEE
WHERE SALARY > 5000000 -- 고급만 보겠다
ORDER BY SALARY DESC; -- 월급이 높은 순서대로 보겠다

 

    <그룹 함수>


        그룹 함수: n개의 값을 읽어서 1개의 결과를 반환 (하나의 그룹별로 함수 실행 결과 반환)


-- 1. SUM(숫자타입컬럼명): 해당 컬럼값들의 총 합계를 구해서 반환해 주는 함수 (NUMBER 타입 변환)

-- 전체 사원들의 총 급여 합계
SELECT SUM(SALARY)
FROM EMPLOYEE;

-- 부서 코드가 'D5'인 사원들의 총 급여 합계
SELECT SUM(SALARY)
FROM EMPLOYEE
WHERE DEPT_CODE = 'D5';

-- 남자 사원의 총 급여 합
SELECT SUM(SALARY)
FROM EMPLOYEE
WHERE SUBSTR(EMP_NO, 8, 1) IN('1', '3');


-- 2. AVG(숫자타입컬럼명): 해당 컬럼값들의 평균값을 구해서 반환해 주는 함수 (NUMBER 타입 변환)

-- 전체 사원들의 평균 급여
SELECT AVG(SALARY)
FROM EMPLOYEE;

-- 전체 사원들의 평균 급여(반올림)
SELECT ROUND(AVG(SALARY))
FROM EMPLOYEE;


-- 3. MIN(아무타입컬럼명): 해당 컬럼값들 중 가장 작은 값을 반환해 주는 함수(매개변수의 타입을 반환)

-- 전체 사원들 중 최저급여, 가장 작은 이름값, 가장 작은 이메일값, 가장 예전에 입사한 날짜 구하기
SELECT MIN(SALARY) "최저급여"
         , MIN(EMP_NAME) "가장작은이름"
         , MIN(EMAIL) "가장작은이메일"
         , MIN(HIRE_DATE) "가장작은입사일"
FROM EMPLOYEE;

SELECT *
FROM EMPLOYEE
ORDER BY HIRE_DATE ASC; -- 가장예전입사일 ~ 가장최근입사일 순서로 출력됨
--> MIN 함수의 원리: 해당 컬럼값을 기준으로 오름차순 정렬했을 때 가장 위쪽에 나오는 값이 리턴


-- 4. MAX(아무타입컬럼명): 해당 컬럼값들 중 가장 큰 값을 반환해 주는 함수(매개변수의 타입을 반환)

-- 전체 사원들 중 최고급여, 가장 큰 이름값, 가장 큰 이메일값, 가장 최근 입사일 구하기
SELECT MAX(SALARY) "최고급여"
         , MAX(EMP_NAME) "가장큰이름값"
         , MAX(EMAIL) "가장큰이메일값"
         , MAX(HIRE_DATE) "가장큰입사일"
FROM EMPLOYEE;

SELECT *
FROM EMPLOYEE
ORDER BY EMP_NAME DESC;
--> MAX 함수의 원리: 해당 컬럼값을 기준으로 내림차순 정렬했을 때 가장 위쪽에 나오는 값이 리턴


-- 5. COUNT(*/컬럼명/DISTINCT 컬럼명): 조회된 행의 개수를 반환해 주는 함수(NUMBER 타입 반환)

-- COUNT(*): 조회 결과에 해당되는 모든 행 개수를 다 세서 반환
-- COUNT(컬럼명): 제시한 해당 컬럼값이 NULL이 아닌 것만 행 개수를 세서 반환
-- COUNT(DISTINCT 컬럼명): 제시한 해당 컬럼값이 중복값이 있을 경우 한 개로만 개수를 세서 반환 (NULL 포함 X)

-- 전체 사원 수 조회
SELECT COUNT(*)
FROM EMPLOYEE;

-- 여자 사원 수 조회
SELECT COUNT(*)
FROM EMPLOYEE
WHERE SUBSTR(EMP_NO, 8, 1) IN('2', '4');

-- 부서 배치가 된 사원(DEPT_CODE 값이 존재) 수
-- 방법 1) 전체 수를 카운트하고 NULL 값을 카운트에서 빼는 방법
SELECT COUNT(*)
FROM EMPLOYEE
WHERE DEPT_CODE IS NOT NULL;

-- 방법 2) 애초에 카운트 시 NUILL만 빼고 카운트하는 방법
SELECT COUNT(DEPT_CODE)
FROM EMPLOYEE;

-- 부서 배치가 된 사원(DEPT_CODE 값이 존재) 여자 사원 수
-- 방법 1)
SELECT COUNT(*)
FROM EMPLOYEE
WHERE DEPT_CODE IS NOT NULL
    AND SUBSTR(EMP_NO, 8, 1) IN ('2', '4');
    
-- 방법 2)
SELECT COUNT(DEPT_CODE)
FROM EMPLOYEE
WHERE SUBSTR(EMP_NO, 8, 1) IN ('2', '4');

-- 사수가 있는 사원 수
SELECT COUNT(MANAGER_ID)
FROM EMPLOYEE;

-- 현재 사원들이 속해 있는 부서의 개수
SELECT COUNT(*)
FROM DEPARTMENT; -- 9
--> 사람들이 속해 있지 않은 부서까지 함께 출력함 // 요구사항 지키지 못했음

SELECT COUNT(DISTINCT DEPT_CODE)
FROM EMPLOYEE; -- 6 (사원들이 한 명이라도 있는 부서의 개수)     <그룹 함수>