Oracle (feat. docker)
※ Oracle Database 11g Express Edition 중심으로
자바에서 Oracle DB를 사용할 때 JDBC 라이브러리를 사용하게 되는데 이때 JDBC를 사용하기 위해 몇가지 필요한 정보가 있다. 즉 JDBC를 통해 Oracle DB에 connection하기 위해 몇 가지 필요한 정보가 있다. 대표적으로 Oracle 사용자 계정에 대한 id, password... 그 중에서 Oracle database에 대한 url 정보가 있다. 대체로 다음과 같은 형태이다.
jdbc:oracle:thin:@localhost:1521:xet
이것이 무엇을 의미하는지에 대해서 살펴본다. 이에 대한 프로토타입은 다음과 같다.
jdbc:oracle:driver_type:[username/password]@[//]host_name[:port][/XE]
위의 프로토타입으로부터 아래 정보를 해석해 보면 다음과 같다.
jdbc:oracle:thin:@localhost:1521:xe
-. jdbc:oracle:thin은 사용하는 JDBC드라이버가 thin 타입을 의미한다. 자바용 오라클 JDBC드라이버는 크게 두가지가 있는데 하나는 Java JDBC THIN 드라이버고, 다른 하나는 OCI기반의 드라이버라고 한다.
-. username/password은 option이다. [ ]안에 있는 정보는 반드시 명기할 필요는 없다는 뜻이다.
-. :port 번호도 option이다. 다만 Oracle의 listener port인 1521을 사용하지 않을 경우는 이 값을 명기해 줘야 된다. 예를 들어서 jdbc:oracle:thin:hr/hr@//localhost:1522
-. localhost는 Oracle DB가 설치되어 있는 서버의 IP인데 위 경우는 로컬에 설치되어 있다는 뜻이다.
-. 1521 은 오라클 listener의 포트번호이다.
-. /XE는 Oracle database client의 고유한 service name이다. 디폴트로 XE를 사용하므로 이 정보도 option이다. 이에 대한 설정 정보는 Oracle이 설치된 폴더 아래의 app\oracle\product\11.2.0\server\network\ADMIN\listener.ora 파일에 다음과 같이 표시되어 있다
SQLPLUS 를 docker 에서 접속
docker exec -it oracle12c sqlplus
계정 변경
conn kh/1234
SQLPLUS 계정 수정하기
alter user hr account unlock; ==> hr유저의 상태를 unlock
USER hr의 비밀번호를 1234로 지정
alter user hr identified by 1234;
화면 clear
cl scr;
계정 만들기 (id = kh , pwd = 1234 )
create user kh identified by 1234 default table space users;
권한 주기
grant connect to kh;
로그아웃
disconnect
현 사용자 확인
show user
username , account_status 확인
select username , account_status from dba_users
sql 파일 실행하기
start 파일경로
접속시 오류 및 해결 방법
SQL> connect username/password
Error accessing PRODUCT_USER_PROFILE
Warning: Product user profile information not loaded!
You may need to run PUPBLD.SQL as SYSTEM
Connected.
SQL>
Oracle User 접속시 Profile 을 액세스 하는 오류입니다.
SYSTEM User 계정으로 PUPBLD.SQL 실행이 필요합니다.
SQL> connect system/password Connected. SQL> @?/sqlplus/admin/pupbld.sql
일반관리자 , 슈퍼 관리자
sys (슈퍼 관리자) - 데이터베이스 생성/삭제하는 권한 있음
데이터베이스 관련 모든 문제를 처리 가능
로그인시 SYSDBA룰로 접속해야함
system (일반 관리자) - 데이터베이스 생성/삭제 권한 X
xxxxxxxxxx데이터베이스 유지보수
no privileges on tablespace 'USERS' , 테이블 스페이스 권한 주기
grant create session, create table, create view,resource to (User_Name);
테이블 스페이스 권한 주기
xxxxxxxxxxALTER USER <user> quota 100M on <tablespace name>;예제
show user;
--kh가 가진 테이블 조회 select * from tab;
--하나씩 조회 select * from department; select * from employee; select * from job; select * from location; select * from nation; select * from sal_grade;
--일반관리자, 슈퍼관리자 -- 1. sys(슈퍼관리자) : -- 데이터베이스를 생성/삭제하는 권한 있음 -- 데이터베이스 관련 모든 문제를 처리 가능 -- 로그인 시 SYSDBA로 접속해야함
-- 2. system(일반관리자) : -- 데이터베이스 생성/삭제 권한이 없음 -- 데이터베이스를 유지보수하는 관리자
/*
Table
- 데이터를 담고 있는 객체
- 데이터에 대해서 생성, 조회, 수정, 삭제(CRUD)작업을 하는 대상
- 행과 열로 이루어져 있다.
Table(Eneity, Relation) : Employee 테이블과 테이블을 붙여서(조인) = Relation Column(Filed, Attribute : Emp_Id 200 201 202... Row(Record, Tuple) : 행, 200 선동일 621225-1985634 sun_di@kh.or.kr 01099546325 D9 J1 S1 8000000 0.3 90/02/06 N Domain : 하나의 컬럼(속성_이 취할 수 있는 같은 타입으로 이루어진 값의 집합 gender : 남, 여 (여기서 남, 여를 도메인이라고 함) */
--하나의 테이블 정보조회 desc employee; --desc : 내림차순 --null여부 (nullable) --1. not null : 필수요소 --2. null : 선택요소
/* Data type
- 문자형 CHAR : 고정길이 문자형(최대 2000byte) VARCHAR2 : 가변길이의 문자형(최대 4000byte)
NCHAR : char와 동일하나 유니코드 문자개수로 처리 (고정형), 1000개까지 됨. 유니코드(2byte라서) NVARCHAR2 : varchar2와 동일하나 유니코드 문자개수로 처리 (가변형), 2000개까지 됨.
char(10) 고정형으로 문자열 10byte 'korea' => 실제로는 5byte지만, 저장시에는 10byte를 모두 사용한다. 빈공백으로 잔여크기 채움 >>> 고정형 '대한민국' => 글자당 2byte지만, 저장시에는 8byte가 아닌 10byte를 모두 사용 '대한민국 서울시' => 글자2, 공백1 : 15byte지만 최대크기가 10byte로 지정되어있어 에러가 발생된다.
varchar(20) 가변형으로 문자열 20byte까지 저장가능 'korea' : 실제 5byte, 저장도 5byte '대한민국' : 실제 8byte, 저장도 8byte '대한민국 서울시' : 실제 15byte, 저장도 15byte 물론 20byte보다 크면 오류
nchar(10) 고정형, 문자열개수가 10개까지 허용 nvarchar2(10) 가변형, 문자열개수가 10개까지 허용
long : 최대 2GB까지 작성 가능한 가변형 문자타입
*/
create table tb_chartype( col1 char(8), col2 varchar2(8), col3 nchar(6), col4 nvarchar2(6) );
--데이터입력(create) Insert into tb_chartype values('abc', 'abc', 'abc', 'abc');
Insert into tb_chartype values('가다', '하다', '호호나', '푸호나'); --오라클에서 한글처리는 2byte지만, 11g xe버전은 3byte로 처리된다.
select * from tb_chartype;
--메모리에서 작업인것을 취소 rollback;
--메모리에서 작업한 내용을 실제 파일에 적용 commit;
/*
- 숫자형 number([p,s]) -- precision : 표현할 수 있는 전체 숫자 자리수(1~38) -- scale : 소수점이하 자리수(-84 ~ 127)
데이터입력값이 1234 678 일때,
데이터 타입 저장되는 값
number(7,3) 1234.678 number(7) 1235 number 1234.678 number(7,1) 1234.7 (반올림) number(5, -2) 1200 (마이너스는 소수점을 기준으로 왼쪽, 0으로 처리)
- 날짜형 : date 일자(년/월일) 및 시간(시/분/초) 정보를 관리 기본적으로는 화면에 년/월/일만 표시됨 (물론 시분초도 저장되어있음) 날짜의 연산 또는 비교가 가능함
연산 결과타입 설명
날짜+-숫자 date 하루를 1로 계산해서 처리함 날짜+-날짜 number 두 날짜의 차이를 리턴(하루가 1기준) */
select sysdate from dual; select sysdate-1 from dual; select sysdate+1 from dual;
select to_char(sysdate, 'yyyy/mm/dd hh:Mi:ss')from dual;
-- 정확한 시각정보(밀리초 이하)를 표현하기 위한 timestamp타입도 있다 select systimestamp from dual; /*
- LOB형 clob : 최대 4GB 허용가능한 문자타입 */
--실습문제 : today라는 회사의 회원테이블을 작성할 때, -- 다음 필드에 적절한 db데이터 타입을 설정해서 tb_today_member 라는 테이블을 생성하세요. /*
- id : 8자리에서 15자리
- pw : 8자리 이상 15자리
- name
- phone : -없이 11자리
- ssn : 주민번호 -없이 13자리
- mileage : 회원 마일리지
- reg_date : 가입일 */
create table tb_today_member( id varchar2(15) NOT NULL,
pw char(15) NOT NULL,
name varchar2(100) NOT NULL,
phone char(11) NOT NULL,
ssn char(13) NOT NULL,
mileage number,
reg_date date
);
--## SQL
/*
Structed Query Language : 구조화된 질의 언어
dbms사용되는 SQL문법이 조금씩 다름
DDL (Data Definition Language) 데이터베이스의 구조, 객체에 대한 언어 ex) create , drop , alter
DML (Data Manipulation Language) 저장된 데이터의 조작 ex) insert, update , delete, select ==> CRUD
DCL (Data Control Language) 사용자권한, 설정 ex) grant , revoke
TCL (Transaction Control Language) ex) commit , rollback, savepoint
*/
--##DQL
/*
데이터를 검색(추출)하기 위한 언어
DML로 분류
데이터를 조회한 결과를 ResultSet(결과집합)
-> 0개 이상의 행이 포함
-> 특정 기준에 의해서 정렬될 수 있음
select : 조회하고자 하는 컬럼명을 기술
from : 조회하고자 하는 테이블명 기술
where : 특정 행을 선출하는 조건을 기술
group by :
having :
order by : 특정 컬럼을 대상으로 오름, 내림 차순 지정 asc , desc
처리 되는 순서 From -> where -> group by -> having -> select -> order by (중요 앙앙)
*/
select * from employee;
select emp_id, emp_name,salary from employee;
select emp_id, emp_name,salary,job_code from employee where job_code='J5' order by emp_name asc;
select emp_id,emp_name,salary,job_code -- 3
from employee --1
where job_code='J5' -- 2
order by emp_name; -- 4
—1. job 테이블에서 job_name 컬럼만 출력
select job_name
from job;
--2. department테이블의 전체 컬럼을 출력
select *
from department;
--3 employee테이블에서 이름,이메일, 전화번호, 입사일을 출력
select emp_name,email,phone,hire_date
from employee;
—4. employee테이블에서 월급이 2,500,000원 이상인 사람만 출력
select *
from employee
where salary >= 2500000;
—5. employee테이블에서 월급이 350만원 이상이면서 job_code가 'J3 '인 사람의 이름
select emp_name
from employee
where salary >= 3500000 and job_code = 'J3';
--6 산술연산
— 연봉 : 월급 * 12 + (월급보너스) * 12
select emp_name,
salary,
bonus,
salary * 12,
(salary +(salary * nvl(bonus,0))) *12
from employee;
— null처리 함수 : nvl(col, val) ==> 값을 리턴
— col의 값이 null인 경우 val을 리턴
— col의 값이 null이 아닌 경우 col의 값을 리턴
select nvl(7,3), nvl(null,3), nvl('이순신','거북선'), nvl(null,'거북선')
from dual;
select nvl(bonus, 0), bonus
from employee;
— 컬럼명 별칭(alias)지정
— as "별칭" ===> as 와 "" 생략 가능
-- as는 항상 생략이 가능하지만 ""을 반드시 써야하는 경우가 있음 : 공백이 포함된 경우, 숫자로 시작되는 경우
— 컬럼에 리터럴(literal)지정
— 리터럴은 resultset의 모든 행에 반복 표시
select emp_name, salary, '원' as 단위 , 12345
from employee;
— 중복값 제거 distinct
— select절에 한번만 사용 가능, 여러 컬럼을 사용하면, 여러 컬럼을 합친 것을 고유값으로 간주
select distinct job_code
from employee;
— 연결 연산자 ||
— 문자열연결, 기타값과 문자열연결에 +연산자가 아닌 || 연산자 사용
— 문자는 왼쪽 정렬, 정수형은 오른쪽 정렬
select emp_name, salary || '원' as "연봉"
from employee;
— where절에 사용하는 연산자
/*
-1. 논리연산자
and
or
not : 해당 결과를 반전 (null 제외)
-2. 비교 연산자
=
< , >
<= , >=
<> , ^= , != : 같지 않다
between 값1 and 값2 : 값1 이상 값2 이하
like , not like : 문자 패턴비교
is null , is not null : null여부 비교 -> where bonus is not null
in , not in : 비교값 목록에 포함되었는지 여부
*/
— 부서코드 D6이고, 급여를 2000000원보다 많이 받는 사람의 이름, 부서, 월급을 출력
select emp_name,dept_code,salary
from employee
where dept_code = 'D6' and salary > 2000000;
— 직급코드가 J1이 아닌 사원들의 월급등급(sal_level)을 중복없이 출력
select distinct sal_level
from employee
where job_code != 'J1';
— 급여를 3500000원 이상, 6000000 이하 의 이름과 연봉 출력
select emp_name,salary
from employee
where salary >= 3500000 and salary <= 6000000;
— 숫자 , 날짜에 대해서 사용가능
— 입사일이 90/01/01 ~ 01/01/01인 사원을 조회
— 기본 date 형식을 사용하면 됨
select *
from employee
where hire_date between '90/01/01' and '01/01/01';
— 문자열 패턴비교 like
— 비교하려는 값이 지정한 문자패턴을 만족시키면 true를 리턴함
— 와일드카드 % _를 사용함
— % : 글자가 없든지, 있으면 true
-- _ : 임의의 문자 1개를 의미
select emp_name,salary
from employee
where emp_name like '형';
--email에서 _의 앞자리가 3자리인 직원을 조회 --이스케이핑처리 : 임의의 문자를 이스케이프 문자로 사용하고, 이를 명시
create table tb_watch(
watchname varchar2(40),
description varchar2(200)
);
insert into tb_watch values ('금시계','순금 99.99% 함유 고급시계');
insert into tb_watch valuse('은시계','고객만족도 99.99점을 획득한 고급시계');
commit;
실습문제
xxxxxxxxxx#@실습문제1. EMPLOYEE 테이블에서 이름, 연봉, 총수령액(보너스포함), 실수령액(총 수령액-(월급*세금 3%))가 출력되도록 하시오(컬럼명 변경)2. EMPLOYEE 테이블에서 이름, 근무 일수(입사한지 몇일인가)를 출력해보시오.3. EMPLOYEE 테이블에서 20년 이상 근속자의 이름,월급,보너스율를 출력하시오4. EMPLOYEE 테이블에서 이름 끝이 연으로 끝나는 사원의 이름을 출력하시오5. EMPLOYEE 테이블에서 전화번호 처음 3자리가 010이 아닌 사원의 이름, 전화번호를 출력하시오6. EMPLOYEE 테이블에서 메일주소 '_'의 앞이 4자이면서, DEPT_CODE가 D9 또는 D6이고 고용일이 90/01/01 ~ 00/12/01이면서, 월급이 270만원인 사원의 전체 정보를 출력하시오
1
select emp_name as "이름" , (salary * 12) || '원' as "연봉" , (salary + (salary * nvl(bonus,0))) * 12 as "총수령액", ((salary + (salary * nvl(bonus,0))) * 12) - (salary * 0.03) as "실수령액" from employee;
2
select emp_name as "이름",
trunc(sysdate - hire_date) as "근무 일수"
from employee;
3
select emp_name as "이름",
salary as "월급",
nvl(bonus,0) as "보너스율"
from employee
where trunc(sysdate - hire_date) >= (365 * 20);
4
select emp_name as "이름"
from employee
where emp_name like '%연';
5
xxxxxxxxxxselect emp_name as "이름",phone as "전화번호"from employeewhere phone not like '010%';
6
xxxxxxxxxxselect *from employeewhere email like '____\_%' escape '\'and (dept_code = 'D9' or dept_code = 'D6')and hire_date between '90/01/01' and '00/12/01'and salary >= 2700000;
비교연산자 in
비교 하려는 값 목록에 일치하는 것이 있는가? 있으면 true, 없으면 false
D6 , D8 부서인 사원들을 조회하라
xxxxxxxxxxselect *from employeewhere dept_code = 'D6' or dept_code = 'D8';--------select *from employeewhere dept_code in('D6','D8');
D6, D8 부서가 아닌 사원들을 조회하라
xxxxxxxxxxselect *from employeewhere dept_code != 'D6' and dept_code != 'D8';select *from employeewhere dept_code not in('D6','D8');
연산자 우선순위
- 산술 연산자
- 연결연산자 ||
- 비교연산자
- IS NULL / IS NOT NULL, LIKE , IN / NOT IN
- BETWEEN AND / NOT BETWEEN AND
- NOT
- AND
- OR
부서코드가 J7 또는 J2이고, 급여가 2000000원 이상인 사원 조회
select *
from employee
where job_code in ('J7','J2')
and salary >= 2000000;
order by
select한 컬럼에 대해서 정렬기준을 제시
order by 컬럼명 | 별칭 | 컬럼순서 {정렬방식 (asc , desc)][NULLS FIRST | LAST]
xxxxxxxxxxselect emp_namefrom employee;order by emp_name desc;select emp_id as "아이디",emp_name as "사원명",dept_code as "부서코드"from employeeorder by 아이디 desc;// 부서코드로 선 정렬 후, 그안 에서 다시 사원명으로 정렬select emp_id as "아이디",emp_name as "사원명",dept_code as "부서코드"from employeeorder by 3,2;
FUNCTION
일련의 처리과정을 반복적으로 사용하기 위해서 작성해둔 서브프로그램
호출시 값을 전달하면, 수행결과를 리턴하는 방식 (반드시 값을 리턴 , void X)
함수의 유형
단일행 처리 함수
- 문자처리함수
- 숫자처리함수
- 날짜처리함수
- 형변환함수
- 기타함수
그룹 함수
- group by
- having
단일행 처리 함수
문자 처리 함수
length : 문자 개수를 리턴
xxxxxxxxxxselect emp_name, length(emp_name),email, length(email)from employee;
lengthb : 문자열의 byte를 리턴
xxxxxxxxxxselect emp_name, lengthb(emp_name),email, lengthb(email)from employee;
instr : 찾는 문자열이 대상문자열에서 지정한 위치부터 지정한 회수번째에 나타난 위치를 반환
instr(대상문자열, 찾고자하는 문자열[,시작 인덱스 [, 몇번째]])
오라클에서 인덱스는 0기반이 아닌 1기반이다.
xxxxxxxxxxselect instr('kh정보교육원 국가정보원 정보문화사','정보',1,1),instr('kh정보교육원 국가정보원 정보문화사','정보',4),instr('kh정보교육원 국가정보원 정보문화사','정보',4,2),instr('kh정보교육원 국가정보원 정보문화사','정보',-6), -- 음수는 역방향으로 찾는다, 오른쪽에서 왼쪽으로 검색from dual;
연습문제
email컬럼에서 @의 위치인덱스값을 출력
select email,
instr(email,'@')
from employee;
lpad / rpad : lpad(string,byte[,paddingString])
byte공간에 string문자열을 쓰고, 남은 공간 왼쪽/오른쪽에 paddingString으로 채워라
xxxxxxxxxxselect lpad(email,20,'#'), rpad(email,20,'#'),lpad(email,20), rpad(email,20)from employee;
ltrim / rtrim
ltrim / rtrim(string, str)
xxxxxxxxxxselect ltrim(' kh',' '),ltrim(' kh'),ltrim('000123456','0'),ltrim('12343124556797923kh','123456789') — 삭제할 문자열은 문자단위로 처리 (하나하나)from dual;
trim : 양쪽에서 지정한 문자를 제거
xxxxxxxxxxselect trim(' kh '),trim('z' from 'zzzzzzzzkhzzzzz'),trim(leading 'z' from 'zzzzzzkhzzzzzzz'), -- 앞쪽 z만 제거trim(trailing 'z' from 'zzzzzkhzzzzzzz'), -- 뒤쪽 z만 제거trim(both 'z' from 'zzzzzzkhzzzzzzzz') -- 양쪽 z 제거from dual;요놈은 제거할 문자열 하나만 지정할 수 있다,
연습문제
1231233546356542hello48763987649387461
xxxxxxxxxxselect rtrim(ltrim('1231233546356542hello48763987649387461' , '123456789') , '123456789')from dual;
substr
substr(string, position ,[, length])
xxxxxxxxxxselect substr('showmethemoney', 5,2),substr('showmethemoney',10),substr('showmethemoney',-8,3)from dual;
lower / upper / initcap
xxxxxxxxxxselect lower('ABCDE'),upper('abcde'),initcap('abcde') -- 첫글자만 대문자 잉잉from dual;
concat : 문자 연결함수이지만 , 2개의 문자만 처리가능 , 3개 이상연결에는 ||사용할 것
xxxxxxxxxxselect concat('abc','def')from dual;select 'abc'||'def'||'ghi'from dual;
replace : replace(대상문자열,fromString, toString)
xxxxxxxxxxselect replace('show me the money','money','gimothy')from dual;
연습문제
사원명에서 성만 중복없이 출력(성은 한글자라고 가정)
xxxxxxxxxxselect distinct substr(emp_name , 1 , 1)from employee;order by 1;employee테이블에서 남자만 사원번호,사원명,주민번호 출력(주민 뒷자리 *출력)
xxxxxxxxxxselect emp_name as "사원명",emp_id as "사원번호",rpad(substr(emp_no , 1,7) ,13, '*')from employeewhere substr(emp_no , 8 , 1) = '1' or substr(emp_no , 8 , 1) = '3';
- 다음 tb_files테이블에서 파일명만 출력
xxxxxxxxxxcreate table tb_files(fileno number(3),filepath varchar2(500));insert into tb_files values(1,'C:\abc\def\salesinfo.xls);insert into tb_files values(2,'C:\music\안녕.mp3);insert into tb_files values(3,'C:\movies\서치.mp4);
xxxxxxxxxxselect substr(filepath,instr(filepath , '\' , -1) + 1)from tb_files;
숫자 처리 함수
abs : 절대값
xxxxxxxxxxselect abs(10),abs(-10)from dual;mod : 나머지 구하는 함수
%가 아닌 mod함수를 사용
xxxxxxxxxxselect mod(10,3),mod(10,2),mod(10,4)from dual;round : 반올림
round(nunber, [, position])
xxxxxxxxxxselect round(123.456 , 3),round(123.456 , 2),round(123.456 , 1),round(123.456 , 0), -- 0은 생략 가능round(123.456 , -1),round(123.456 , -2),from dual;ceil
xxxxxxxxxxselect cell(123.456)from dual;12.3456을 소수점 둘째자리까지 올림처리해서 표현하기
xxxxxxxxxxselect cell(123.456*100)from dual;floor
xxxxxxxxxxselect floor(10.5),foor(10.51)from dual;trunk : trunc(number , position)
xxxxxxxxxxselect trunc(123.456 , 1),trunc(123.456 , 2),trunc(123.456),trunc(123.456 , -1)from dual;
날짜처리함수
sysdate
xxxxxxxxxx// 시분초 표현select to_char(sysdate - 1, 'yyyy-mm-dd hh24:mi:ss'),to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss'),to_char(sysdate + 1, 'yyyy-mm-dd hh24:mi:ss')from dual;// 어제 , 오늘 , 내일 표현// 하루를 1로 치환해서 연산select sysdate - 1, sysdate , sysdate + 1from dual;// 한시간 전select to_char(sysdate - 1 / 24, 'yyyy-mm-dd hh24:mi:ss')from dual;// 30분전select to_char(sysdate - (30*1/24/60), 'yyyy-mm-dd hh24:mi:ss')from dual;
add_months : add_months(date, number);
xxxxxxxxxxselect add_months(sysdate , 1)from dual;// 입사 후 3개월 후 날짜 조회select emp_name, hire_date, add_months(hire_date , 3);from dual;months_between : months_between(date1, date2)
date1 과 date2의 날짜 차이를 month단위로 리턴
1이 리턴
xxxxxxxxxxselect months_between(add_months(sysdate , 12) - sysdate)from dual;// 수료 일로부터 남은 개월수select months_between(to_date('19/03/12', 'yy/mm/dd') , sysdate)from dual;
연습문제
근무개월수 구하기
xxxxxxxxxxselect trunc(months_between(sysdate , hire_date)) || '개월' as "근무 개월수"from employee;
군복무 1년 6개월, 전역일, 전역일까지 먹어야 할 짬밥의 수
xxxxxxxxxxselect add_months(sysdate , 18) as "전역일",(add_months(sysdate , 18) - sysdate) * 3 as "짬밥 수"from dual;
next_day : 인자로 전달받은 요일데이터를 토대로 가장 빠른 해당요일을 리턴
next_day(date, string | number)
xxxxxxxxxx-- 요일을 추출select to_char(sysdate, 'day') "월요일",to_char(sysdate, 'dy') "월",to_char(sysdate, 'd') "1" -- 일요일 1from dual;select next_day(sysdate, 'thursday'),next_day(sysdate, 'monday'),next_day(sysdate, 6)from dual;
last_day : 해당일자를 기준으로 해당 월의 말일을 리턴
xxxxxxxxxxselect last_day(sysdate)from dual;extract : 특정일에서 년월일 정보를 추출
extract(year|month|day from date)
xxxxxxxxxxselect extract(year from sysdate) "year",extract(month from sysdate) "month",extract(day from sysdate) "day"from dual;trunc : trunc(date) : 시분초 정보를 00:00:00으로 처리
xxxxxxxxxxselect to_char(trunc(sysdate), 'yyyy-mm-dd hh24:mi:ss'),to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss')from dual;
// docker 의 컨테이너로 진입
xxxxxxxxxxdocker exec -t -i container_name /bin/bash
// /etc/localtime 링크
xxxxxxxxxxsudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
xxxxxxxxxx-- 1select department_name as "학과 명",category as "계열"from tb_department;-- 2select department_name || '의 정원은 ' || capacity || ' 입니다.' as "학과별 정원"from tb_department;-- 3select student_namefrom tb_studentwhere absence_yn = 'Y'and department_no = '001'and (substr(student_ssn , 8 , 1) = '2' or substr(student_ssn , 8 , 3) = '3');-- 4select student_namefrom tb_studentwhere student_no in('A513079','A513090','A513091','A513110','A513119')order by 1;-- 5select department_name as "학과 이름",category as "계열"from tb_departmentwhere capacity >= 20 and capacity <= 30;-- 6select professor_namefrom tb_professorwhere department_no is null;-- 7select *from tb_studentwhere department_no is null;-- 8select class_no as "과목 이름"from tb_classwhere preattending_class_no is not null;-- 9select distinct categoryfrom tb_departmentorder by 1;-- 10select student_no as "학번",student_name as "이름",student_ssn as "주민번호",student_address as "주소"from tb_studentwhere absence_yn = 'N'and student_address like '%전주%'
형변환 함수
xxxxxxxxxxto_char() to_number()-----------> ----------->DATE CHARACTER NUMBER<---------- <----------to_date() to_char()
xxxxxxxxxxselect to_char(sysdate-1, 'yyyy-mm-dd-hh24:mi:ss') "어제", to_char(sysdate, 'yyyy-mm-dd-hh24:mi:ss') "오늘", to_char(sysdate+1, 'yyyy-mm-dd-hh24:mi:ss') "내일"from dual;
날짜에서 앞에 0 지우기
xxxxxxxxxxselect emp_name "사원명", to_char(hire_date, 'fmyyyy-mm-dd (dy)') "입사일" --fm은 공백제거 (0을 제거함!)from employee;
sysdate 연산
xxxxxxxxxx--2. 현재시각으로부터 1일 2시간 3분 4초뒤를 출력 (yyyy-mm-dd hh24:mi:ss)select (to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss')) "현재", (to_char(sysdate+1, 'yyyy-mm-dd hh24:mi:ss')) "+1일뒤", (to_char(sysdate+(1 + (1/24)*2), 'yyyy-mm-dd hh24:mi:ss')) "+2시간뒤", (to_char(sysdate+((1 + (1/24)*2)+((1/24)/60)*3), 'yyyy-mm-dd hh24:mi:ss')) "+3분뒤", (to_char(sysdate+((1 + (1/24)*2)+((1/24)/60)*3) + (1/24/60/60*4), 'yyyy-mm-dd hh24:mi:ss')) "+4초뒤"from dual;1이 1일이다
to_char 포맷
xxxxxxxxxx-- to_char(number([,format])/*, ==> 9.999 : 세자리마다 콤마. ==> 9.99 : 소수점이하 2자리까지 표현9 ==> 999 해당 자리의 숫자0 ==> 900 해당자리의 숫자가 없을 시 0으로 표현$ ==> $999 통화기호L ==> L999 Local통화로 표시 >> 한국은 \으로 표시됨fm ==> fm999 포맷9로부터 치환된 공백 혹은 소수점 이하 0을 제거함*/select to_char(1234567, '999,999,999'), to_char(1234567, '999,999'), -- << 오류남 범위를 벗어남 to_char(1234567, 'L999,999,999'), to_char(1234567, '999,999,999.999'), to_char(1234567, 'fm000,000,000.000')from dual;
xxxxxxxxxxselect trunc((to_date('201903122130','yyyymmddhh24mi') - sysdate) * 24 *60 * 60) -- 초 구하기from dual;-- ?일 ?시간 ?분 ?초 남았습니다.--151.201608~~~~~~~~~~~~~--일단위 -> 초단위 -> 결과도출? == 초select trunc(?/60/24/24) as "일",mod(trunc(?/60/60),24) as "시간",mod(tunc(?/60),60) as "분",mod(?,60) as "초"from dual;
to_number
to_number(character[, format])
1,000,000,000 - 50,000,000
xxxxxxxxxxselect to_number('1,000,000,000','9,999,999,999') - to_number('50,000,000','999,999,999')from dual;
자동 형변환
xxxxxxxxxxselect '1000' + '100',to_number()from dual;
nvl
null처리 함수 nvl(컬럼, null일 경우 값)
nvl2(col,val1,val2)
col의 값이 null이 아니면 val1, null이면 val2
xxxxxxxxxxselect nvl2(7,3,2),nvl2(null,3,2),nvl2('거북선','이순신','이성계'),nvl2('null','이순신','이성계'),from dual;
보너스가 있는 사원은 보너스있음, 없으면 보너스없음으로 표시하세요,
xxxxxxxxxxselect emp_name,bonus,nvl2(bonus,'보너스 있음','보너스 없음')from employee;
기타함수
greatest , least
숫자,문자,날짜 비교
xxxxxxxxxxselect greatest('이재선','한동준','김민성'),least('이재선','한동준','김민성'),greatest(1,2,3),least(1,2,3),greatest(sysdate+1,sysdate,sysdate-1),least(sysdate+1,sysdate,sysdate-1)from dual;decode
선택함수
switch문과 흡사함
decode(표현식,조건1,결과1,조건2,결과2) - 표현식은 반드시 값이여야 함
사원명과 직급코드에 따른 직급명으로 출력
J1 대표 , J2 부사장 , J3 부장 , J4 차장, J5 과장, J6 대리 , J7 사원
xxxxxxxxxxselect emp_name,job_code,decode(job_code,'J1','대표','J2','부사장','J3','부장','J4','차장','J5','과장','J6','대리','J7','사원'),decode(job_code,'J1','대표','J2','부사장','J3','부장','평사원')from employee;case
case when 조건1 then 결과1
when 조건2 then 결과2
...
else 결과
end
case 조건
when 조건1 then 결과1
when 조건2 then 결과2
...
else 결과
end
xxxxxxxxxxselect emp_name,case when substr(emp_no,8,1) = '1' then '남'when substr(emp_no,8,1) = '3' then '남'else '여'end "성별",case when substr(emp_no,8,1) in ('1','3') then '남'else '여'end "성별"from employee;xxxxxxxxxxselect emp_name,case substr(emp_no,8,1)when '1' then '남'when '3' then '남'else '여'endfrom employee;사원 나이를 조회 , 사원명, 주민번호 , 성별 , 나이 (현재년도 - 출생년도 + 1)
xxxxxxxxxxselect emp_name as "사원명",emp_no as "주민번호",case substr(emp_no,8,1)when '1' then '남'when '3' then '남'else '여'end as "성별",case when substr(emp_no,8,1) in('1','2') then to_char(sysdate,'yyyy') - to_char(substr(emp_no,1,2) + 1900)+1else to_char(sysdate,'yyyy') - to_char(substr(emp_no,1,2)+2000)+1end as "나이"from employee;select extract(year from sysdate)- (to_number(substr(emp_no,1,2))+case when substr(emp_no,8,1) in ('1','2') then 1900else 2000 end) + 1 as "나이"from employee;
날짜 포맷 (rr을 사용할 시)
현재년도 입력년도 처리년도
00~49 00~49 현재세기(2000년도)
50~99 전세기(1900년도)
50~99 00~49 다음세기(2100년도)
50~99 현재세기(2000년도)
ex)
현재년도 입력년도 rr yy
1994 95 1995 1995
04 2004 1994
4자리인 경우엔 차이가 없구요. 2자리인경우엔 차이가 있습니다. yy 는 무조건 '20'이 앞에 붙습니다. rr 은 50년을 기준으로 작으면 '20' 크거나 같으면 '19'가 붙습니다. TO_DATE('00','yy') ==> 2000 TO_DATE('50','yy') ==> 2050 TO_DATE('00','rr') ==> 2000 TO_DATE('50','rr') ==> 1950
그룹함수
하나의 행이 아니라, 여러행을 그룹으로 묶어서 종합, 평균 등의 값을 리턴하는 함수
sum
xxxxxxxxxxselect sum(salary)from employee;avg
xxxxxxxxxxselect avg(salary)from employeecount
xxxxxxxxxxselect count(*) as "총 사원수"from employeeselect count(*) as "보너스 없는 사원"from employeewhere bonus is null;min , max
xxxxxxxxxxselect max(salary),max(salary)from employee;select max(hire_date),max(hire_date)from employee;
2번 pdf
xxxxxxxxxx--1select student_no as "학번",student_name as "아룸",entrance_date as "입학년도"from tb_studentorder by 3;--2select professor_name as "교수 이름",professor_ssn as "주민번호"from tb_professorwhere length(professor_name) != 3;--3select professor_name as "교수 이름",extract(year from sysdate) -(19 || substr(professor_ssn,1,2)) as "나이"from tb_professororder by 2;--4select substr(professor_name, 2) as "이름"from tb_professor;--5select student_no as "학번",student_name as "이름"from tb_studentwhere extract(year from entrance_date) - (1900 + substr(student_ssn , 1 , 2)) = 20;--6select to_char(to_date('20/12/25' , 'yy/mm/dd') , 'dy') as "크리스마스 요일"from dual;--8select student_no as "학번",student_name as "이름"from tb_studentwhere substr(student_no ,1,1) != 'A';select student_no as "학번",student_name as "이름"from tb_studentwhere extract(year from entrance_date) < 2000;--9select round(avg(point),1)from tb_gradewhere student_no = 'A517178';--10select department_no as "학과번호",count(*)from tb_studentgroup by department_noorder by 1;--11select count(*)from tb_studentwhere coach_professor_no is null;select * from tb_student;--12select to_char(to_date(term_no ,'yyyymm'), 'yyyy') as "년도",round(avg(point),1) as "년도 별 평점"from tb_gradewhere student_no = 'A112113'group by to_char(to_date(term_no ,'yyyymm'), 'yyyy')order by 1;--13select department_no,sum(decode(absence_yn,'Y',1,0))from tb_studentgroup by department_noorder by 1;
group by
세부적인 그룹을 지정해서 그룹함수를 사용할 수 있다.
부서별 salary의 합계
xxxxxxxxxxselect dept_code,sum(salary)from employeegroup by dept_code;select *from employeewhere dept_code is null; --null도 하나의 그룹으로 친다--직급별 사원수를 조회하고, 직급순으로 정렬하라select job_code, count(*) 사원수from employeegroup by job_codeorder by 1;-- 부서코드별 급여의 합계, 급여의 평균(정수처리),인원수 조회select dept_code,sum(salary),trunc(avg(salary))from employeegroup by dept_code;--부서코드별로 보너스를 지급받는 사원 수를 조회select dept_code,count(*)from employeewhere bonus is not nullgroup by dept_codeorder by 1;select dept_code,count(bonus) -- 컬럼의 값이 null인 행을 제외하고 연산from employeegroup by dept_codeorder by 1;-- 두개이상의 컬럼을 대상으로 할 수 있다select dept_code , job_code, count(*)from employeegroup by job_code, dept_codeorder by 1,2;-- employee테이블에서 직급코드가 J1인 사원을 제외하고, 직급별 사원수, 평균 급여를 조회select job_code,count(*),trunc(avg(salary))from employeewhere job_code != 'J7'group by job_code;-- employee테이블에서 직급코드가 J1인 사원을 제외하고,-- 인사년도별 인원수를 조회해서, 입사년 기준으로 정렬할 것select extract(year from hire_date),count(*)from employeewhere job_code != 'J1'group by extract(year from hire_date)order by 1 ;-- group by 안에 있는 것은 컬럼안에 존재 해야함-- employee테이블에서 성별 인원수와 급여의 평균을 출력할 것select decode(substr(emp_no,8,1),'1' , '남' , '3', '남' ,'여'),count(*),trunc(avg(salary))from employeegroup by decode(substr(emp_no,8,1),'1' , '남' , '3', '남' ,'여');-- 부서내 성별 인원수를 구하세요select dept_code,decode(substr(emp_no,8,1),'1' , '남' , '3', '남' ,'여'),count(*)from employeegroup by dept_code , decode(substr(emp_no,8,1),'1' , '남' , '3', '남' ,'여')order by 1;
Having
그룹함수 값을 구해야하는 그룹에 조건은 Having절에 기술함
그룹함수를 사용한 where은 쓸수 없다
xxxxxxxxxx-- 부서별 급여평균이 300만원 이상인 부서들만 부서명, 급여평균을 출력select dept_code as "부서명",trunc(avg(salary)) || '원' as "급여평균"from employeehaving trunc(avg(salary)) >= 3000000group by dept_codeorder by 1;-- 부서별인원이 5명 이상인 부서만 출력select dept_code,count(*)from employeehaving count(*) >= 5group by dept_code;-- 매니저가 관리하는 사원이 2명 이상인 매니저 아이디와 관리하는 사원 수를 출력select manager_id as "매니저 아이디",count(*) as "관리하는 사원의 수"from employeegroup by manager_idhaving count(*) >= 2 and manager_id is not nullorder by 1;
소계, 누계를 처리하기 : rollup, cube
rollup : 가장 먼저 지정한 그룹별 합계와 총 합계를 출력
cube : 그룹으로 지정된 모든 그룹에 대한 합계와 총 합계를 출력
그룹지정한 컬럼이 하나인 경우 , rollup 과 cube의 결과는 같다
xxxxxxxxxxselect dept_code,count(*)from employeegroup by rollup(dept_code)order by 1;select dept_code,count(*)from employeegroup by cube(dept_code)order by 1;
grouping함수
group by절에 의해서 산출된 컬럼은 0을 리턴
rollup, cube절에 의해서 산출된 컬럼은 1을 리턴
xxxxxxxxxxselect dept_code,grouping(dept_code),count(*)from employeegroup by cube(dept_code)order by 1;select dept_code,decode(grouping(dept_code),0,nvl(dept_code,'인턴'),1,'총계') as "부서",count(*)from employeegroup by cube(dept_code)order by 1;--부서별 급여정도 출력(null처리 할것)select case grouping(dept_code)when 0 then nvl(dept_code, '인턴')else '총계' end 부서코드,sum(salary)from employeegroup by rollup(dept_code)order by 1;--두개이상의 컬럼으로 그룹핑하고, rollup/cube를 사용하면--rollup--부서내 직급별 인원정보select dept_code,job_code,decode(grouping(dept_code), 0 , nvl(dept_code,'인턴') , '총계'),decode(grouping(job_code),0,job_code,'소계') as "직급",count(*)from employeegroup by rollup(dept_code,job_code) -- 롤업은 첫번째 컬럼에서 두번째 컬럼 순으로 진행order by 1,2;select dept_code,job_code,decode(grouping(dept_code), 0 , nvl(dept_code,'인턴') , '총계'),decode(grouping(job_code),0,job_code,'소계') as "직급",count(*)from employeegroup by cube(dept_code,job_code) -- cube은 첫번째 컬럼에서 두번째 컬럼 순으로 진행 , 그리고 반대순인 두번째컬럼에서 첫번쨰 컬럼순으로도 한번 더 진행order by 1,2;
Join
Join : 가로합침, 컬럼과 컬럼을 합치는 것
union : 세로합침, 행과 행을 합치는 것
여러테이블의 레코드를 조합하여 하나의 행으로 표현한 가상 테이블을 생성
연관성이 있는 컬럼을 기준으로 조합
xxxxxxxxxxselect *from employeewhere emp_name = '송종기';select *from department;-- 조인으로 합체 (equal-join)select *from employee join departmenton employee.dept_code = department.dept_idwhere emp_name = '송종기';
join 두가지 종류
- equal-join : 동등조건으로 조인하는 경우 , = 을 이용
- non equal-join : 동등조건이 아닌 between and , is null , is not nul , in등을 이용한 조인
join 두가지 문법
- ANSI 표준문법 : DBMS와 상관없이 공통적으로 사용하는 표준 SQL
- Oracle 표준문법 (벤더사 제공하는 문법) : Oracle에서 사용하는 구문 , 콤마(오라콤마)
equl - Join
- 조인하는 컬럼명이 다른 경우
xxxxxxxxxx-- 부서명과 지역명을 출력하라select distinct *from location join departmenton location.local_code = department.location_id;
- 조인하는 컬럼명이 같을 경우
xxxxxxxxxx-- employee , jobselect * from employee;select * from job;select *from employee join jobon employee.job_code = job.job_code;-- 별칭을 줄수도 있음select *from employee E join job Jon E.job_code = J.job_code;select emp_name,E.job_code,job_namefrom employee E join job Jon E.job_code = J.job_code;-- 컬럼명이 같을경우에 한해서 on 이 아닌 using 키워드를 사용할 수 있음-- 테이블명이나 별칭을 사용하면 ERRORselect emp_name,job_code,job_namefrom employee join jobusing(job_code)-- 지역명과 국가명을 출력(location, nation)select * from location;select * from nation;select national_name,local_namefrom nation join locationusing(national_code);
eqal-join 종류
inner join : 보통 교집합 생각하면됨
기본 조인 : (inner) join
outer join : 합집합
left (outer) join : 왼쪽 외부 조인
right (outer) join : 오른쪽 외부 조인
full (outer) join : 완전 외부 조인
- cross join
- self join
- multiple join

inner join
교집합, 기존되는 컬럼에서 null이나 해당되는 값이 없다면 제외
xxxxxxxxxx-- employee , departmentselect * from employee; -- 23개 rowselect * from department; -- 9개 rowselect * -- 21개 rowfrom employee inner join departmenton employee.dept_code = department.dept_id;
outer join
left outer join : 좌측 table 모두 반환, 우측 테이블 조건 컬럼에 일치하는 row만 반환
xxxxxxxxxxselect *from employee left outer join departmenton employee.dept_code = department.dept_id;
right outer join : 우측 table 모두 반환, 좌측 테이블 조건 컬럼에 일치하는 row만 반환
xxxxxxxxxxselect *from employee right outer join departmenton employee.dept_code = department.dept_id;
full outer join : 조건 컬럼에 일치하지않아도 , 우측 table , 좌측 table 모두 row 반환
xxxxxxxxxxselect *from employee full outer join departmenton employee.dept_code = department.dept_id;
조인 결과 : employee(23row) , department(9row)
- inner join : 21
- left outer join : 23 = 21 + 2
- right outer join : 24 = 21 + 3
- full outer join : 26 = 21 + 2 + 3
3번 pdf
xxxxxxxxxx--1select student_name as "학생 이름",student_address as "주소지"from tb_studentorder by 1;--2select student_name as "학생 이름",student_ssn as "주민 번호"from tb_studentwhere absence_yn = 'Y'order by 2 desc;--3select student_name as "학생 이름",student_no as "학번",student_address as "거주지 주소"from tb_studentwhere (student_address like '%경기도%' or student_address like '%강원도%')and student_no not like 'A%'order by 2;--4select tb_professor.professor_name as "교수 이름",tb_professor.professor_ssn as "주민 번호"from tb_professor join tb_departmentusing(department_no)where tb_department.department_name = '법학과'order by 2;--5select student_no as "학생 학번",point as "학점"from tb_gradewhere class_no = 'C3118100' and term_no = '200402'order by 2 desc , 1;--6select student_no as "학생 번호",student_name as "학생 이름",tb_department.department_name as "학과 이름"from tb_student join tb_departmentusing(department_no)order by 2;--7select class_name as "과목 이름",department_name as "학과 이름"from tb_class join tb_departmentusing(department_no);--8select class_name as "과목 이름",tb_professor.professor_name as "교수 이름"from tb_class join tb_professorusing(department_no)order by 1;--9select * from tb_class;select * from tb_class_professor;select * from tb_department;select * from tb_grade;select * from tb_professor;select * from tb_student;
cross join
좌측 테이블 * 오른쪽 테이블
모든 경우의 수를 구하고, 행의 수는 (좌측테이블 행 개수 * 우측테이블 행 개수)
cartesian product : 카테시안의 곱
xxxxxxxxxxselect *from employee cross join department; -- 207select count(*) from employee; --23select count(*) from department; --9-- 평균월급과 평균월급과의 차이select trunc(avg(salary)) -- trunc(avg(salary))를 하나의 테이블로 인식from employee;select emp_id,emp_name,salary,평균,salary - 평균 as "월급차이"from employee cross join(select trunc(avg(salary)) as "평균"from employee);
self join
같은 테이블을 조인해서 결과셋을 얻고자 할 때 사용
xxxxxxxxxx--사원명과 해당사원의 매니저명을 조회select E.emp_id,E.emp_name,E.manager_id,M.emp_namefrom employee E join employee Mon E.manager_id = M.emp_id; --15
multiple join(다중 조인)
xxxxxxxxxx--사원명, 부서명, 지역명을 출력select emp_name, dept_code from employee;select dept_id,dept_title, location_id from department;select local_name,local_name from location;select emp_name,dept_code,local_namefrom employee Ejoin department D on E.dept_code = D.dept_idjoin location L on D.location_id = L.local_code;--직급이 대리이면서, asia지역에 근무하는 직원을 조회--사번,이름,직급명,부서명,근무지역명,급여select emp_id as "사번",emp_name as "이름",J.job_name as "직급명",d.dept_title as "부서명",L.local_name as "근무지역명",salary as "급여"from employee Ejoin JOB J on E.job_code = J.job_codejoin department D on E.dept_code = d.dept_idjoin location L on D.location_id = L.local_codewhere J.job_name = '대리' and L.local_name like 'ASIA%';
Oracle 전용 문법
join키워드 대신 ,(콤마)를 사용
on절 대신 where절을 사용
xxxxxxxxxx-- 조인하는 컬럼명이 다를 경우select dept_title, local_namefrom department D, location Lwhere D.location_id = L.local_code;-- 조인하는 컬럼명이 같을 경우 (Oracle 전용에는 Using이 없음)-- employee, job;select * from employee;select * from job;
outer join
where절에 (+)이 없는 쪽을 우선 다출력
left : (+) 우항에 작성
right : (+) 좌항에 작성
xxxxxxxxxxselect *from employee E, department Dwhere E.dept_code = D.dept_id(+); --employee E기준으로 join을 하고 싶으면 D.dept_id에 +표시
self join
xxxxxxxxxxselect E.emp_id,E.emp_name,E.manager_id,M.emp_namefrom employee E, employee Mwhere E.manager_id = M.emp_id
multiple join (oracle전용구문의 다중조인에서는 테이블 순서가 중요하지 않다.)
xxxxxxxxxxselect emp_name,dept_title,local_namefrom employee E, department D, location Lwhere E.dept_code = D.dept_id and D.location_id = L.local_code;
non - equal join
동등조건이 아닌 기준으로 조인
xxxxxxxxxxselect * from employee;select * from sal_grade;select emp_name,E.salary,S.sal_levelfrom employee E join sal_grade Son E.salary between S.min_sal and S.max_sal;
집합연산자(Set Operator)
여러개의 질의결과를 가로로 연결하여 합치거나 뺀 후, 하나로 결합하는 방식
조건1. slect절의 컬럼수가 동일해야됨
조건2. select절의 동일위치계 존재하는 컬럼의 데이터타입이 상호 호환 가능해야함
- union 합집합
- union all 합집합
- intersect 교집합
- minus 차집합
A = {1,2,3,4,5};
B = {3,5,7,9};
union --- A U B = {1,2,3,4,5,7,9}; ==> 중복 제거 O , 첫번째 컬럼기준으로 오름차순
union all --- A U B = {1,2,3,4,5,3,5,7,9}; == > 중복 제거 X , 그냥 연결
intersect --- A n B = {3,5};
minus --- A - B = {1,2,4};
xxxxxxxxxx--union-- 가로합침, 중복데이터제거, 첫번째 컬럼 기준정렬(오름차순)-- 부서코드가 D5인 사원의 사원번호, 사원명, 부서코드, 급여를 출력select emp_id,emp_name,dept_code,salaryfrom employeewhere dept_code = 'D5' --6-- 급여가 300만원보다 많은 사원의 사원번호, 사원명, 부서코드, 급여를 출력select emp_id,emp_name,dept_code,salaryfrom employeewhere salary > 3000000; --9-- 위에 두개를 unionselect emp_id,emp_name,dept_code,salaryfrom employeewhere dept_code = 'D5' --6unionselect emp_id,emp_name,dept_code,salaryfrom employeewhere salary > 3000000; --9-- 컬럼 타입과 위치가 모두 동일 해야함select 123, 'zzz' from dualunionselect 'abc' 456 from dual (XXXX)select 123, 'zzz' from dualunionselect 456, 'abc' from dualunionselect 345, 'def' from dual;
grouping sets
group by + union all 을 한번에 처리
xxxxxxxxxx--부서코드별 인원수, 직급코드별로 인원수를 동시에 출력select dept_code,job_code,count(*)from employeegroup by grouping sets(dept_code,job_code);select dept_code,null job_code,count(*)from employeegroup by dept_codeunion allselect null dept_code,job_code,count(*)from employeegroup by job_code;-- 성별, 부서별, 직급별 급여평균을 구하시오select decode(substr(8,1,1),'1','남','3','남','여'),dept_code,job_code,trunc(avg(salary))from employeegroup by grouping sets(decode(substr(8,1,1),'1','남','3','남','여'),dept_code,job_code)
SubQuery
하나의 SQL문안에 포함된 또 다른 SQL문
메인쿼리에 종속된 서브쿼리가 존재함
존재하지 않는 조건에 근거한 값들을 검색 할 때
서브쿼리는 반드시 소괄호로 묶을 것
서브쿼리는 연산자의 오른쪽 위치 할 것.
xxxxxxxxxx--노옹철사원의 관리자이름을 출력하라.-- 셀프조인select E.emp_namefrom employee E left join employee Mon E.manager_id = m.emp_idwhere e.emp_name = '노옹철';-- 서브쿼리select emp_namefrom employeewhere emp_id = (select manager_idfrom employeewhere emp_name ='노옹철');
xxxxxxxxxx--전직원의 급여 평균보다 많은 급여를 받고 사원의 사번,이름,직급코드,급여를 출력--1. 급여 평균--2. 급여평균보다 많은 급열 받는 사원 필터링select emp_id as "사번",emp_name as "이름",sal_level as "직급코드",salary as "월급"from employeewhere salary >= (select avg(salary)from employee);
서브쿼리의 유형
- 단일행 서브쿼리(단일컬럼)
- 다중행 서브쿼리(단일컬럼)
- 다중열 서브쿼리(단일행)
- 다중행 다중열 서브쿼리
- 상관 서브쿼리
- 스칼라 서브쿼리
- 인라인 뷰
1.단일행 서브쿼리
xxxxxxxxxx-- 1.윤은혜사원과 급여가 같은 사원의 사번,사원명,급여 출력select emp_id 사번,emp_name 사원명,salary 급여from employeewhere emp_name != '윤은해'and salary = (select salaryfrom employeewhere emp_name = '윤은해');-- 2.기본급여가 가장 많은 사람과 제일 적은 사람을 출력select *from employeewhere salary = (select max(salary)from employee)or salary = (select min(salary)from employee);-- 3. D1, D2부서에 근무하는 사원중에 급여가 D5부서의 평균급여보다 많은 사원을 조회select emp_id 사번,emp_name 사원명,dept_code 부서코드,salary 급여from employeewhere dept_code in ('D1','D2')and salary > (select avg(salary)from employeewhere dept_code = 'D5');
2. 다중행 서브쿼리
IN, NOT IN, ANY , ALL , EXISTS
xxxxxxxxxx--in--송종기, 하이유가 속한 부서의 사원정보 출력select dept_codefrom employeewhere emp_name in ('송종기','하이유');select emp_name, dept_codefrom employeewhere dept_code in('D5','D9');-- 서브쿼리로 합쳐 보리기select emp_name,dept_codefrom employeewhere dept_code in (select dept_codefrom employeewhere emp_name in ('송종기','하이유'));-- not in-- 송종기와 하이유가 근무하지 않는 부서를 다니는 닝겐들select emp_name , dept_codefrom employeewhere dept_code not in (select dept_codefrom employeewhere emp_name in('송중기','하이유'));--any(some)--서브쿼리의 결과 중에서 하나라도 참이면 참/*x > any(..) : any절의 어떤 값보다도 크기만 하면 참, 최소값보다 크기만 하면 참x < any(..) : any절의 어떤 값보다도 작기만 하면 참, 최대값보다 작기만 하면 참x = any(..) : any절의 어떤 값과 일치하기만 하면 참, in 과 동일x != any(..) : any절의 하나 이상의 값과 일치하지 않으면 참*/select emp_name, salaryfrom employeewhere salary > any(2000000,5000000);-- all-- 서브쿼르이 결과 중에서 모두 참이면 참./*x> all(..) 모든 값보다 크면 참, 최대값 보다 크면 참x > all(..) 모든 값보다 작으면 참, 최소값 보다 작으면 참x = all(..) 모든 값과 같으면 참, 보통 false처리x != all(..) 모든 값과 다르면 참, not in하고 동일함*/select emp_name , salaryfrom employeewhere salary > all(2000000,5000000,3000000); -- 50000000이랑만 비교하게 됨select emp_name , salaryfrom employeewhere salary < all(2000000,5000000,3000000); -- 20000000이랑만 비교하게 됨-- D2부서의 모든 사원보다 적은 급여를 받는 사원 조회select *from employeewhere salary < all(select salaryfrom employeewhere dept_code ='D2');--부서별 평균급여중 가장 높은 부서의 부서코드, 평균급여를 조회--allselect dept_code,trunc(avg(salary))from employeegroup by dept_codehaving avg(salary) >= all(select trunc(avg(salary))from employeegroup by dept_code); --최대값과 비교해서 같거나 크면 되는데, 최대값과 똑같은 결과를 원할 떄 씀select trunc(avg(salary))from employeegroup by dept_code;--exist--서브쿼리의 결과 중에서 만족하는 행이 하나라도 존재하면 참select *from employeewhere 1=1; --무적권 참select *from employeewhere 1=2; -- 절대 거짓--행을 검사 할 때 서브쿼리의 결과셋에 행이 존재하면 참 => 메인쿼리의 해당행을 resultset에 포함시킴--메인쿼리행을 검사할때 서브쿼리의 결과셋에 행이 존재하지 않으면 거짓-- 메인쿼리의 해당행을 resulltset에 포함시키지 않음select *from employeewhere exists(select emp_name from employee where dept_code='D5');--부하직원이 한명이라도 있는 직원을 출력select emp_id, emp_namefrom employee Ewhere exists(select emp_namefrom employeewhere manager_id = E.emp_id);--not exists를 이용해서 최대/최소값 구하기--가장 많은 급여를 받는 사원을 조회--행이 존재하면 거짓, 행이 존재하지 않으면 참select emp_id, salaryfrom employee Ewhere not exists(select 1from employeewhere salary > E.salary --이 부분에서는 계속 돈다. 메인쿼리 salary받아와서 한바퀴 쭈욱 받고);select emp_id, salaryfrom employee Ewhere not exists(select 1from employeewhere salary < E.salary);
실습문제
xxxxxxxxxx--1. 2011년 1월1일 이전 입사자 중에서--2011년 1월 1일 이후의 어떤 입사자보다 급여를 적게 받는 사원 조회--사원명,입사일,급여 출력select emp_name 사원명,hire_date 입사일,salary 급여from employeewhere extract(year from hire_date) < 2011and salary < all(select salaryfrom employeewhere extract(year from hire_date) >= 2011);order by 2;--2. 어떤 'J4'직급의 사원보다도 많은 급여를 받은 직급이 'J5,J6,J7'인 사원 출력--사원명,직급코드,급여select emp_name 사원명,job_code 직급코드,salary 급여from employeewhere job_cOde in ('J5','J6','J7')and salary > all(select salaryfrom employeewhere job_code = 'J4');order by 3;--3. 직급이 대표,부사장이 아닌 모든 사원 출력(메인쿼리 조인하지 말것)--부서코드 , 사원명select *from employeewhere job_code != all(select job_codefrom jobwhere job_name in ('대표','부사장'))order by job_code;--4.Asia1지역에 근무하는 사원정보 출력(메인쿼리 조인하지 말것)--부서코드 , 사원명select dept_code,emp_namefrom employeewhere dept_code = any(select dept_idfrom departmentwhere location_id = (select local_codefrom locationwhere local_name = 'ASIA1'));select dept_idfrom department E join location Lon E.location_id = L.local_code;
3. 다중열 서브쿼리
하나의 컬럼이 아닌 여러 컬럼
xxxxxxxxxx--퇴사한 여직원--퇴사한 여직원 같은 부서, 같은 직급에 해당하는 사원을 조회select emp_name,dept_code,job_codefrom employeewhere (dept_code, job_code) in (select dept_code, job_codefrom employeewhere quit_yn = 'y');--이때, 서브쿼리 결과가 단일행이든, 다중행이든 처리방식은 같다.--직급별 최소급여를 받는 직원의 사번, 이름, 직급, 급여를 조회select job_code,min(salary)from employeegroup by job_code;select emp_id,emp_name,job_code,salaryfrom employeewhere(job_code,salary) in (select job_code,min(salary)from employeegroup by job_code);
상관서브쿼리
상호연관 서브쿼리
메인쿼리의 값을 서브쿼리에 전달하고, 서브쿼리를 수행한 다음, 그 결과를 다시 메인쿼리로 리턴
xxxxxxxxxx--직급별 평균급여보다 많은 급여를 받는 사원 출력select *from employee Ewhere salary > (select avg(salary)from employeewhere E.job_code = 'J2');--직급별 평균급여보다 많은 급여를 받는 사원 출력select emp_name, job_code, salaryfrom employee Ewhere salary > (select avg(salary)from employeewhere job_code = E.job_code)order by 2;
스칼라 서브쿼리
결과값이 1개(단일행, 단일컬럼)인 상관서브쿼리
select : 주로 select절 스칼라서브쿼리를 일컫음
where
order by
xxxxxxxxxx--1. select--모든 사원의 사번,이름,관리자 사번,관리자명 조회select emp_id,emp_name,manager_id,( select emp_namefrom employeewhere emp_id = E.manager_id)from employee E;--2. where절--사원이 속한 직급평균보다 많은 급여를 받은 사원 조회select emp_id,emp_name,salaryfrom employee Ewhere salary > (select avg(salary)from employeewhere job_code = E.job_code);--3. order by-- 모든 직원의 사번, 이름, 부서코드를 부서명으로 조회select emp_id, emp_name, dept_codefrom employee Eorder by (select dept_titlefrom departmentwhere E.dept_code = dept_id);select dept_titlefrom deaprtment;
인라인 뷰
실제테이블에 근거한 논리적 가상테이블(result set)
- inline view : 1회용
- stored view : db에 저장해서 영구적 사용 가능
view를 사용하면 복잡한 쿼리문을 가독성 좋게 만들 있음
xxxxxxxxxx--여사원의 사번, 사원명, 부서코드, 성별을 출력select emp_id,emp_no,emp_name,dept_codefrom(select emp_id,emp_no,emp_name,dept_code,decode(substr(emp_no,8,1),'1','남','3','남','여') 성별from employee)where 성별 = '여';
실습문제
xxxxxxxxxx--1. 1990년대 입사한 사원을 조회--사번,사원명,입사년도select 사번,사원명,입사년도from (select emp_id 사번,emp_name 사원명,extract(year from hire_date) 입사년도from employeeorder by 3)where 입사년도 >= 1900 and 입사년도 < 2000;--2. 사원명, 부서명, 급여, 부서별 평균임금을 스칼라서브쿼리를 이용해서 출력select emp_name 사원명,(select dept_titlefrom departmentwhere dept_id = E.dept_code) 부서명,salary 급여,(select trunc(avg(salary))from employeewhere dept_code = E.dept_code) 평균임금from employee E;--3 직급이 J1을 제외하고, 부서를 지정받은 사원중에서--자신의 부서별 평균급여보다 적은 급여를 받는 사원출력--부서코드, 사원명, 급여, 부서별 급여평균select dept_code 부서코드,emp_name 사원명,salary 급여,(select trunc(avg(salary))from employeewhere dept_code = E.dept_code) 평균임금from employee Ewhere salary > (select avg(salary)from employeewhere dept_code = E.dept_code)and dept_code <> 'J1'order by 부서코드;--4. employee테이블에서 사원중 30대, 40대인 여자사원의 사번, 부서명, 성별, 나이를 출력 (인라인 뷰 사용)select 사번,nvl(부서명,'인턴'),성별,나이,emp_namefrom (select E.emp_id 사번,(select dept_titlefrom departmentwhere dept_id = E.dept_code) as 부서명,decode(substr(emp_no,8,1),'1','남','3','남','여') 성별,extract(year from sysdate) - extract(year from to_date(substr(emp_no,1,2),'rr'))+1 나이,emp_namefrom employee E)where 나이 >= 30 and 나이 < 50and 성별 = '여';
DML
Data Manipulation Language
CRUD - 데이터를 이용하려는 사용자와 Database서버 사이의
인터페이스를 직접적으로 제공하는 언어
C : insert into
R : select ~ from
U : update set
D : delete from
insert
테이블에 새로운 행 을 추가
xxxxxxxxxxinsert into 테이블명 values(col값1, col값2,...);insert into 테이블명(col1명 , col2명,...) values(col1값, col2값,...);테스트용 테이블 emp_test1 생성 - employee의 테이블을 쫘악 가져옴, 복붙(서브쿼리 이용)create table emp_test1 asselect * from employee;-- 데이터 추가 (컬럼명 없이 추가)insert into emp_test1values(301,'장채현','920727-1231231','eunchan2000@gmail.com','01068685797','D0','J0','S0',10000000,0.5 , 200,sysdate,default,default );-- 데이터 추가 (컬럼명 지정 후 추가)insert into emp_test1(emp_id , emp_name , emp_no ,email , phone , dept_code ,job_code, sal_level , salary ,bonus , manager_id , hire_date ,quit_date , quit_yn)values(302,'함지민','940321-2123123','ikso2000@naver.com','01012341234','D0','J0','S0',43000000,0.2,'200',SYSDATE,default,default);-- 컬럼명은 생략 가능 , 단 nullabe , default값이 지정된 한해서 생략 가능-- 컬럼지정순서도 자유롭다, 하지만 반드시 values절과 반드시 일치헤야 함insert into emp_test1(emp_id , emp_name , emp_no ,email , phone , dept_code ,job_code, sal_level , salary ,bonus , manager_id)values(302,'잡채현','911212-1231111','ikarus@naver.com','01043214321','D0','J0','S0',4300000,0.1,'200');-- 서브쿼리를 이용한 insert intocreate table emp_test2(emp_id number,emp_name varchar2(30),dept_title varchar2(20));select emp_id,emp_name,dept_titlefrom employee left join departmenton dept_code = dept_id;-- 서브쿼리 대입insert into emp_test2(select emp_id,emp_name,dept_titlefrom employee left join departmenton dept_code = dept_id);-- 데이터 사전 보기select *from user_tab_colswhere table_name = 'EMP_TEST1';
update
테이블에 이미 기록된 행에 대해서 컬럼의 값을 수정
작업 후 행의 개수에는 변화가 없다
xxxxxxxxxx-- 다른 테이블 복사해보리기create table dept_copy asselect * from department;-- D9의 부서명을 총무부에서 전략기획팀으로 변경-- where 조건절을 반드시 명시할 것update dept_copy set dept_title = '전략기획팀'where dept_id = 'D9';-- 서브쿼리를 이용한 update-- 방명수사원의 급여와 보너스율을 유재식사원과 동일하게 변경create table emp_salary asselect emp_id,emp_name,dept_code,job_code,salary,bonusfrom employee;select emp_name,salary,bonusfrom emp_salarywhere emp_name in ('유재식','방명수');update emp_salary set salary = 3400000, bonus = 0.2where emp_name = '방명수';rollback;-- 서브쿼리update emp_salaryset salary = (select salary from emp_salary where emp_name = '유재식'),bonus = (select bonus from emp_salary where emp_name = '유재식')where emp_name = '방명수';-- 서브쿼리2update emp_salaryset (salary, bonus) = (select salary , bonus from emp_salary where emp_name = '유재식')where emp_name = '방명수';
delete
테이블의 행을 삭제
where절을 반드시 사용 , 안할 시 테이블 내용 다 삭제
xxxxxxxxxxselect * from emp_test1;-- emp_id 300번대 친구들 날려 보리기delete from emp_test1 where emp_name >= 300;-- 외래키 (다른 테이블에 참조 시, 기본 옵션으로는 삭제 불가)delete from department where dept_id = 'D2';--ORA-02292: integrity constraint (KH.FK_EMPLOYEE_DEPARTMENT) violated - child record found--truncate로 테이블 행삭제하기--DML이 아닌 DDL이다. 그러므로 자동커밋되므로 사용시 주의select * from dept_copy;delete from dept_copy; --DML은 작업시 before image를 생성 , 그래서 롤백이 가능rollback;-- 대량의 데이터를 삭제 할 땐, before image를 만들면 느리기 때문에, 때에 따라서 사용함truncate table dept_copy; -- DDL은 before image를 생성하지 않기 때문에 , 롤백 불가능-- DML / DDL 혼용시 주의할 점-- DML은 commit / rollback (TCL) 사용가능-- DDL은 autocommit되므로 TCL 사용불가create table tbl_test(id varchar2(20));insert into tbl_test values('Hello');insert into tbl_test values('world');-- 현재 데이터를 추가했고, commit하지 않았음create table tbl_other(no number);-- 의도대로 데이터 롤백되지 않는다-- DDL을 사용했으므로, 자동 커밋 됨rollback;delete from tbl_test where id = 'world';select * from tbl_test;rollback;-- 드랍을 날렸을 경우는 rollback 불가drop table tbl_other;
DDL
Data Definition Language 데이터 정의 언어
데이터베이스객체에 대해서 , 생성 create , 수정 alter , 삭제 drop하는 명령어 모음
rollback / commit 등의 tcl사용이 불가 - 자동 커밋(auto commit)
데이터베이스 객체
- table
- user
- sequence : numbering을 담당
- index : 컬럼에 인덱스를 넣어, 조회할 때 속도향상 B Tree + 가 기본
- view : 가상테이블
- package
- procedure
- function
- trigger
- synonym
create
xxxxxxxxxxcreate table member(member_id varchar2(20),member_pwd varchar2(20),member_name varchar2(20));-- 주석-- 테이블 / 컬럼 등에 주석을 다는 습관 !-- DD : dba_ , all_, user_ 객체에 대한 메타정보를 가지고 있는 테이블select *from user_tab_commentswhere table_name = 'EMPLOYEE';select *from user_col_commentswhere table_name = 'EMPLOYEE';-- 1. 테이블 주석 달기comment on table member is '회원관리테이블';select *from user_tab_commentswhere table_name = 'MEMBER';-- 2. 컬럼 주석 달기comment on column member.member_id is '회원아이디';comment on column member.member_pwd is '회원비번';comment on column member.member_name is '회원이름';select *from user_col_commentswhere table_name = 'MEMBER';-- 수정 / 삭제는 동일한 명령어comment on column member.member_id is '';comment on column member.member_pwd is '';comment on column member.member_name is '';-- ##제약조건-- 데이터무결성을 지키기 위해 컬럼단위로 제한된 조건-- 무결성(intergrity) : 데이터의 정합성, 일관성이 유지 되는 것./*1. NOT NULL : 데이터에 NULL을 허용하지 않는 것 => 필수항목(컬럼)2. UNIQUE : 중복된 값을 허용하지 않음 => 주민등록번호, 이메일3. PRIMARY KEY : 레코드의 고유식별자로 사용. unique + not null => 아이디4. FOREIGN KEY : 두 테이블간 데이터를 연결하여 설정 함외래키테이블에 올 수 있는 데이터를 제한함.5. CHECK : 저장가능한 데이터의 값의 조건, 범위를 설정 => 성별, 기혼여부(0,1), 삭제 여부제약조건 정보 확인user_constraintsuser_cons_columnsselect *from user_constraintswhere table_name = 'EMPLOYEE';select *from user_cons_columnswhere table_name = 'EMPLOYEE';제약조건 조회select *from user_constraints UC join user_cons_columns UUC using(constraint_name)where UC.table_name = 'EMPLOYEE';*/
1. NOT NULL
필수항목인 컬럼에 추가하는 제약조건
xxxxxxxxxx-- 제약조건이 없는 막장테이블create table user_nocons(user_no number,user_id varchar2(20),user_pwd varchar2(20),user_name varchar2(30),gender char(3),phone char(13),email varchar2(50));-- 데이터 추가insert into user_noconsvalues(1,'user01','p1234','홍길동','남','010-1234-1234','test@naver.com');insert into user_noconsvalues(2,null,null,null,null,'010-3214-3214','tttt@naver.com');select * from user_nocons;-- 제약조건이 있는 테이블create table user_notnull(user_no number not null,user_id varchar2(20) not null,user_pwd varchar2(20) not null,user_name varchar2(30) not null,gender char(3),phone char(13),email varchar2(50));-- 제약 조건이 있는 테이블에 , 제약조건 이름을 내가 붙여주기create table user_notnull(user_no number constraint nn_user_notnull_user_no not null, --컬럼 레벨에서 작성 했다user_id varchar2(20) constraint nn_user_notnull_user_id not null,user_pwd varchar2(20) constraint nn_user_notnull_user_pwd not null,user_name varchar2(30) constraint nn_user_notnull_user_name not null,gender char(3) default '여',phone char(13),email varchar2(50)); -- 이쪽에서 제약조건을 줄수도 있음 ㅇㅈ? ㅇ ㅇㅈ-- 데이터 추가insert into user_notnullvalues(1,'user01','p1234','홍길동','남','010-1234-1234','test@naver.com');-- 데이터 추가insert into user_notnullvalues(1,'user01','p1234','홍길순',default,'010-1234-1234','test@naver.com');-- ORA-01400: cannot insert NULL into ("KH"."USER_NOTNULL"."USER_ID")insert into user_notnullvalues(2,null,null,null,null,'010-3214-3214','tttt@naver.com');select * from user_notnull;
2. Unique
컬럼 입력값에 대해서 중복을 제한하는 제약조건
xxxxxxxxxxcreate table user_unique(user_no number not null,user_id varchar2(20) constraint uq_user_unique_id unique, -- 컬럼레벨에서 제약조건 작성user_pwd varchar2(20) not null,user_name varchar2(30) not null,gender char(3) default '남',phone char(13),email varchar2(50));-- 같은 결과create table user_unique(user_no number not null,user_id varchar2(20),user_pwd varchar2(20) not null,user_name varchar2(30) not null,gender char(3) default '남',phone char(13),email varchar2(50),constraint uq_user_unique_user_id unique(user_id));-- 데이터 삽입insert into user_uniquevalues(1,'user01','p1234','홍길동','남','010-1234-1234','test@naver.com');-- 같은 데이터를 삽입시-- ORA-00001: unique constraint (KH.UQ_USER_UNIQUE_USER_ID) violatedinsert into user_uniquevalues(1,'user01','p1234','홍길동','남','010-1234-1234','test@naver.com');--unique제약조건이 걸린 컬럼에 null데이터 추가-- mssql은 null데이터 입력 불가-- mysql 과 oracle은 null데이터 입력 가능이라 ,not null이라는 옵션을 줘야함create table user_unique(user_no number not null,user_id varchar2(20) not null,user_pwd varchar2(20) not null,user_name varchar2(30) not null,gender char(3) default '남',phone char(13),email varchar2(50),constraint uq_user_unique_user_id unique(user_id));
3. primary key
not null + unique 기능을 하지만, 테이블당 한개만 선언가능하다
테이블에서 한행의 정보를 구분하기 위한 고유식별자(identifier)역할을 하기 위해
xxxxxxxxxxcreate table user_primarykey(user_no number not null,user_id varchar2(20) constraint pk_user_primarykey_user_id primary key,user_pwd varchar2(20) not null,user_name varchar2(30) not null,gender char(3) default '남',phone char(13),email varchar2(50),constraint uq_user_unique_user_id unique(user_id));-- orcreate table user_primarykey(user_no number not null,user_id varchar2(20),user_pwd varchar2(20) not null,user_name varchar2(30) not null,gender char(3) default '남',phone char(13),email varchar2(50),constraint uq_user_unique_user_id unique(user_id),constraint pk_user_primarykey_user_id primary key(user_id));-- primary key는 단일컬럼에 대해서 부여하는 simgle primary key-- 여러 컬럼을 묶어서 부여하는 composite primary key-- 주문 테이블 : 상품번호, 주문자아이디, 주문일자, 주문수량create table tbl_order_composite_pl(product_no varchar2(20),user_id varchar2(20),order_date date,order_num number, -- 주문수량constraint pk_tbl_order primary key(product_no , user_id, order_date));-- 데이터 추가insert into tbl_order_composite_pkvalues ('p123','u123',sysdate,10);select * from tbl_order_composite_pk;
4. foreign key
결성을 유지하기 위한 제약조건
참조하는 컬럼(자식)과 참조되는 컬럼(부모)간의 부모자식테이블관계를 형성
xxxxxxxxxx--shop_member--shop_buy : shop_member.user_id 컬럼값을 참조create table shop_member(user_no number,user_id varchar2(20),user_pwd varchar2(30) not null,user_name varchar2(30),gender varchar2(10) default '남',phone varchar2(30),email varchar2(50),constraint user_no_uq unique(user_no),constraint user_id_pk primary key(user_id));-- 데이터 삽입insert into shop_membervalues(1,'user01','p1234','홍길동','남','010-1234-1234','test1@naver.com');insert into shop_membervalues(2,'user02','p1234','이순신','남','010-1234-1234','test2@naver.com');insert into shop_membervalues(3,'user03','p1234','장보고','남','010-1234-1234','test3@naver.com');insert into shop_membervalues(4,'user04','p1234','정지수','남','010-1234-1234','test4@naver.com');insert into shop_membervalues(5,'user05','p1234','박광준','남','010-1234-1234','test5@naver.com');-- 회원이 물품을 구매했을 때 기록하는 테이블-- shop_member.user_id 를 참조하는 외래키를 설정-- 1. 컬럼 레벨 작성create table shop_buy(buy_no number constraint pk_shop_buy_no primary key,user_id varchar2(20) constraint fk_shop_buy_user_idreferences shop_member(user_id),product_name varchar2(20),reg_date date);-- 2. 테이블 레벨 작성create table shop_buy(buy_no number constraint pk_shop_buy_no primary key,user_id varchar2(20),product_name varchar2(20),reg_date date,constraint fk_shop_buy_user_id foreign key(user_id)references shop_member(user_id));insert into shop_buy values(1,'user01','축구화',sysdate);insert into shop_buy values(2,'user01','축구화',sysdate);insert into shop_buy values(3,'user01','축구화',sysdate);-- ORA-02291: integrity constraint (KH.FK_SHOP_BUY_USER_ID) violated - parent key not foundinsert into shop_buy values(4,'kkkk','축구화',sysdate);insert into shop_buy values(5,null,'축구화',sysdate); -- fk컬럼에 null값은 추가가능select * from shop_member;select * from shop_buy;delete from shop_memberwhere user_id = 'user03';-- 참조되고 있는 데이터는 삭제 불가능-- ORA-02292: integrity constraint (KH.FK_SHOP_BUY_USER_ID) violated - child record found-- 자식테이블데이터 -> 부모테이블데이터 순으로 삭제drop table shop_member;-- ORA-02292: integrity constraint (KH.FK_SHOP_BUY_USER_ID) violated - child record found-- 자식 테이블 삭제 -> 부모테이블 삭제 순으로 삭제-- 외래키 삭제 옵션-- on delete restricted : 기본값-- on delete set null : 부모데이터가 삭제된 자식데이터를 null로 변경-- on delete cascade : 부모데이터가 삭제되면 자식데이터도 삭제create table shop_buy_set_null(buy_no number constraint pk_shop_buy_set_null_no primary key,user_id varchar2(20),product_name varchar2(20),reg_date date,constraint fk_shop_buy_set_null_user_id foreign key(user_id)references shop_member(user_id)on delete set null);insert into shop_buy_set_null values(1,'user01','축구화',sysdate);insert into shop_buy_set_null values(2,'user02','농구화',sysdate);-- 부모데이터 삭제시도delete from shop_memberwhere user_id = 'user02';select * from shop_buy_set_null;
5. check
컬럼 도메인의 값을 제한
xxxxxxxxxx-- 컬럼레벨create table user_check(user_no number not null,user_id varchar2(20),user_pwd varchar2(20) not null,user_name varchar2(30) not null,gender char(3) default '남' constraint ck_user_check_gendercheck(gender in('남','여')),phone char(13),email varchar2(50),constraint pk_user_check_user_id primary key(user_id));-- 테이블 레벨create table user_check(user_no number not null,user_id varchar2(20),user_pwd varchar2(20) not null,user_name varchar2(30) not null,gender char(3) default '남' not null, -- default절 이후에 not null추가phone char(13),email varchar2(50),constraint pk_user_check_user_id primary key(user_id),constraint ck_user_check_gender check(gender in ('남','여')));
--subquery를 이용한 create table --employee테이블의 특정컬럼 emp_id,emp_name,salary,dept_title,job_name --의 결과셋을 특정테이블로 생성하고 싶다면..
create table emp_copy as
select emp_id,emp_name,salary,dept_title,job_name
from employee
left join department on dept_code = dept_id
left join job using (job_code);
select * from emp_copy;
--제약조건 조회 --create table [table명] as로 생성된 테이블에는 not null제약조건만 복사. --생성된 테이블에 제약조건 추가는 alter문을 통해서 사용해야함. select constraint_name,UC.owner,UC.table_name,UC.constraint_type, UC.search_condition from user_constraints UC join user_cons_columns UCC using (constraint_name) where UC.table_name = 'EMP_COPY';
select constraint_name,UC.owner,UC.table_name,UC.constraint_type, UC.search_condition from user_constraints UC join user_cons_columns UCC using (constraint_name) where UC.table_name = 'EMPLOYEE';
Alter
xxxxxxxxxx--테이블등 데이터베이스객체의 정보를 수정할 때 사용.--1. 컬럼/제약조건 : add,modify,rename,drop--2. 테이블 : rename--연습용테이블생성create table tbl_alter(user_no number primary key,user_id varchar2(20),user_pwd char(20));desc tbl_alter;--####1. 컬럼/제약조건--1. add 컬럼alter table tbl_alter add user_name varchar2(20);desc tbl_alter;--컬럼 default값 추가alter table tbl_alter add user_age number default 0;desc tbl_alter;--제약조건 추가alter table tbl_alter add user_gender char(3) default '남'check(user_gender in ('남','여'));--2. add 제약조건alter table tbl_alter add constraint uq_tbl_alter_user_id unique(user_id);--not null 제약조건은 추가가 아니라 변경으로 해줘야함--최초의 상태가 nullable 이기 때문에 => not null로 바꿔줘야함alter table tbl_alter modify user_pwd not null;desc tbl_alter;--3. modify 컬럼--데이터 타입, default값 변경alter table tbl_alter modify user_name char(10);alter table tbl_alter modify user_age default 20;--4. modify 제약조건--이런건 없음, 제약좋건 삭제후 다시 생성할 것, (제약조건의 이름을 변경하는 것은 가능)--5. rename 컬럼alter table tbl_alter rename column user_pwd to password;--6. rename 제약조건명alter table tbl_alter rename constraint SYS_C0010263 to pk_tbl_alter;--7. drop 컬럼alter table tbl_alter drop column user_age;--8. drop 제약조건alter table tbl_alter drop constraint SYS_C0010266;-- add 컬럼/제약조건 둘다 가능-- modify 컬럼만 가능-- rename 컬럼/제약조건 둘다 가능-- drop 컬럼/제약조건 둘다 가능--####2. 테이블--테이블 명만 변경가능alter table tbl_alter rename to tbl_alter_alter;select * from tab; --user_tables (bin 안에 있는놈들은 지워진 애들)-- 간단 버전rename tbl_alter_alter to tbl_alter
drop
데이터베이스 객체 삭제시 사용
xxxxxxxxxx--제약조건 조회select constraint_name,UC.owner,UC.table_name,UC.constraint_type, UC.search_conditionfrom user_constraints UC join user_cons_columns UCC using (constraint_name)where UC.table_name = 'TBL_ALTER_CHILD';--자식테이블에서 외래키로써, 부모테이블의 기본키를 참조하고 있을 때,--부모테이블을 삭제 할 수 있을 까?create table tbl_alter_child(parent_no number references tbl_alter(user_no));--부모 테이블 삭제drop table tbl_alter;----ORA-02449: unique/primary keys in table referenced by foreign keys--부모테이블 강제삭제 옵션 : cascade constraintdrop table tbl_alter cascade constraint;
DCL
Data Control Language
grant(권한부여), revoke(권한회수), commit(실행), rollback(복구)
xxxxxxxxxx-- 1. grant-- grant [System_Privilege | role] to [user | role | PUBLIC] [WITH ADMIN OPTION]-- System_Privilege : 권한 create session(접속할수 있는 권한), create table,...-- role : 권한 묶음 connect, resource, dba-- PUBLIC : 관리자가 모든 사용자가 사용할 수 있도록 부용함-- WITH ADMIN OPTION : 권한을 부여받은 사용자도 이 권한을 다른 사용자에게 부여 가능-- qwerty 사용자를 생성해서 관리-- 관리자계정에서create user qwerty identified by qwertydefault tablespace users;select * from dba_users;-- 접속권한 주기grant create session to qwerty;grant connect to qwerty;-- 테이블 만드는 권한 주기 => create table-- resource라는 role부여 : create table 등등, 다른 권한들 묶음grant resource to qwerty;-- 테이블스페이스 users에 대한 권한을 주어야함alter user qwerty default tablespace users quota unlimited on users;--(관리자계정) 사용자에게 부여된 권한/role 조회select * from dba_sys_privswhere grantee = 'QWERTY';select * from dba_role_privswhere grantee = 'QWERTY';-- 일반사용자가 본인의 권한/role조회select * from user_sys_privs;select * from user_role_privs;-- role에 포함된 권한 보기 (관리자에서 가능)select *from dba_sys_privswhere grantee in ('CONNECT','RESOURCE');-- 특정테이블에 대한 권한 부여-- kh계정의 테이블에 대한 권한을 kh가 qwerty에게 부여create table tbl_coffee(pname varchar2(20) primary key,price number not null,company varchar2(20) not null);insert into tbl_coffee values('맥심커피', 30000 , '동서식품');insert into tbl_coffee values('카누커피', 20000 , '동남식품');insert into tbl_coffee values('캔커피', 10000 , '동북식품');commit;select * from tbl_coffee;--tbl_coffee을 qwerty가 볼 수 있도록 권한 주기grant select on kh.tbl_coffee to qwerty;--qwerty에서 이방식으로 보면됨 (select만 가능)select * from kh.tbl_coffee;--qwerty에게 insert , update, delete , select 권한 주는 2가지 방식grant insert, update, delete on kh.tbl_coffee to qwerty;grant all on kh.tbl_coffee to qwerty; --select , insert, update, delete--qwerty에서 insert 하는 방법insert into kh.tbl_coffee values('맥심잡지',40000,'어후');select * from tbl_coffee;
2. revoke
권한 회수해보리기
xxxxxxxxxxrevoke insert on kh.tbl_coffee from qwerty;revoke all on kh.tbl_coffee from qwerty;
Database Object
DD - Data Dictionary
자원(데이터베이스 객체)을 효율적으로 관리하기 위해서 다양한 메타정보를 담고 있는 테이블
다양한 메타정보를 담고 있는 테이블
사용자가 DB에 직접적으로 작업하지 않음
사용자가 테이블 등에 작업을 하게되면, 자동으로 DD에 반영됨.
사용자는 조회만 가능하다
xxxxxxxxxxselect * from dictionary;select * from dict; -- dictionary의 동의어(synonym)으로 지정select * from tab; -- user_tables 동의어select * from user_tables;
- user_xxx : 자신의 계정이 소유한 객체 조회
- all_xxx : 자신에게 부여받은 객체 조회
- dba_xxx : 관리자가 조회하는 모든 객 조회
VIEW
stored view
inline view(일회용)과 다르게 저장된 가상테이블
뷰를 사용해서 특정사용자가 원본테이블에 제한적으로 접근하게 함.
view를 생성하기 위해서는 권한을 부여받아야함 , resource에는 포함 안됨
xxxxxxxxxxselect * from user_views;grant create view to kh;create view view_emp asselect emp_id, emp_no, emp_name , email , phone , job_code , sal_levelfrom emp_test1;-- 다른유저에게 테이블에서 제한된 정보를 제공grant select on view_emp to qwerty;update emp_test1 set emp_name = '암지민' where emp_name = '함지민';-- 가상 테이블이라 update한 내용이 반영 되지 않음select * from emp_test1;-- DD에서 조회 (서브쿼리가 저장되있는 걸 확인 할 수 있음)select * from user_views;--select * from view_emp 를 한다는 것은 view_emp가 가지고 있는 서브쿼리를 실행한다고 생각하면 된다.--select emp_id, emp_no, emp_name , email , phone , job_code , sal_level-- from emp_test1;-- 뷰삭제drop view view_emp;-- 뷰특징-- 1. 실제 존재하는 컬럼뿐아니라 가상컬럼(산술연산) 생성가능 - or replace는 만약 view가 존재하면 갱신create or replace view view_emp_salary -- 객체가 존재하면 갱신asselect emp_name 사원명,(salary+(salary * nvl(bonus,0))) * 12 연봉from emp_test1;select * from view_emp_salary;-- join 뷰-- 사번, 사원명, 부서명, 직급명을 포함하는 view_emp_read 뷰객체를 작성create or replace view view_emp_readasselect emp_id 사번,emp_name 사원명,nvl(job_name,'인턴') 직급,nvl(dept_title,'인턴') 부서from employee E left join department Don E.dept_code = D.dept_idleft join job J using (job_code);-- view의 DML-- 생성된 뷰에 대해서 제한적으로 DML이 가능insert into view_empvalues(401,'790405-1012333','강호동','kangod@kh.or.kr','01012341234','J4','S3');update view_empset emp_no = '810405-1012333'where emp_id = 401;delete from view_emp where emp_id = 401;-- view에 DML이 불가능한 경우-- 1. 뷰에 가상컬럼이 포함된 경우, 가상컬럼을 조작할 수 없다.-- 2. insert시 뷰에 포함되지 않은 컬럼중에 not null인 경우 , 추가 할 수 없다.-- 3. 산술표현식으로 처리된 경우 , 연봉 -> salary , bonus (X)-- 4. distince, group by가 포함된 경우-- 5. join view에서 fk로 연결되지 않은 경우-- view 옵션-- 1. with check option-- where절 조건에 사용된 컬럼은 수정하지 못한다create or replace view view_emp_d5 asselect emp_id, emp_name,salary, dept_codefrom emp_test1where dept_code='D5' -- 여기에 사용된 컬럼은 변경 불가with check option;-- 급여가 250만원 이상인 사원을 D2부서로 변경update view_emp_d5set dept_code ='D2' -- 안된다잉where salary >= 2500000;--2. with read only-- 이 뷰는 읽기만 가능 , 수정 불가create or replace view view_emp_d5 asselect emp_id, emp_name,salary, dept_codefrom emp_test1where dept_code='D5'with read only;--수정불가update view_emp_d5set salary = salary + 5000000; -- 안된다잉
SEQUENCE
순차적으로 정수값을 자동으로 생성하는 객체, 채번기
xxxxxxxxxx/*create sequence [시퀀스명][start with 숫자][increment by 숫자][maxvalue 숫자 | nomaxvalue] -- 최대값 , 기본값은 nomaxvalue[minvalue 숫자 | nominvalue] -- 최소값 , 기본값은 nominvalue[cycle | nocycle] -- 순환여부 , 기본값은 nocycle[cache | nocache] -- 캐쉬여부 , 기본값은 nocache*/create table tbl_sequence(no number primary key,name varchar2(20) not null,age number not null);-- 시퀀스 객체 생성create sequence seq_tbl_sequence_nostart with 1increment by 1nominvaluenomaxvaluenocyclenocache;-- 데이터 삽입-- no : 기본키 : 중복x , 열을 유일하게 식별하는 고유 키insert into tbl_sequence values (seq_tbl_sequence_no.nextval,'홍길동',20);insert into tbl_sequence values (seq_tbl_sequence_no.nextval,'띠그랭',30);insert into tbl_sequence values (seq_tbl_sequence_no.nextval,'필리멍',40);-- dd : user_sequencesselect * from user_sequences;--last_number컬럼이 다음 nextval요청이 부여받는 번호.--curr_val : 현재 번호select seq_tbl_sequence_no.currval from dual;--nextval 호출시 시퀀스 객체 1씩 증가select seq_tbl_sequence_no.nextval from dual;insert into tbl_sequence values(seq_tbl_sequence_no.nextval, '하춘화', 50);select * from tbl_sequence;--실습문제--고객이 상품주문시 사용할 테이블 tbl_order를 생성create table tbl_order(order_no number primary key,user_id varchar2(20),product_id varchar2(20),product_cnt number,order_date date default sysdate);--order_no 컬럼은 seq_order_no 시퀀스객체에서 채번할 것,create sequence seq_order_nostart with 1increment by 1nominvaluenomaxvaluenocyclenocache;--주문 내역--kang님이 swk 상품을 5개 주문--gam님이 gjk 상품을 20개 주문--ring님이 oring 상품을 30개 주문insert into tbl_order values (seq_order_no.nextval,'gang','swk',5,sysdate);insert into tbl_order values (seq_order_no.nextval,'gam','gjk',20,sysdate);insert into tbl_order values (seq_order_no.nextval,'ring','oring',30,sysdate);--order_no : kh-20181024-1000 고유번호를 가질 수 있게 작성--고객이 상품주문시 사용할 테이블 tbl_order2를 생성create table tbl_order2(order_no varchar2(20) primary key,user_id varchar2(20),product_id varchar2(20),product_cnt number,order_date date default sysdate);create sequence seq_order2_nostart with 1increment by 1nominvaluenomaxvaluenocyclenocache;insert into tbl_order2 (order_no, user_id, product_id , product_cnt)values('kh-' || to_char(sysdate,'yyyymmdd') || '-' || seq_order2_no.nextval,'kang','swk',5);select * from tbl_order2;
INDEX
색인 -> 처리속도 향상을 위해서 컬럼에 대하여 생성하는 객체
key = value
key = 해당컬럼값
value = 행이 저장된 주소값
단점 : 덱스에 대한 추가저장공간이 필요, 인덱스생성시간이 소요
데이터변경(insert / update / delete)가 자주 일어나는 컬럼에는 오버헤드가 오히려 더크다
어떤 컬럼에 인덱스를 만들어야 될까?
선택도가 좋은 컬럼을 가지고 인덱스를 만들어야 한다.
고유한 값을 많이 가지는 컬럼이 선택도가 좋다고 할 수 있다
선택도가 좋은 컬럼 : 주민번호 > 성명
선택도가 나쁜 컬럼 : 성별
효율적으로 인덱스를 사용하려면
- where절에 자주 사용되는 컬럼을 인덱스를 만들것
- 전체데이터에서 10~15%에 해당하는 컬럼
- 조인시에 조건절로 사용되는 컬럼
- 데이터변경이 적은 컬럼
- 테이블의 데이터가 아주 많은 컬럼(최소 20만건)
xxxxxxxxxx-- 인덱스 조회-- pk와 uq제약조건이 걸린 컬럼은 자동으로 인덱스 생성select * from user_indexeswhere table_name = 'EMPLOYEE';select * from employeewhere emp_id = 200; --pk컬럼을 통한 검색select * from employeewhere emp_name = '송중기';-- 인덱스create index idx_emp_name on employee(emp_name);
복합 인덱스
2개 이상의 컬럼으로 이루어진 인덱스
선행이 무엇이냐가 아주 중요하다
xxxxxxxxxxcreate index idex_emp_name_dept_code on employee(emp_name.dept_code);select * from employeewhere emp_name='박나라' and dept_code = 'D5';
PL/SQL
Oracle's Procedural Language Extension to SQL
오라클이 제공하는 절차적 언어 확장
기존 sql에 변수, 조건문, 반복처리등을 지원
종류
- 익명블럭 : 1회용
- 프로시져 : 특정작업처리를 위한 서브프로그램, 단독으로 실행하거나, 다른 프로시저를 호출해서 실행
- 함수 : 거의 프로시저와 유사하나 반드시 리턴값이 있다
구조
DECLEAR(선택) : 선언부
BEGIN(필수) : 실행부
EXCEPTION(선택) : 예외처리부
END;(필수)
/ -> pl/sql 선언 끝 및 실행
xxxxxxxxxxdeclarevid number;beginselect emp_idinto vid -- 변수에 대입from employeewhere emp_name = '&emp_name'; -- 사용자로부터 입력값 받기dbms_output.put_line('emp_id=' || vid);exceptionwhen no_data_found then dbms_output.put_line('데이터가 없음');end;/-- 1. 변수선언declarev_empno number(4);v_empname varchar2(10);test_num number := 50;beginv_empno := 1010;v_empname := '강호동';dbms_output.put_line('사번 이름');dbms_output.put_line('----------');dbms_output.put_line(v_empno || ' ' || v_empname);dbms_output.put_line('----------');dbms_output.put_line('test_num =' || test_num);end;/-- 참조변수 %typedeclarevid employee.emp_id%type;&nbsnbsp; vname employee.emp_name%type;begin--주민번호를 입력받아 emp_id , emp_name 조회select emp_id,emp_nameinto vid, vnamefrom employeewhere emp_no = '&emp_no';dbms_output.put_line('사번 : ' || vid);dbms_output.put_line('사원명 : ' || vname);end;/select * from employee;-- 참조변수 %rowtypedeclarevemp employee%rowtype;beginselect *into vempfrom employeewhere emp_name = '&emp_name';dbms_output.put_line('사번 : ' || vemp.emp_id);dbms_output.put_line('이름 : ' || vemp.emp_name);dbms_output.put_line('주민 : ' || vemp.emp_no);dbms_output.put_line('부서코드 : ' || vemp.dept_code);end;/-- 실습문제 : 사원명을 입력받아서 사번, 사원명 , 직급명을 출력declarevemp_id employee.emp_id%type;vemp_name employee.emp_name%type;vjob_name job.job_name%type;vdept_title department.dept_title%type;beginselect emp_id, emp_name, job_name, dept_titleinto vemp_id, vemp_name, vjob_name, vdept_titlefrom employee Eleft join department D on E.dept_code = D.dept_idleft join job J using(job_code)where emp_name = '&사원명';dbms_output.put_line('사번 : ' || vemp_id);dbms_output.put_line('사원명 : ' || vemp_name);dbms_output.put_line('직급 : ' || vjob_name);dbms_output.put_line('부서 : ' || vdept_title);end;/--pl/sql 에서 dml문도 실행가능select * from member;begininsert into membervalues('abc','123@','김동현');--적용처리까지 함께 할 것commit;end;/--### pl/sql 조건문--1 if ~ then ~ end if문beginif '&이름' = '김동현' thendbms_output.put_line('김동현님, 환영합니다');elsedmbs_output.put_line('엥');end if;end;/--2. if then ~ elsif then end ifdeclarevno number;beginvno := &number;if vno = 1 thendbms_output.put_line('1을 입력하셨습니다');elsif vno = 2 thendbms_output.put_line('2를 입력하셨습니다');elsedbms_output.put_line('딴거 입력');end if;end;/--3. case when thendeclarevinput number;beginvinput := &input;case vinputwhen 1 thendbms_output.put_line(11111);when 2 thendbms_output.put_line(22222);elsedbms_output.put_line('딴걸 입력');end case;end;/-- loopdeclareno number := 1;beginloopdbms_output.put_line(no);no := no + 1;--탈출조건문if no > 5 thenexit;end if;end loop;end;/declareno number := 1;beginloopdbms_output.put_line(no);no := no + 1;--탈출조건문exit when no>5;end loop;end;/--2. while loop-- 조건식이 true인 동안만 반복문 실행-- 별도의 exit절 필요업ㅈㅅ음declarenum number := 1;beginwhile num <= 5 loopdbms_output.put_line(num);num := num + 1;end loop;end;/--실습문제--사용자로부터 2~9까지의 숫자를 입력받아 구구단을 출력declaren number;i number := 0;beginn := &number;if n between 2 and 9 thendbms_output.put_line(n || '단');while i <= 9 loop-- n이 짝수일 경우 건너 뜀i := i + 1;continue when mod(n , 2) = 0;dbms_output.put_line(n || ' X ' || i || ' = ' || n * i);end loop;end if;end;/--3. for in loop-- 별도의 증감식은 필요없음beginfor n in 1..5 loopdbms_output.put_line(n);end loop;end;/beginfor n in reverse 1..5 loopdbms_output.put_line(n);end loop;end;/
문제
xxxxxxxxxxselect max(emp_id)from emp_test1;declarevemp_id emp_test1.emp_id%type;vemp_name emp_test1.emp_name%type;vemp_no emp_test1.emp_no%type;vemp_phone emp_test1.phone%type;vjob_code emp_test1.job_code%type;vsal_level emp_test1.sal_level%type;beginselect max(emp_id)+1into vemp_idfrom emp_test1;--dbms_output.put_line('이름을 입력하세요.');vemp_name := '&name';--dbms_output.put_line('주민번호를 입력하세요.');vemp_no := '&no';--dbms_output.put_line('전화번호를 입력하세요.');vemp_phone := '&phone';--dbms_output.put_line('직급코드를 입력하세요.');vjob_code := '&job';--dbms_output.put_line('급여등급을 입력하세요.');vsal_level := '&sal';insert into emp_test1(emp_id,emp_name, emp_no,job_code,sal_level) values(vemp_id,vemp_name,vemp_phone,vjob_code,vsal_level);end;/select * from emp_test1;-------------select * from employee;declarevemp employee%rowtype;vgrade char(1);begindbms.output.put_line('사번을 입력하세요');select *into vempfrom employeewhere emp_name = '&emp_name';if vemp.salary >= 5000000 thenvgrade := 'A';elsif vemp.salary >= 4000000 thenvgrade := 'B';elsif vemp.salary >= 3000000 thenvgrade := 'C';elsif vemp.salary >= 2000000 thenvgrade := 'D';elsif vemp.salary >= 1000000 thenvgrade := 'E';elsevgrade := 'F';end if;dbms_output.put_line(vgrade);end;/
--양모자 씌우기 --리턴절에 세미클론 금지, 파라미터와 리턴절의 데이터 타입의 크기 지정 금지 ->컴파일 에러 create or replace function myfunc( P_str varchar2 ) return varchar2 is result varchar2(1000); --리턴될 변수 begin dbms_output.put_line('p_str = ' || P_str ); result := 'd' || P_str || 'b'; return result; end; / --확인 --user_functions(X) --user_procedures에서 object_type = 'FUNCTION'로 검색할 것 select * from user_procedures where object_type='FUNCTION';
--실행 --1. exec명령문 실행 var result varchar2; exec :result :=myfunc('김민우'); print result;
--2. 쿼리문에서 실행 select myfunc('&이름') 양모자 from dual;
--3. 익명블럭 begin dbms_output.put_line(myfunc('&이름')); end; /
--사번을 입력 받아서 해당 사원의 연봉을 계산해서 리턴하는 저장 함수 작성
create or replace function fn_bonus_calc(
p_emp_id employee.emp_id%type
)
return number
is
result number;
v_sal employee.salary%type;
v_bonus employee.bonus%type;
begin
select salary, nvl(bonus,0)
into v_sal, v_bonus
from employee
where emp_id = p_emp_id;
result := trunc((v_sal+(v_salv_bonus))12);
return result;
end;
/
select * from user_procedures where object_type = 'FUNCTION';
--실행 var annualPay number; exec :annualPay := fn_bonus_calc('&emp_id'); print annualPay;
select emp_id, emp_name, fn_bonus_calc(emp_id) 연봉 from employee;
--@실습문제 : 주민번호를 입력 받아서 성별을 리턴하는 저장함수 fn_get_gender를 생성하고
--사번, 사원명, 성별을 조회하는데 사용할 것
create or replace function fn_get_gender(
p_emp_no employee.emp_no%type
)
return varchar2
is
result varchar2(10);
gender varchar2(10);
begin
select case when substr(emp_no,8,1) in('1','3') then '남'
else '여' end 성별
into gender
from employee
where emp_no = p_emp_no;
result := gender;
return result;
end;
/
drop function fn_get_gender;
var empGender varchar2; exec :empGender := fn_get_gender('&emp_no'); print empGender;
select emp_id 사번, emp_name 사원명, fn_get_gender(emp_no) 성별 from employee where emp_no = '&emp_no';
select * from employee;
--@실습문제 : 사원번호를 받아서 직급명을 리턴하는 저장 함수 fn_get_job_name
create or replace function fn_get_job_name(
v_emp_id employee.emp_id%type
)
return job.job_name%type
is
result varchar2(10);
jn varchar2(10);
begin
select J.job_name
into jn
from employee E join job J using(job_code)
where emp_id = v_emp_id;
result := jn;
return result;
end;
/
var emp_job_name varchar2; exec :emp_job_name := fn_get_job_name('&emp_id'); print :emp_job_name;
select emp_id, emp_name, fn_get_job_name(emp_id) from employee where emp_id = '&emp_id';
--## PROCEDURE
--Stored Procedure --함수와 달리 반환값이 없을 수도 있다. /* create [or replace] procedure 프로시저명( 매개변수명1 [IN | OUT] 자료형, 매개변수명2 [IN | OUT] 자료형, ... ) is 변수선언부; begin 실행부; end; / */ --1. 매개변수 없는 프로시저 : 정기적 실행하는 프로그램시 주로 사용 select * from emp_copy; --emp_copy 테이블의 모든 데이터를 삭제하는 프로시저 create or replace procedure proc_del_all_emp is begin delete from emp_copy; commit; end; / select * from user_procedures;
--실행 exec proc_del_all_emp; select * from emp_copy;
--2. 매개변수 있는 프로시저 insert into emp_copy (select emp_id, emp_name, salary, dept_title, job_name from employee E join department D on E.dept_code = D.dept_id join job J using(job_code)); commit; select * from emp_copy;
--emp_id를 입력 받아서, emp_copy테이블에서 제거하는 프로시저 --IN : 프로시저 입장에서 받는 것(default) create procedure proc_del_emp( p_emp_id IN emp_copy.emp_id%type ) is
begin delete from emp_copy where emp_id = p_emp_id; commit; dbms_output.put_line(p_emp_id || '번 사원을 삭제했습니다!'); end; / exec proc_del_emp('&emp_id');--201삭제 select * from emp_copy where emp_id = '201';
--매개변수 OUT
--사번을 입력 받아서 사원명, 급여, 보너스를 OUT하는 프로시저
create or replace procedure proc_select_emp(
p_emp_id IN employee.emp_id%type,
p_emp_name OUT employee.emp_name%type,
p_salary OUT employee.salary%type,
p_bonus OUT employee.bonus%type
)
is
begin
select emp_name, salary, nvl(bonus,0)
into p_emp_name, p_salary, p_bonus
from employee
where emp_id = p_emp_id;
end;
/
--실행
--바인드 변수 선언
var b_emp_name varchar2(30);
var b_salary number;
var b_bonus number;
--프로시저 호출
exec proc_select_emp('&emp_id', :b_emp_name, :b_salary, :b_bonus);
--IN과 OUT을 한번에 입력
print :b_emp_name;
print :b_salary;
print :b_bonus;
--exec 명령 후 바인드 변수 자동출력 해주는 옵션
set autoprint on;
--프로시저 활용 예제 : UPSERT --insert + update : 기존에 데이터가 존재하면 update 존재하지 않으면 insert --직급코드 관리 create table job_copy as select * from job;
select* from job_copy; --제약조건 조회 select constraint_name, UC.owner, UC.table_name, UCC.column_name, UC.constraint_type, UC.search_condition from user_constraints UC join user_cons_columns UCC using(constraint_name) where UC.table_name = 'JOB_COPY';
--제약조건 추가
alter table job_copy add constraint pk_job_code primary key(job_code) modify job_name not null;
--프로시저 생성
create or replace procedure proc_upsert_job_copy(
p_job_code job_copy.job_code%type,
p_job_name job_copy.job_name%type
)
is
v_cnt number;
begin
--0. null여부 검사
if p_job_name is null then
dbms_output.put_line('ERROR: null값 대입 불가');
return;
end if;
--1. 데이터가 존재하는지 확인
select count(*)
into v_cnt
from job_copy
where job_code = p_job_code;
dbms_output.put_line('v_cnt = ' || v_cnt);
--2. insert or update
if v_cnt = 0 then insert into job_copy values(p_job_code, p_job_name);
else update job_copy set job_name = p_job_name where job_code = p_job_code;
end if;
commit;
-- insert into job_copy values(p_job_code, p_job_name); -- commit; end; / --실행 exec proc_upsert_job_copy('J8', '인턴'); select * from job_copy;
exec proc_upsert_job_copy('J8', '수습'); --ORA-00001: unique constraint (KH.PK_JOB_CODE) violated exec proc_upsert_job_copy('J9', ''); --에러 메세지 보여주기
--2018-10-25 @실습문제
/*@실습문제
- 주민번호를 입력받아 나이를 리턴하는 저장함수 fn_get_age를 사용해서
사번, 이름, 성별, 연봉, 나이를 조회/
create or replace function fn_get_age(
p_emp_no employee.emp_no%type
)
return number
is
empAge number; begin select extract(year from sysdate) - (case when substr(emp_no,8,1) in('1','2') then to_number(substr(emp_no,1,2)) +1900 else to_number(substr(emp_no,1,2)) +2000 end) 나이
into empAge
from employee where emp_no = p_emp_no;
return empAge; end; / select emp_id 사번, emp_name 사원명, fn_get_gender(emp_no) 성별,fn_bonus_calc(emp_id) 연봉, fn_get_age(emp_no) 나이 from employee where emp_no = '&emp_no'; --621225-1985634 selectfrom employee;
/2. 사원에게 특별상여금(보너스)를 지급하려고 하는데, 입사일기준으로 차등 지급하려 한다. 입사일기준 10년이상이면 150%, 3년이상 10년미만이면 125%, 3년미만이면 50%를 지급함. 저장함수명 : FN_BONUS_CALC, FN_GET_WORKING_DAYS(HIRE_DATE) 조회컬럼 : 사번, 사원명, 입사일, 근무기간(~년 ~개월), 보너스금액/
--사원번호와 근속년수를 입력받아 상여금 계산
create function FN_BONUS_CALC2(
p_emp_id employee.emp_id%type,
p_workYear number
)
return number
is
result number;
begin
select case when p_workYear >= 10 then salary1.5
when p_workYear between 3 and 9 then salary1.25
else salary*0.5
end
into result
from employee
where emp_id = p_emp_id;
return result;
end;
/
drop function FN_BONUS_CALC2;
--사번과 입사일을 입력하면 근속 년수를 계산하는 함수 create function FN_GET_WORKING_DAYS( v_emp_id employee.emp_id%type --v_hire_date employee.hire_date%type ) return number is workYear number; begin select to_number(trunc(months_between(sysdate, hire_date)/12)) 근속년수 into workYear from employee where emp_id = v_emp_id;
xxxxxxxxxxreturn workYear;
end; / --정답! select emp_id 사번, emp_name 사원명, FN_GET_WORKING_DAYS(emp_id) 근속년수, FN_BONUS_CALC2(emp_id, FN_GET_WORKING_DAYS(emp_id)) 상여금 from employee;
select emp_name, hire_date, to_number(trunc(months_between(sysdate, hire_date)/12))근속년수, round(mod(months_between(sysdate, hire_date), 12)) 개월 from employee order by 1;
/3. 기존부서테이블의 DEPT_ID, DEPT_TITLE만 복제한 DEPT_COPY를 생성한다. DEPT_ID 컬럼 PK추가. DEPT_ID 컬럼 변경 CHAR(3) DEPT_COPY를 관리하는 프로시져 PROC_MAN_DEPT_COPY를 생성한다. 첫번째 인자로 작동FLAG값 'UPSERT'/'DELETE'를 받는다. UPDATE시, 데이터가 존재하지 않으면 INSERT, 데이터가 존재하면 UPDATE DELETE시, 해당부서에 사원이 존재하는지(EMPLOYEE에서 FK로 참조하고있다고 가정)를 검사해서, 존재하면, 경고메세지와 함께 실행취소함. 그렇지 않으면, 삭제. 프로시저의 매개변수에 기본값을 지정하면, 생략가능함(매개변수명 mode 자료형 := 값)/ create table dept_copy as select dept_id, dept_title from department; select* from dept_copy;
alter table dept_copy add constraint pk_dept_id primary key(dept_id); alter table dept_copy modify dept_id char(3);
create or replace procedure PROC_MAN_DEPT_COPY(
flagNo IN number,
p_dept_id dept_copy.dept_id%type,
p_dept_title dept_copy.dept_title%type
)
is
v_cnt number;
c_cnt2 number;
begin
select count(*)
into v_cnt
from department
where dept_id = p_dept_id;
xxxxxxxxxxselect count(*)into c_cnt2from department D join employee E on D.dept_id = E.dept_codegroup by D.dept_id, D.dept_titlehaving D.dept_id = p_dept_id;if flagNo = 1 thenif v_cnt = 0 then insert into dept_copy values(p_dept_id, p_dept_title);else update dept_copy set dept_title = p_dept_title where dept_id = p_dept_id;end if;
else
if c_cnt2 = 0 then delete from dept_copy where dept_id = p_dept_id;
else dbms_output.put_line('해당 직원이 있어 삭제 불가');
return;
end if;
end if;
commit;
end;
/
select * from user_unique; --기록하기 commit; --commit 전까지 복구 --rollback;
select * from location; select * from employee; desc employee; select * from job; select * from department; select * from sal_grade;
--제약조건 조회 select constraint_name, UC.owner, UC.table_name, UCC.column_name, UC.constraint_type, UC.search_condition from user_constraints UC join user_cons_columns UCC using(constraint_name) where UC.table_name = 'EMPLOYEE'; /* constraint_type = 제약조건 유형 (user_constraints)
- P : 기본키 (Primary Key)
- R : 외래키 (Foreign Key) + r_constraint_name = 참조하고 있는 제약조건 명 (user_constraints)
- U : 고유값 (Unique)
- C : 체크 (Not Null, Check). search_condition 컬럼에서 내용 확인 가능합니다.
*/
CURSOR
sql문의 result set(결과집합)을 가리키는 참조변수(포인터)
하나 이상의 row에 대해서 순차적으로 접근할 수 있다.
--OPEN ~ FETCH ~ CLOSE
커서 종류
암시적 커서 : 모든 sql문이 실행됨과 동시에 암시적커서가 생성되서 처리됨
xxxxxxxxxx커서에 직접적으로 접근할 수 없다. 속성으로 처리하는 기능xxxxxxxxxxdeclarev_emp_id employee.emp_id%type;v_emp employee%rowtype;beginv_emp_id := '&emp_id';-- 커리 실행select *into v_empfrom employeewhere emp_id = v_emp_id;--커서 접근if sql%found then -- 실행시 행이 하나라도 있다면 found는 truedbms_output.put_line(v_emp_id || '사원 : ' || v_emp.emp_name);dbms_output.put_line('검색된 row수 = ' || sql%rowcount);end if;exceptionwhen no_data_found then dbms_output.put_line('검색 결과 없음');end;/--커서속성--sql%rowcount : 최근 실행된 sql문의 결과행을 리턴--sql%found : open후 fetch된 행이 있으면 true, 없으면 false;--sql%notfound : open후 fetch된 행이 있으면 false, 없으면 true;declarebeginupdate emp_copy set emp_name = emp_namewhere dept_code = '&부서코드';commit;dbms_output.put_line(sql%rowcound || '행이 업데이트 되었음');end;/명시적 커서 : 쿼리 결과집합에 직접 접근해서 처리할 필요가 있을경우 커서를 선언해서 사용한다.
(선언부)cursor
(실행부)OPEN
(실행부)fetch
(실행부)close
xxxxxxxxxxcreate table emp_cursor asselect * from employee;--1. 파라미터 없는 커서declarev_emp emp_cursor%rowtype;--커서 선언cursor mycursoris select *from employee;beginopen mycursor;loopfetch mycursor into v_emp;exit when mycursor%notfound;dbms_output.put_line('사번 : ' || v_emp.emp_id);dbms_output.put_line('사원명 : ' || v_emp.emp_name);dbms_output.put_line('급여 : ' || v_emp.salary);dbms_output.put_line('---------------------------');end loop;close mycursor;end;/--2. 파라미터 있는 커서declarecursor myc(p_dept_code emp_cursor.dept_code%type)isselect *from emp_cursorwhere dept_code = p_dept_code;--커서에서 fetch된 행을 담아둘 행변수v_emp emp_cursor%rowtype;--사용자 입력 부서코드를 담아둘 변수v_dept_code emp_cursor.dept_code%type;beginv_dept_code := '&dept_code';open myc(v_dept_code);loop fetch myc into v_emp;exit when myc%notfound;dbms_output.put_line('사번 : ' || v_emp.emp_id);dbms_output.put_line('사원명 : ' || v_emp.emp_name);dbms_output.put_line('급여 : ' || v_emp.salary);dbms_output.put_line('---------------------------');end loop;end;/--컬럼단위처리declarecursor cs(p_dept_code emp_cursor.dept_code%type)isselect emp_name,salaryfrom emp_cursorwhere dept_code = p_dept_code;v_emp_name emp_cursor.emp_name%type;v_salary emp_cursor.salary%type;beginopen cs('&dept_code')loop fetch cs into v_emp_name , v_salary;exit when cs%notfounddbms_output.put_line(v_emp_name || ',' || v.salary);end loop;close cs;end;/
TRIGGER
방아쇠, 연쇄반응
특정이벤트, ddl, dml문이 실행되었을 때
자동으로 어떤동작이 수행되도록 처리하는 객체
트리거 종류
dml trigger
ddl trigger
logon/logoff trigger
회원테이블에 탈퇴가 발생하면, 탈퇴회원테이블에 추가하기
테이블변경이 있을때, 수정내역을 로그테이블에 추가하기
/*create or replace trigger 트리거명before | afterupdate or delete or insert on 테이블명[for each row] --> 행단위, 문장단위 옵션begin(해당테이블에 dml을 실행시 처리로직)end;/*/create or replace trigger trg_emp_newafterinsert on emp_test1for each rowbegindbms_output.put_line('신입사원이 입사했습니다.');end;/-- 신입사원 insertinsert into emp_test1 values('999','띠그량','920727-1239874','eunchan2000@gmail.com','01068681231','D1','J1','s1',123123123,0.5,200,sysdate,null,default);
