- 데이터 형식 확인하자
- 숫자로된 문자는 숫자형으로 변환하여 계산
[문제] 2005년도에 홀수달에 입사한 사원들의 정보를 출력.
DESC employees; -- 테이블 형식 체크
SELECT * FROM nls_session_parameters; -- 클라이언트 정보 체크
SELECT *
FROM hr.employees
WHERE to_char(hire_date,'yyyy') = '2005' -- to_char : 문자형
AND mod(to_number(to_char(hire_date,'mm')),2) = 1; -- mod 계산을 위해 to_number 숫자형으로 변환
SELECT *
FROM hr.employees
WHERE hire_date >= to_date('2005/01/01','rrrr/mm/dd')
AND hire_date <= to_date('2005/12/31','rrrr/mm/dd')
AND mod(to_number(to_char(hire_date,'mm')),2) = 1;
hire_date >= '2005/01/01' -> 연도 4자리 표기
date char -> 암시적 데이터 변환 사용 자제 -> to_date 사용하자
SELECT mod(to_number(to_char(hire_date,'mm')),2), salary
FROM hr.employees;
salary -> 숫자 : 오른쪽정렬
to_char(hire_date,'mm') -> 문자로 추출 : 왼쪽정렬
-> 암묵적 숫자형변환 -> 사용 자제
to_number(to_char(hire_date,'mm')) -> 숫자로 바꿔 계산하자
■ NULL
- null은 사용할 수 없거나, 할당되지 않았거나, 알 수 없거나, 적용할 수 없는값, 계산할 수 없다. 결측값(치)
- null 0, 공백이 아니다
SELECT
employee_id,
salary,
commission_pct,
((salary * 12) + (salary * 12 * commission_pct)) annual_salary_1,
((salary * 12) + (salary * 12 * nvl(commission_pct,0))) annual_salary_2
FROM hr.employees;
■ nvl ★
- null값을 실제값으로 리턴하는 함수
- nvl(칼럼, 실제값) , nvl(인수, 인수)
- nvl함수 사용시에 두 인수의 타입이 일치해야 한다.
SELECT
nvl(commission_pct,0),
nvl(commission_pct,to_number('0')),
nvl(to_char(commission_pct),'no comm')
FROM hr.employees;
COMMISSION_PCT : NUMBER(2,2) : 숫자형 칼럼
nvl(commission_pct,'no comm') -> 'no comm' : 문자라 형식이 달라 오류 남
-> to_char(commission_pct) 숫자를 문자형으로 변환
■ nvl2(exp1, exp2, exp3)
- exp1이 null이 아니면 exp2를 수행하고 exp1이 null이면 exp3를 수행한다.
- exp2와 exp3의 인수가 일치해야 한다.
SELECT
employee_id,
salary,
commission_pct,
((salary * 12) + (salary * 12 * commission_pct)) annual_salary_1,
((salary * 12) + (salary * 12 * nvl(commission_pct,0))) annual_salary_2,
nvl2(commission_pct, ((salary * 12) + (salary * 12 * commission_pct)), salary * 12) annual_salary_3
FROM hr.employees;
nvl2(commission_pct, ((salary * 12) + (salary * 12 * commission_pct)), salary * 12) annual_salary_3
<PL/SQL>
if commission_pct is not null then
((salary * 12) + (salary * 12 * commission_pct))
else :
salary * 12
end if
SELECT
nvl(commission_pct,0),
nvl(commission_pct,to_number('0')),
nvl(to_char(commission_pct),'no comm'),
nvl2(commission_pct, to_char(salary * 12 * commission_pct), 'no comm'),
FROM hr.employees;
nvl2(commission_pct, salary * 12 * commission_pc, 'no comm')
└ 숫자형 └ 문자형 -> 타입 불일치 오류
: to_char사용하여 형변환
■ coalesce(exp1, exp2, exp3, ..., expn)
- exp1 null이면 exp2를 수행하고, exp2 null이면 exp3를 수행하고,
exp3 null이면 다음 exp를 수행한다.
즉, null이 발생하지 않을때가지 인수를 수행한다.
- ★ 비교에 있어 인수타입이 서로 같아야 한다.
SELECT
nvl(commission_pct,0),
nvl(commission_pct,to_number('0')),
nvl(to_char(commission_pct),'no comm'),
nvl2(commission_pct, to_char(salary * 12 * commission_pct), 'no comm'),
coalesce(to_char(commission_pct), to_char(salary * 12 * commission_pct), 'no comm')
FROM hr.employees;
SELECT
employee_id,
salary,
commission_pct,
((salary * 12) + (salary * 12 * commission_pct)) annual_salary_1,
((salary * 12) + (salary * 12 * nvl(commission_pct,0))) annual_salary_2,
nvl2(commission_pct, ((salary * 12) + (salary * 12 * commission_pct)), salary * 12) annual_salary_3,
coalesce(((salary * 12) + (salary * 12 * commission_pct)), salary * 12, 0) annual_salary_4
FROM hr.employees;
■ nullif(exp1, exp2)
- exp1과 exp2가 일치하면 null, 일치하지 않으면 exp1을 리턴한다.
SELECT
length(last_name),
length(first_name),
nullif(length(last_name),length(first_name)),
nullif(length(last_name),5)
FROM hr.employees;
<PL/SQL>
if exp1 = exp2 then
null
else
exp1
end if
■ 조건제어문
- SQL문에서는 IF문을 사용해서 조건제어문을 수행할 수 없다.
- CASE 표현식, DECODE 함수
- DECODE 함수는 기준값과 비교값을 내부적으로 같다(=)라는 비교연산자를 사용한다.
DECODE(기준값, 비교값, 참값) -- false면 null값으로 리턴한다.
**DECODE(기준값, 비교값, 참값, 기본값) -- false면 기본값을 리턴한다.**
- 오라클 전용 함수
DECODE(기준값, 비교값1, 참값1, 비교값2, 참값2, 비교값3, 참값3, ... 기본값) -- 기본값 미표기 시 null 값 리턴 | IF 기준값 = 비교값 THEN 참값 ELSE null END IF |
---|
SELECT
employee_id,
salary,
job_id,
decode(job_id,
'IT_PROG', salary * 1.1,
'ST_CLERK', salary * 1.2,
'SA_REP', salary * 1.3,
salary) revised_salary
FROM hr.employees;
비교연산자 : =, >, >=, <, <=, <>, !=, ^=, BETWEEN AND, IN,
IS NULL, IS NOT NULL, LIKE
논리연산자 : AND, OR, NOT
- CASE 표현식(9i) : 기준값과 비교값의 대해서 모든 비교연산자를 사용할 수 있다.
CASE 기준값
WHEN 비교값1 THEN 참값1
WHEN 비교값2 THEN 참값2
WHEN 비교값3 THEN 참값3
...
ELSE 기본값
END
SELECT
employee_id,
salary,
job_id,
CASE job_id
WHEN 'IT_PROG' THEN salary * 1.1
WHEN 'ST_CLERK' THEN salary * 1.2
WHEN 'SA_REP' THEN salary * 1.3
ELSE salary -- 생략 시 NULL값 리턴
END revised_salary
FROM hr.employees;
CASE
WHEN 기준값 비교연산자 비교값1 [AND,OR] ... THEN 참값1
WHEN 기준값 비교연산자 비교값2 THEN 참값2
WHEN 기준값 비교연산자 비교값3 THEN 참값3
...
ELSE 기본값
END
SELECT
employee_id,
salary,
job_id,
CASE
WHEN job_id = 'IT_PROG' THEN salary * 1.1
WHEN job_id = 'ST_CLERK' THEN salary * 1.2
WHEN job_id = 'SA_REP' THEN salary * 1.3
ELSE salary
END revised_salary
FROM hr.employees;
[문제] 사원들의 급여를 기준으로 아래와 같이 출력.
~ 4999 : low
5000 ~ 9999 : medium
10000 ~ 19999 : good
20000 ~ : excellent
SELECT
salary,
CASE
when salary < 5000 then 'low'
when salary < 10000 then 'medium'
when salary < 20000 then 'good'
else 'excellent'
END grade
FROM hr.employees;
■ DECODE 함수, CASE 표현식 null check 방법
- decode함수의 null 체크는 null 키워드를 사용한다.
- case표현식에서 null 체크는 is null, is not null 연산자를 사용한다.
SELECT
employee_id,
salary,
commission_pct,
DECODE(commission_pct, null, salary * 12,
((salary * 12) + (salary * 12 * commission_pct))) annual_salary_1,
CASE
WHEN commission_pct is null then salary * 12
ELSE (salary * 12) + (salary * 12 * commission_pct)
END annual_salary_2
FROM hr.employees;
■ yy와 rr 차이점
예) 95-10-27
- YY : 현재 년도의 세기를 반영 : 20세기 (2095-10-27), 21세기 (2009/10/27)
- RR : 2000년부터는 표기법을 자동화로 변경해순다.
SELECT
to_char(to_date('95-10-27','yy/mm/dd'), 'yyyy-mm-dd') yy,
to_char(to_date('95-10-27','rr/mm/dd'), 'yyyy-mm-dd') rr
FROM dual;
(RR 타입 기준 표)
현재연도 | 데이터 입력 연도 | |
---|---|---|
0~49 | 50~99 | |
0~49 | 반환 날짜는 현재 세기를 반영 | 반환 날짜는 이전 세기를 반영 |
50~99 | 반환 날짜는 이후 세기를 반영 | 반환 날짜는 현재 세기를 반영 |
예)
현재연도 | 데이터입력날짜 | YY | RR |
---|---|---|---|
1994 | 95-10-27 | 1995 | 1995 |
1994 | 17-10-27 | 1917 | 2017 |
2001 | 17-10-27 | 2017 | 2017 |
2048 | 52-10-27 | 2052 | 1952 |
2051 | 47-10-27 | 2047 | 2147 |
■ 그룹함수
- 여러행당 하나의 결과를 반환하는 함수
- SUM, AVG, MEDIAN, VARIANCE, STDDEV, MAX, MIN, COUNT
- 그룹함수에 입력하는 값이 숫자형만 입력해야하는 그룹함수 : SUM, AVG, MEDIAN, VARIANCE, STDDEV
- 그룹함수에 입력하는 값이 모든 타입이 가능한 그룹함수 : MAX, MIN, COUNT
- 그룹함수는 NULL 을 포함하지 않습니다(=제외). 단, COUNT(*) 만 NULL 포함한 행수를 구한다.
- COUNT : 행의 수를 구하는 함수
SELECT count(*) -- null 포함 : 전체 검색 시
FROM hr.employees;
SELECT count(department_id) -- null 제외한 건수 : 컬럼명으로 검색 시
FROM hr.employees;
SELECT count(distinct department_id) -- 중복을 제거한 건수
FROM hr.employees;
SELECT count(unique department_id) -- 중복을 제거한 건수
FROM hr.employees;
- SUM : 합
SELECT sum(salary)
FROM hr.employees;
SELECT count(commission_pct),sum(commission_pct) -- null 값을 제외한 합
FROM hr.employees;
- AVG : 평균
SELECT
avg(commission_pct), -- null 제외한 평균
avg(nvl(commission_pct, 0)) -- null을 0으로 변환하여 전체 평균
FROM hr.employees;
예) 10, 20, null
sun : 10 + 20
avg : (10 + 20) / 2
전체 avg : (10 + 20 + 0) / 3
- MEDIAN : 중앙값
SELECT avg(salary), median(salary)
FROM hr.employees;
예) 관측개수 : 홀수
20, 30, 10, 50, 60, 90, 70, 40, 80
1.크기순으로 오름차순 정렬
10, 20, 30, 40, 50, 60, 70, 80, 90
2.건수 계산
홀수 : (관측개수 + 1) / 2 = 5 = 5번째 값 = 중앙값의 위치
예) 관측개수 : 짝수
20, 30, 10, 50, 60, 90, 70, 40, 80, 100
1.크기순으로 오름차순 정렬
10, 20, 30, 40, 50, 60, 70, 80, 90, 100
2.건수 계산
짝수 : 관측개수 / 2 = 5
(관측개수 / 2) + 1= 6 = 5번,6번 위치값의 평균
- VARIANCE : 분산, 내가 가진 자료(데이터)가 평균값을 중심으로 퍼져있는 평균적인 거리
SELECT variance(salary)
FROM hr.employees;
1. 평균
2. 편차 제곱합의 평균
(관측값 - 관측값평균)² + (관측값 - 관측값평균)² + (관측값 - 관측값평균)²
---------------------------------------------------------------
관측값의 수 - 1 (자유도)
- STDDEV : 표준편차, 분산에 제곱근을 수행한 값, 이유는 단위를 마춰기 위해서
SELECT stddev(salary)
FROM hr.employees;
- MAX : 최대값
SELECT max(salary), max(last_name), max(hire_date)
FROM hr.employees;
- MIN : 최소값
SELECT min(salary), min(last_name), min(hire_date)
FROM hr.employees;
- 범위 : max - min
SELECT max(salary) - min(salary)
FROM hr.employees;
■ GROUP BY 절 : 데이터의 행을 작은 그룹(군집)으로 나눌 수 있는 절
- 9i R1 버전까지는 sort group by 알고리즘으로 수행되어 group by 절에 명시된 컬럼을 기준으로 정렬된 결과로 출력된다.
- 9i R2 버젼부터 hash group by 알고리즘으로 수행하기 때문에 group by 절ㅇ에 명시된 컬럼을 기준으로 정렬이 수행되지 않습니다.
■ 그룹함수 사용 시 주의 사항
- NULL 을 포함하지 않습니다.(단, COUNT(*) 제외)
- SELECT 절에 그룹함수에 포함되지 않은 개별 컬럼은 하나도 빠짐 없이 GROUP BY 절에 명시해야 한다.
- GROUP BY 절에는 열별칭, 위치표기법 사용할 수 없다.
- GROUP BY 절에 명시된 컬럼을 기준으로 군집화 할때 NULL 포함한다.
- 그룹함수의 결과를 제한하기위해서 WHERE 절을 사용하면 오류 발생
SELECT department_id, sum(salary) -> 오류 : 그룹함수 사용 시 나머지 개별칼럼을 그룹으로 묶어주기
FROM hr.employees;
↓
SELECT department_id, sum(salary)
FROM hr.employees
GROUP BY department_id;
SELECT department_id, job_id, sum(salary)
FROM hr.employees
GROUP BY department_id, job_id;
SELECT department_id dept_id, job_id, sum(salary)
FROM hr.employees
GROUP BY dept_id, 2; -> 오류 : GROUP BY 절에서는 열별칭, 위치표기법 사용불가
SELECT department_id, sum(salary)
FROM hr.employees
WHERE sum(salary) >= 20000 -> 오류 : WHERE 절은 행을 제한하는 절이지
GROUP BY department_id 그룹함수의 결과를 제한하지 않는다. -> HAVING 절 사용
ORDER BY 1;
■ HAVING 절 : 그룹함수의 결과를 제한하는 절
SELECT department_id, sum(salary)
FROM hr.employees
GROUP BY department_id
HAVING sum(salary) >= 20000
ORDER BY 1;
SELECT department_id, sum(salary)
FROM hr.employees
WHERE last_name LIKE '%i%'
GROUP BY department_id
HAVING sum(salary) >= 20000
ORDER BY 1;
SELECT department_id, sum(salary)
FROM hr.employees
WHERE last_name LIKE '%i%'
GROUP BY department_id
HAVING count(*) >= 5
ORDER BY 1;
- 그룹함수는 2번만 중첩할 수 있다.
- 그룹함수를 2번 중첩했을 경우 개별 컬럼을 사용할 수 없다. 오류발생.
해결방법 : 서브쿼리를 이용해야한다.
SELECT max(avg(salary))
FROM hr.employees
GROUP BY department_id;
[문제] 2008년도에 입사한 사원들의 job_id별 인원수를 구하고 인원수가 많은 순으로 출력.
SELECT job_id, count(*) 인원수
FROM hr.employees
WHERE to_char(hire_date,'rrrr') = '2008'
GROUP BY job_id
ORDER BY 2 desc;
[문제] 년도별 입사한 인원수를 출력.
SELECT to_char(hire_date,'rrrr') 입사연도, count(*) 인원수
FROM hr.employees
GROUP BY to_char(hire_date,'rrrr');
[문제] 월별 입사한 인원수 출력.
SELECT to_number(to_char(hire_date,'mm')) 입사월, count(*) 인원수
FROM hr.employees
GROUP BY to_number(to_char(hire_date,'mm'))
ORDER by 1 ;
[문제] 년도별 입사 인원수를 아래와 같이 출력. ★
total 2001년 2002년 2003년
----- ----- ----- ------
107 1 7 6
SELECT
count(*) total,
count(decode(to_char(hire_date,'yyyy'), '2001', 'x')) "2001년",
count(decode(to_char(hire_date,'yyyy'), '2002', 'x')) "2002년",
count(decode(to_char(hire_date,'yyyy'), '2003', 'x')) "2003년"
FROM hr.employees;
또는
SELECT
count(*) total,
count(case when to_char(hire_date,'yyyy') = '2001' then 'x' end) "2001년",
count(case when to_char(hire_date,'yyyy') = '2002' then 'x' end) "2002년",
count(case when to_char(hire_date,'yyyy') = '2003' then 'x' end) "2003년"
FROM hr.employees;
☆ SQL에서 조건문은 decode, case 사용
SELECT count(decode(to_char(hire_date,'yyyy'), '2001', 'x')) "2001년"
-- (2001 맞으면 x)의 카운트
FROM hr.employees;
SELECT sum(decode(to_char(hire_date,'yyyy'), '2001', 1)) "2001년"
-- (2001 맞으면 1)의 합계
FROM hr.employees;
'Oracle SQL' 카테고리의 다른 글
06 SUBQUERY (0) | 2023.10.13 |
---|---|
05 JOIN (0) | 2023.10.12 |
03 숫자함수, 날짜함수, 형변환함수 (0) | 2023.10.10 |
02 DISTINCT, WHERE, BETWEEN, IN, LIKE, SORT, FUNCTION (0) | 2023.10.06 |
01 OracleXE, Select (0) | 2023.10.06 |