[Oracle SQL 강의(뉴렉처)] 1강 ~ 44강
유튜브 강의 주소
보는 방법
- 강의가 화면 자료에 내용 설명이 적고, 적당히 설명해서 구체적으로 메모 힘들다. 따라서 키워드 위주로만 적었다.
- 강의 내용 복습은 메모한 것뿐만 아니라 강의 영상도 봐라.
- 여기서는 다음을 기록.
- 강의 목차와 키워드 및 내용
- 강의를 보고 생긴 의문에 대해 구글링해서 찾은 내용
1강 - 학습안내(SQL, DB, DBMS의 의미와 필요성)
강의 내용
- SQL이란?
- DBMS에게 질의하는 명령어
- DBMS: DataBase Management System
- 질의: 무엇을? 구조화된 데이터를
- Database와 DBMS의 개요
- Database 쓰기 전: 각 컴퓨터는 각각 데이터를 중복해서 가지고 있음. 따라서 어느 한 컴퓨터의 데이터가 수정되도 다른 컴퓨터의 데이터는 반영되지 않음. 따라서 각 컴퓨터의 데이터를 싱크하는데 텀이 있는 문제.
- Database 사용 후
- 장점: 각 컴퓨터는 하나의 데이터베이스를 통해 데이터에 접근. 따라서 어떤 컴퓨터가 데이터를 수정하면 다른 컴퓨터도 이를 바로 반영 가능. 또한 데이터를 각 컴퓨터가 중복해서 가질 필요 없게 됨.
- 해결해야 하는 점: 데이터베이스 사용 초반에 어떻게 데이터를 참조할 지(Hierarchical DBMS / Network DBMS / Object-Oriented DBMS / Relational DBMS)를 고민하다가 현재는 Relational DBMS가 쓰이고 있음. 또 동시성 문제(여러 컴퓨터가 하나의 데이터에 동시에 접근하는 문제), 성능은 어떻게 높일지, 보안은 어떻게 할지도 고려해야 함.
- DBMS 사용: Database 사용 후 해결해야 하는 점(동시성, 성능, 보안 등)을 DBMS가 다 관리함. 각 컴퓨터는 Database를 직접 사용하는 것이 아니라, DBMS를 통해 접근.
- SQL(Structured Query Lanquage)
- 각 컴퓨터는 DBMS에게 요청할 때 사용하는 명령어가 Query Lanquage.
- DDL, DML, DCL이 있음
강의 의문점에 대해 찾아봤다
- DDL, DML, DCL은?
- SQL문은 다루는 객체나 용도에 따라 그룹핑하여 나눌 수 있다.
- DDL(Data Definition Language): 데이터 구조를 정의하는 것. 데이터를 생성하거나 수정, 삭제 등 데이터의 전체 골격을 결정하는 언어
- CREATE: 데이터 베이스, 테이블 등을 생성
- ALTER: 테이블을 수정
- DROP: 데이터베이스, 테이블을 삭제
- DML(Data Manipulation Language): 데이터 조작 언어. 정의된 데이터베이스에 입력된 레코드를 조회하거나 수정하거나 삭제하는 등의 역할을 하는 언어. 사용자가 질의어를 통하여 저장된 데이터를 실질적으로 처리하는데 사용하는 언어
- SELECT: 데이터를 조회
- INSERT: 데이터를 삽입
- UPDATE: 데이터를 수정
- DELETE: 데이터를 삭제
- DCL (Data Control Language): 데이터 제어 언어. 데이터베이스에 접근하거나 객체에 권한을 주는 등의 역할을 하는 언어.
- GRANT: 특정 데이터베이스 사용자에게 특정 작업에 대한 수행권한 부여
- REVOKE: 특정 데이터베이스 사용자에게 특정 작업에 대한 권한을 박탈, 회수
2강 - 오라클 DBMS 18c XE 설치하기
설치 에러 해결 기록
- 23년 1월 10일 기준 18c XE가 아니라, 21c XE를 다운
- 설치 중 자꾸 에러가 나서 시간을 많이 까먹었다. 왜인지 찾아보니 저장경로에 한글이 있어서라는 의견이 있었다. 따라서 한글이 없는 저장경로에 설치했는데… 되는 것처럼 보였는데 잠시 쉬고 온 후 보니 에러가 또 나 있었다…
- 삽질의 과정(아래 과정은 아무 의미 없는 삽질이며, 다시 볼 땐 아래 과정은 다시 안 해도 됨)
- 다운로드한 파일 안에 있는 ISSetupPrerequisites 폴더 속 vcredist_x64.exe 파일을 다운받해야 하는지 걱정
- 혹시 내가 윈도우 11을 쓰고 있어서 안 되는 게 아닐까 걱정
- 방화벽을 꺼야 되는지에 대한 걱정
- 계속 찾다가 해당 영상을 보고 다시 시도했는데, 드디어 성공했다!!!!
- 오라클 설치 에러 해결법 정리
- 저장경로에 한글이 들어있으면 안 됨.
- 제어판(control panel) -> 사용자 계정(user accounts) -> 사용자 계정(user accounts)로 들어간다.
- 여기서 사용자 이름을 확인할 수 있는데, 사용자 이름도 한글이면 안 된다.
- 또 계정이 ms 계정인지 아니면 로컬 계정인지 확인할 수 있는데, ms계정이면 안된다. 로컬 계정으로 바꿔야 한다. 로컬 계정으로 바꾸는 법은 인터넷 보면 바로 알 수 있으니 패스
- 그 후 다시 설치 시도해 보니 끝내 성공!!!!!!
강의 내용
- 앞에서 데이터베이스 서버를 다운받았고, 우리가 서버를 쓸 것이다.
- 클라이언트 프로그램
- 이 서버 프로그램을 이용하려면 사용자 인터페이스를 가지고 있는 클라이언트 프로그램이 필요
- sqlplus(콜솔 기반)와 sql developer(윈도우 기반)가 있음
- sqlplus 사용
- 사용자명 입력(Enter user-name): sys as sysdba
- 비밀번호 입력(Enter password): 아무 비밀번호(이건 내 추측이니 신뢰하진 말 것)
3강 - SQL Developer 설치하기
- Windows 64-bit with JDK 11 included: 나의 경우 기존에 jdk 18.0.1.1 버전이 깔려 있지만, SQL Developer는 이 버전은 지원하지 않았다. 따라서 영상과 다르게 jdk가 포함된 버전을 다운받아 사용했다. 따라서 영상과 달리 jdk 저장경로를 입력하지 않고 바로 실행되었다.
4강 - 오라클 PDB 서버에 접속하기
강의 내용
- Seed PDB를 이용한 Pluggable 데이터베이스 생성
- PDB 확인: sqlplus에서
select name from v$pdbs;
- sql developer를 통해 PDB 서버에 접속하기
- 원격 접속을 위한 설정 변경
EXEC DBMS_XDB.SETLISTENLOCALACCESS(FALSE);
- 접속할 때는 데이터베이스가 설치된 컴퓨터의 ip 주소를 알아내서 호스트 이름을 localhost 대신 아이피 주소 입력해서 접속하기
- 아이피 알아내는 법: 데이터베이스가 설치된 컴퓨터에서 cmd 열고, ipconfig를 치면 알 수 있음
- 단 그 컴퓨터에서 방화벽이 설치되어 있어서 포트가 열려있지 않으면 접속 안 됨
강의 의문점에 대해 찾아봤다
- CDB와 PDB란?
- 멀티테넌트(Multi Tenant): 말 그대로 다중 세입자라는 뜻으로 하나의 DB 안에 여러 개의 DB가 포함되어 있는 구조다.
- CDB와 PDB
- 예를 들어 MYCDB 안에 pdb1, pdb2가 들어있는 경우를 떠올려 보자. 밖에서(시스템 상에서) 보면 달랑 MYCDB 라는 DB 1개만 보인다. 하지만 DB를 사용하는 사용자 입장에서 보면 pdb1 와 pdb2 2개가 별개로 각각 존재하는 것처럼 보인다. 시스템 상으로는 1개의 DB이기 때문에 패치, 백업 등의 작업을 통으로 1번만 해주면 되어, 관리가 수월해진다.
- pdb1, pdb2처럼 셋방살이 하고 있는 세입자(tenant) DB들을 PDB(Pluggable DB)라고 하고, MYCDB 처럼 pdb1, pdb2 를 담고 있는(contain) 집주인 DB를 CDB (Container DB) 라고 한다.
- Pluggable이란 DB를 USB 꼽듯이 꼽았다(Plug) 뽑았다(Unplug) 하기가 매우 쉽기 때문에 이런 이름이 붙었다고 한다.
- PDB끼리는 서로 독립적이다. 서로 데이터를 공유하지 않는다.
5강 - 수업용 사용자와 데이터베이스 생성하기
강의 내용
- predefined user accounts provided by oarcle database
- 데이터베이스 설치하자마자 이미 계정이 있음
- admin accounts(관리자 계정)
- sys: 관리자 계정은 관리자 권한(sysdba)를 가지고 있어야 한다. sys는 sysdba를 가지고 있다. 따라서 이 계정은 데이터베이스 전체적으로 완전하게 관리할 수 있다.
- system: 일반적으로 데이터베이스 관리하는 계정
- default sample schema user accounts
- 우리들이 쿼리 연습할 수 있도록 하는 계정
- BI(business intelligence), HR(human resources), OE(order entry), PM(product media), IX(information exchange), SH(sales)
- 데이터베이스와 데이터 파일 그리고 테이블 스페이스
- 테이블과 인덱스와 기타 오브젝트로 이루어진 데이터베이스 구조를 스키마(schema)라고 함
- 테이블 스페이스 생성
강의 의문점에 대해 찾아봤다
- 테이블 스페이스란?
- 내가 조회하는 이 데이터들은 도대체 어디에 저장 되어 있는건가? 데이터를 조작했으니 어딘가에는 물리적으로 존재해야한다는 것을 인식하게 된다. Oracle에서는 Data file 이라는 물리적 파일 형태를 저장한다. 이러한 Data file이 하나 이상 모여서 Tablespace라는 논리적 저장공간을 형성한다.
- Tablespace는 하나의 데이터베이스 안에 가장 큰 논리적 저장공간이다. 업무의 단위나 사용용도에 따라 여러 개의 Tablespace로 분리하여 관리된다. 이는 Segment(오브젝트)라는 논리적 저장공간의 집합이기도 하다.
6강 - MEMBER 테이블 생성하기
- SQL 구분(1강 부분에서 정리해놓은 내용이 있으니 참고해라)
- DDL
- 구조화된 데이터를 정의. 나는 이러이러한 속성을 가진 데이터를 다룰 것임을 나타냄. 이러한 속성을 가진 테이블을 CREATE하는 거고
- CREATE, ALTER, DROP
- DML: SELECT, INSERT, UPDATE, DELETE -> CRUD
- DCL: GRANT, REVOKE
- DDL
- 테이블 생성하기 - CREATE
- 테이블 정의하기 = Entity(뉘앙스는 자바의 클래스 느낌?인 것 같음) 정의하기 = 데이터 구조 정의하기 = 개념 상의 데이터 정의하기
- 자바에서 클래스와 테이블은 크게 다르지 않음. 자바의 자료형만 오라클의 자료형으로 바꾼다고 생각하면 쉬움. 아래 예제처럼 CREATE해서 우리가 다룰 구조화된 데이터는 이런 속성을 가진 데이터임을 정의함.
- 주의점: sql에서 {중괄호}가 아니라 (소괄호)를 써야 함. 또 테이블 CREATE한 다음에 새로고침해야 옆에 보임.
class MEMBER { ID // int NAME // String GENDER // String }
CREATE TABLE MEMBER ( ID NUMBER, NAME VARCHAR2(50), GENDER VARCHAR2(50) )
7강 - 오라클 데이터 형식 #1 (문자 형식)
- oracle built data type: charater 형식, numeric 형식, date 형식, lob 형식
- character: ‘abc’, ‘123’
- numeric: 123, 4.56, 3.85F
- date: ‘2022-02-22’, 연도-월-일 표현
- timestamp: ‘2022-02-22 10.38.29.00000’, 연도-월-일뿐 아니라 시.분.초까지 표현
- charater 형식
- CHAR(SIZE)
- SIZE는 1BYTE 단위. 예로 ID를 CHAR(50)으로 하면 문자 50개(영어 기준)를 저장 가능(고정 길이)
- 단 B 사용자는 50글자 꽉 채워서 넣을 수 있지만, A 사용자는 2글자만 넣을 수 있는 것처럼 가변적. 따라서 CHAR는 고정 길이이므로 50글자에 전혀 못 미쳐서 텅텅 비고 비효율적.
- VARCHAR2(SIZE)
- 가변적이다. 따라서 VARCHAR2(500)으로 잡은 ID에 2글자만 입력되면, 나머지 498은 반환됨.
- 즉 VARCHAR2(500)에서 500(SIZE)는 최대 몇 글자 쓰겠다는 뜻
- VARCHAR2(SIZE) 있으니까 CHAR(SIZE) 필요없는 거 아님?
- 아니다. VARCHAR2(SIZE)의 경우 길이가 들쭉날쭉해서 각 데이터 끝에 끝났다는 구분자를 넣음. 따라서 데이터 검색할 때(몇 번째 데이터 검색) 일일히 구분자 세 가면서 몇 번째 데이터를 확인해야 해서 검색 속도 느림
- 반면 CHAR의 경우 길이가 고정되어 있어서 검색 속도가 빠름
- NCHAR(SIZE)
- 여기서 SIZE는 2 혹은 3 BYTE이다.
- 왜냐. 전 세계의 문자(NATIONAL)를 저장할 때 필요
- NVARCHAR(SIZE)
- 이것도 SIZE는 2 혹은 3 BYTE이다.
- NCHAR(SIZE)처럼 national한 VARCHAR2(SIZE)다.
- CHAR(SIZE)
- 실습
- CHAR(2 BYTE), CHAR(2 CHAR): 전자는 2바이트를 뜻하고, 후자는 2개의 문자를 받겠다는 뜻
- 단 CHAR(2 CHAR)로 한글 받는 건 비추. NCHAR(2)가 더 바람직
- 최대 SIZE
- 문자 형식은 최대 4000BYTE(STANDARD)까지 사용가능. 즉 NCHAR의 경우 한 문자당 2BYTE이므로 NCHAR(2000)이 최대
- 물론 EXTENDED의 경우 32767BYTE까지 사용가능
8강 - 오라클 데이터 형식 #2 (숫자,날짜 형식)
강의 내용
- 추가 character 형식
- LONG: 최대 2GB까지. 최근 잘 안씀. 대용량 텍스트 다룰 때 이걸 대신해서 나온게 CLOB이니까
- CLOB: CHARACTER LARGE OBJECT, 대용량 텍스트 데이터 타입(최대 4GB)
- NCLOB: NATIONAL CLOB, 대용량 텍스트 유니코드 데이터 타입(최대 4GB)
- numeric 형식
- 설명 더 좋은 블로그 찾았음으로 생략하고, 내 정리 및 강의 영상 참고.
- date 형식
- DATE: 년-월-일 혹은 월-일-년 등등. 형식은 TIMESTAMP WITH LOCAL TIME ZONE(지역)에 따라 다르게 표시됨
- TIMESTAMP: 년-월-일 뿐만 아니라 시.분.초도 표현
- TIMESTAMP WITH LOCAL TIME ZONE: NLS_TIMESTAMP_FORMAT 파라미터에 명시된 값.
강의 의문점에 대해 찾아봤다
- numeric 형식?
- NUMBER (p, s) 형식으로 크기를 지정
- p(precision, 정밀도)는 최대 유효숫자(0 제외) 자릿수를 나타낸다.
- s(scale, 배율)는 소수점 기준 자릿수를 나타낸다.
- s가 양수면 소수점이하, 음수면 소수점 이상 자릿수를 나타낸다.
- s의 범위 밖의 값은 반올림되어 적용된다.
- 예시
- NUMBER(3)에 123.74 저장: 결과는 124이다. s는 명시하지 않아 0, p가 3이므로 소수점 첫 자리에서 반올림됨
- NUMBER(3, 2)에 123.74 저장: 결과는 오류. p가 3인데 입력값인 123.74는 유효숫자가 다섯 자리이기 때문에 오류가 발생한다.
- NUMBER(5, 2)에 123.74 저장: 결과는 123.74
- NUMBER(7, 1)에 123.74 저장: 결과는 123.7이다.
- NUMBER(7, -1)에 123.74 저장: 결과는 120이다. s가 -1 이기 때문에 소수점 왼쪽 첫 자리 3이 반올림 됨.
- NUMBER(4,5)에 0.1234 저장: 오류. 유효숫자는 4개는 충족하지만 s가 5인데 네 자리수이므로 오류 발생
- NUMBER(3,7)에 0.0001234 저장: 오류. s가 7로 소수점 이하 일곱 째 자리까지 p가 3으로 유효숫자는 3개가 조건인데. 1234로 유효숫자가 4개 이므로 오류 발생
- NUMBER(3,7)에 0.00001234 저장: 결과는 0.0000123. 소수점 이하 일곱 째 자리까지 유효숫자는 123, 4는 제외됨.
- NUMBER (p, s) 형식으로 크기를 지정
9강 - 테이블 수정하기(ALTER TABLE)
- 테이블 수정하기(ALTER TABLE)
- 수정(MODIFY):
ALTER TABLE MEMBER MODIFY ID NVARCHAR2(20);
- 만약 ID 자료형을 NUMBER로 바꿀 수 있을까? 기존에 들어있는 값들이 NUMBER형으로 변환 가능한 값일 때만 가능하고, 아니면 오류
- 삭제(DROP):
ALTER TABLE MEMBER DROP COLUMN AGE;
- 추가(ADD):
ALTER TABLE MEMBER ADD EMAIL VARCHAR2(200);
- 수정(MODIFY):
- DDL 굳이 쓰지 말고…
- DDL의 경우 수정 사항이 어쩌다 한 번 일어나서(잘 안 써서) 현직 개발자들도 자주 까먹는다.
- sql developer같은 툴로 쉽게 테이블 변경할 수 있으니까 외우는 거 대시 툴을 활용하자
10강 - SQL 쿼리 연습용 테이블 준비하기
- CREATE TABLE (테이블 이름)했는데 이름이 부적하다고 나오면?
- CREATE TABLE COMMENT을 실행하니 부적합하다고 한다. 이는 띄어쓰기 등의 부적합한 문자나 예약어인 경우에 해당한다.
- 부적합한 문자나 예약어인 경우 “큰 따옴표”로 감싸면 허용된다.
11강 - 데이터 조작하기 #1 (INSERT/SELECT)
- 데이터 삽입하기 - INSERT
- INSERT 명령 규칙: INSERT INTO <테이블> VALUES <값 목록="">값>테이블>
- 모든 필드 값을 입력하기: INSERT INTO MEMBER VALUES(테이블의 모든 필드에 해당하는 값 전부 입력)
- 필드: RDB 용어에서 속성, 컬럼, 필드, 도메인 다 같은 말이다. 열을 뜻함.
- 원하는 필드만 원하는 순서대로 입력하기(아래 두 경우 모두 됨)
- INSERT INTO MEMBER(ID, PWD) VALUES(‘NEWLEC’, ‘1111’)
- INSERT INTO MEMBER(PWD,ID) VALUES( ‘1111’, ‘NEWLEC’)
- 위 두 경우 ID와 PWD를 제외한 나머지 필드들은 NULL값이 들어감
- 팁: 예약어, 명령어, 필드, 테이블 이름 등은 대소문자 구별 x. 단 값은 대소문자 구별함
- SELECT
- SELECT * FROM MEMBER: 기본 명령 예시
- SELECT ID, NAME, PWD FROM MEMBER: 기본 명령 예시
- SELECT ID AS user_id, NAME, PWD FROM MEMBER: id 컬럼명을 user_id로 출력하고 싶을 때 as 사용
- SELECT ID user_id, NAME, PWD FROM MEMBER: 바로 위의 as는 생략해도 똑같이 기능. 많은 개발자들은 생략하고 씀
- SELECT ID “user id”, NAME, PWD FROM MEMBER: 공백을 표시하고 싶을 땐 “큰 따옴표”로 감싸기
12강 - 데이터 조작하기 #2 (UPDATE/DELETE)
- 데이터 수정하기 - UPDATE
- UPDATE MEMBER SET PWD=’222’: 모든 PWD가 222로 바뀜
- UPDATE MEMBER SET PWD=’222’ WHERE ID=’NEWLEC’: 조건절(WHERE)에 맞는 데이터만 바뀜
- UPDATE MEMBER SET PWD=’222’,NAME=’HELLO’ WHERE ID=’NEWLEC’: 두 개 이상의 열의 데이터를 바꾸고 싶으면 콤마로 이어주면 된다.
- 삭제하기 - DELETE
- DELETE MEMBER WHERE ID=’NEWLEC’
13강 - 트랜잭션 처리를 위한 COMMIT과 ROLLBACK
- 트랜잭션이란? 함께 수행되어야 할 일련의 동작/업무 실행단위/논리 명령단위/개념상의 실행 단위
- 업무적인 단위로 계좌 이체가 있다. 계좌 이체가 트랜잭션이다. 이 트렌잭션은 물리적인 명령어 단위(퀴리문)로 이루어져 있다. 여기선 A 계좌 잔고를 업데이트하고, B 계좌 잔고도 업데이트하는 두 가지 명령어 단위가 있다.
- 단 트랜잭션 도중 오류가 발생했을 시, 오류가 실제로 적용되면 안된다. 즉 어떤 트랜잭션의 모든 명령어가 완전하게 종료되기 전까진 실제로 적용되면 안된다. 따라서 현재 세션을 위한 임시저장소에서 테스트가 진행된다.
- 또한 어떤 트랜잭션이 실행하는 동안 다른 세션이 건드리지 못하도록 LOCK을 걸어야 한다.
- COMMIT/ROLLBACK
- 기본적으로 트랜젝션 처리를 하기 위해서 해당 명령들이 임시 저장소에서 테스트됨. 즉 데이터베이스 만들 때 두 개의 테이블 스페이스를 만들었다. 하나는 임시 테이블 스페이스, 다른 하나는 영구적으로 사용되는 테이블 스페이스.
- 내가 기본적으로 창 열어서 명령들 입력하면, 이는 다 내 임시저장소에서만 반영되는 것임. 그 후 내가 한 명령들이 오류가 없음을 확인한 후에는 다른 사용자(세션)이 봐도 문제 없을 것 같으면
COMMIT;
명령어를 사용할 수 있다. - COMMIT: 커밋 후에는 모든 세션에 변경 사항이 반영된다.
- ROLLBACK: 내가 명령들 치다가, 마음에 안 듦. 그럼
ROLLBACK;
명령을 치면, 변경된 내용은 반영되지 않고, 이전 커밋 내용으로 복원됨. - COMMIT도 ROLLBACK하기 전에 작업하고 실행한 것은 무조건 LOCK이 된다. 따라서 다른 세션은 작업한 곳에서 커밋하던지 취소하던지 해야 다른 세션 명령이 실행된다.
- 예: 똑같은 레코드에 대한 명령을 예시로 들자. 내가
UPDATE MEMBER SET PWD='1234',NAME='뉴렉처' WHERE ID='NEWLEC';
을 수행했지만 아직 커밋이나 롤백하지 않음. 이 때 다른 세션에서UPDATE MEMBER SET PWD='4321',NAME='처렉뉴' WHERE ID='NEWLEC';
을 침. 그럼 앞의 명령이 커밋되거나 취소될 때까지 두 번째 명령은 계속 대기된다.
- 예: 똑같은 레코드에 대한 명령을 예시로 들자. 내가
14강 - 산술 연산자
- 공지사항 조회수 조회할 때, 1을 더해서 조회해라.
- SELECT HIT+1 FROM NOTICE: 문제점이 있다. 명령의 결과 컬럼 이름이 HIT+1로 나온다. JDBC같은 사용자는 컬럼 이름을 가지고 데이터를 뽑아낸다. 따라서 이름이 바뀌면 문제가 될 수 있다.
- SELECT HIT+1 HIT FROM NOTICE: 위 문제 해결
- 오라클에서 1+3을 출력하고 싶을 때
- 그냥 1+3 실행하면 안된다. SELECT문을 이용해야 한다.
- 이 때 SELECT 1+3 FROM DUAL: DUAL은 더미 테이블이다. 즉 이는 데이터를 뽑아내는 테이블이 아난 의미 없는 테이블이다. 이를 넣는 이유로는 SELECT 구문에 반드시 FROM이 들어가야 하니 형식상 맞추는 역할이다.
- 그럼 SELECT 1+’3’ FROM DUAL의 결과는? 4가 나온다.
- 오라클에서는 무조건 문자가 숫자로 바뀐다. 왜냐면 + 연산자는 무조건 숫자만 연산해주는 연산자이기 때문이다.
- SELECT 1+’A’ FROM DUAL: 문자 A는 숫자로 바꿀 수 없으므로 오류가 나온다.
SELECT 1||'3' FROM DUAL
:||
연산자는 두 문자들을 합치는 연산자다. 따라서 13이 출력된다.
||
연산자- 두 문자들을 합치는 연산자
- 회원의 이름을 조회할 때, 이름에 ID도 같이 붙여서 나타내고 싶을 때
- 즉 홍길동(HONG)처럼 나타내고 싶을 때
SELECT NAME ||'('||ID||')' FROM MEMBER
SELECT NAME||'('||ID||')' NAME FROM MEMBER
: 연산된 컬럼에 별칭 사용하기
15강 - 비교연산자(=,!=,^=,…)
- 원하는 것만 필터링하고 싶을 때 비교 연산자나 관계 연산자 활용
- 비교 연산자
- 종류: =, !=, ^=, <>, >, <, >=, <=, IS NULL, IS NOT NULL
- !=, ^=, <>: 전부 같지 않은지를 비교하는 연산자. 이 중에 !=가 가장 많이 쓰이고, <>는 ANSI SQL에서 정의하고 있는 것이다. ^=은 오라클 포함 몇몇 DBMS만 쓰기 때문에 잘 안 쓰인다.
- 게시글 중에 작성자가 ‘NEWLEC’인 게시글만 조회하시오: SELECT * FROM NOTICE WHERE WRITER_ID=’NEWLEC’
- 게시글 중에 조회수가 3이 넘는 글만 조회하시오: SELECT * FROM NOTICE WHERE HIT>3
- 게시글 중에서 내용을 입력하지 않은 게시글을 조회하시오
- (X) SELECT * FROM NOTICE WHERE CONTENT=’NULL’
- (O) SELECT * FROM NOTICE WHERE CONTENT IS NULL
- 종류: =, !=, ^=, <>, >, <, >=, <=, IS NULL, IS NOT NULL
16강 - 관계연산자(AND, OR, BETWEEN, IN)
- AND, BETWEEN
- 조회수가 0,1,2인 게시글을 조회하시오
SELECT * FROM NOTICE WHERE HIT>=0 AND HIT<=2;
SELECT * FROM NOTICE WHERE HIT BETWEEN 0 AND 2;
- 조회수가 0,1,2인 게시글을 조회하시오
- OR, IN
- 조회수가 0,2,7인 게시글을 조회하시오
SELECT * FROM NOTICE WHERE HIT=0 OR HIT=2 OR HIT=7;
SELECT * FROM NOTICE WHERE HIT IN(0, 2, 7);
- 조회수가 0,2,7인 게시글을 조회하시오
- NOT
- 조회수가 0,2,7이 아닌 게시글을 조회하시오
SELECT * FROM NOTICE WHERE HIT NOT IN(0, 2, 7);
17강 - 패턴 비교 연산자(LIKE, %, _
)
- 회원 중에서 박씨 성을 조회하시오
SELECT * FROM MEMBER WHERE NAME LIKE '박%';
- LIKE 연산자:
SELECT FROM MEMBER WHERE NAME = '박%';
로 하면 실제로 이름이 ‘박%’인 사람만 조회하는 것임. 내가 원하는 건 박으로 시작하는 패턴이니까 이 때는 LIKE를 써야 함. - % 연산자:
WHERE NAME LIKE '박%'
의 뜻은 박으로 시작하는 이름을 필터링함.
- 회원 중에서 박씨이고 이름이 외자(한글자)인 회원을 조회하시오
SELECT * FROM MEMBER WHERE NAME LIKE '박_';
-
연산자:WHERE NAME LIKE '박_'
의 뜻은 박으로 시작하는 이름을 필터링함. 단 글자 수 제한이 있음. 여기선박_
이니까 박제, 박사같은 이름만 검색될 것이고,박__
이면 박일이, 박삼사같이 이름이 두 글자인 사람만 검색될 것임.
- 회원 중에서 박씨 성을 제외한 회원을 조회하시오
SELECT * FROM MEMBER WHERE NAME NOT LIKE '박%';
- 회원 중에서 이름에 ‘도’자가 들어간 회원을 조회하시오
SELECT * FROM MEMBER WHERE NAME LIKE '%도%';
18강 - 정규식을 이용한 패턴 비교(REGEXP_LIKE)
- 정규표현식 찾고 테스트할 수 있는 사이트: Regular Expression Library
- 아래 6개 패턴의 전화번호만 조회하고 싶을 때 정규표현식을 작성하기
- 010-1234-1234, 011-342-6453, 016-543-1564, 017-522-1114, 018-543-1564,019-773-2264,
^01[016-9]-\d{3,4}-\d{4}$
- ^와 $: 정규표현식에서 시작을 뜻하는 게 ^고, 끝을 나타내는 게 $이다.
- 단 이렇게 하면 위의 전화번호 패턴만 있으면 찾을 수 있지만, “ASD 010-1234-1234 ASD”같이 섞여있으면 안된다. 왜냐면 ^가 있기 때문에 무조건 01[]로 시작하고, $가 있기 때문에 무조건 1234처럼 끝나야지만 찾을 수 있다. 따라서 이런 경우에는 ^와 $를 빼야 한다.
01[016-9]
: []는 하나의 글자를 나타냄. 또 6-9의 뜻은 6에서 9까지란 의미(즉 6,7,8,9)이다. 따라서01[016-9]
의 뜻은 01로 시작하고 나머지 한 글자는 0이거나 1 또는 6 또는 6~9일 수 있따는 뜻.- \d{3,4}
- \d는 decimal digit(a digit from 0 to 9 in decimal notation)이란 뜻으로,
[0-9]
와 동일하다. - {3,4}: 예로 ab{2}라 하면 abb가 된다. 즉 앞의 문자를 {}안에 지정한 숫자만큼 반복한다는 뜻이다. 따라서 \d{3,4}의 뜻은 \d\d\d 또는 \d\d\d\d를 뜻한다.
- \d는 decimal digit(a digit from 0 to 9 in decimal notation)이란 뜻으로,
- REGEXP_LIKE
- regular expression function이다.
- 제목에 전화번호가 포함된 게시글을 조회하시오
- (X)
SELECT * FROM NOTICE WHERE TITLE LIKE '01[016-9]-\d{3,4}-\d{4}';
: 오라클에서 제공하는 패턴 연산자(%나_
)가 아니고 정규표현식이기 때문에, LIKE를 쓸 수 없다. - (O)
SELECT * FROM NOTICE WHERE REGEXP_LIKE(TITLE, '01[016-9]-\d{3,4}-\d{4}');
- (X)
19강 - 문자열 비교를 위한 정규식(^\D\w+@…)
- 이메일을 정규표현식으로 표현. 단 끝은 org, net, com만 허용된다고 하자. 또한 123asd@123asd.com처럼 @ 앞뒤 단어의 시작이 숫자로 되면 안 된다고 하자.
\D\w*@\D\w*.(org|net|com)
\D\w*
:- \D: Matches any nondigit. Equivalent to
[^0-9]
. 즉 0~9가 아닌 문자만 와야 하나만 와야 한다. @ 앞뒤 단어의 시작이 숫자로 되면 안 된다라는 조건이 있기 때문에 넣었다. - \w: Matches any word character. equivalent to
[a-zA-Z_0-9]
. 즉 소문자, 대문자, 언더바, 숫자를 포함하는 어떤 단어든 올 수 있다는 뜻이다. *
: 0 or more of previous expression. 즉 0개 이상이어야 한다는 뜻이다.\w*
이란 뜻은 \w에 해당하는 문자가 0개 이상 와야한다는 뜻이다. 참고로 +는 1개 이상 와야한다는 뜻인데, \w+라 하면 \w에 해당하는 문자가 하나 이상 와야한다는 뜻이다. 이 때 \D를 해서 숫자가 아닌 문자를 하나 받았으므로 +가 아니라*
를 \w 뒤에다 썼다.
- \D: Matches any nondigit. Equivalent to
-
(org net com) - (): Logical grouping of part of an expression. 즉 이메일 끝 단어를 묶어준 것이다.
-
: 단어를 구분해 주는 용도.
20강 - ROWNUM 그리고 행 제한하기
- 행 제한하기
- 회원 목록에서 상위 5명만 조회하시오
- ROWNUM: 이것은 SELECT해서 결과집합을 만들 때 만들어진다(간단히 말하면 SELECT할 때 만들어지는 번호). 즉 기존 테이블을 읽고 WHERE절에 해당하는 열을 결과집합에 넣을 때마다 1씩 증가하는 것이다. 따라서
SELECT * FROM MEMBER WHERE ROWNUM BETWEEN 1 AND 5;
는 된다. 하지만SELECT * FROM MEMBER WHERE ROWNUM BETWEEN 6 AND 10;
은 빈 결과집합을 얻는다. ROWNUM은 무조건 1부터 자동으로 만들어지는데, 여기서 조건이 6부터 10이므로 모든 열이 해당되지 않아 아무것도 출력되지 않는다. - 그럼 ROWNUM이 결과집합 만들어질 때(즉 SELECT할 때) 생성되는 게 아니라 기존에 있었다면 가능할 것이다.
SELECT * FROM (SELECT ROWNUM NUM, MEMBER.* FROM MEMBER) WHERE NUM BETWEEN 1 AND 5;
FROM (SELECT ROWNUM NUM, MEMBER.* FROM MEMBER)
: 먼저 MEMBER의 모든 컬럼(MEMBER.*
)과 ROWNUM을 합쳐서 결과집합을 만든다. 이렇게 만든 결과집합에 대해 SELECT하겠다는 것이다.WHERE NUM BETWEEN 1 AND 5
: 앞에서 ROWNUM애 NUM이라는 별칭을 주었다. 이 별칭을 안 주고SELECT * FROM (~생략~) WHERE ROWNUM BETWEEN 6 AND 10;
처럼 했다면 WHERE의 ROWNUM은 기존의 ROWNUM(FROM (SELECT ROWNUM, MEMBER.* FROM MEMBER)
해서 만들어진 ROWNUM)이 아니라, 새로 만들어지는 ROWNUM을 가리킨다. 따라서 아무것도 출력되지 않으므로, 이를 구분하기 위해서 별칭을 부여한 것이다.
- ROWNUM: 이것은 SELECT해서 결과집합을 만들 때 만들어진다(간단히 말하면 SELECT할 때 만들어지는 번호). 즉 기존 테이블을 읽고 WHERE절에 해당하는 열을 결과집합에 넣을 때마다 1씩 증가하는 것이다. 따라서
- 회원 목록에서 상위 5명만 조회하시오
21강 - 중복 값 제거하기 DISTINCT
- 중복 값 제거하기(DISTINCT)
- 기존에 테이블에 19, 19, 19, 21, 23이 저장되어 있고,
SELECT AGE FROM MEMBER;
해서 저장된 값이 전부 나온다. - 하지만 이 때 종복되는 나이는 빼고 싶을 때,
SELECT DISTINCT AGE FROM MEMBER;
를 치면 결과로 19, 21, 23이 나온다. 즉 중복되는 나이 19가 빠진 것이다.
- 기존에 테이블에 19, 19, 19, 21, 23이 저장되어 있고,
22강 - 중간요약과 함수 단원 안내
- 메모할 내용은 없었다.
23강 - 문자열 내장 함수 #1
- 문자열 추출 함수 SUBSTR 함수
- SUBSTR(문자열, 시작위치, 길이)
- SELECT SUBSTR(‘HELLO’,1,3) FROM DUAL: 출력결과는 “HEL”
- SELECT SUBSTR(‘HELLO’,3) FROM DUAL: 출력결과는 “LLO”
- SELECT SUBSTRB(‘HELLO’,3) FROM DUAL: 출력결과는 “LLO”지만, 차이점은 1 BYTE단위로 끊는다.
- SELECT SUBSTRB(‘안녕하신가’,2) FROM DUAL: 출력결과는 “녕하신가”
- SELECT SUBSTRB(‘안녕하신가’,3) FROM DUAL: 출력결과는 “녕하신가”
- 모든 회원의 이름과 출생 월만을 조회하시오(BIRTHDAY 형식: 2000-01-01)
- SELECT NAME, SUBSTR(BIRTHDAY,6,2) MONTH FROM MEMBER
- 회원 중에서 전화번호가 011로 시작하는 회원의 모든 정보를 조회하시오
SELECT * FROM MEMBER WHERE SUBSTR(PHONE,1,3)='011'
: 이것도 되긴 하는데… 성능면으로 보면, 연산자가 조금 더 좋기 때문에, 연산자로만 해결 가능하면 함수 대신 연산자 쓰기SELECT * FROM MEMBER WHERE PHONE LIKE '011%'
- SUBSTR(문자열, 시작위치, 길이)
- 문자열 덧셈 함수
SELECT CONCAT('홍','길동') FROM DUAL
- 단 덧셈 함수는 잘 안 쓰인다.
||
연산자가 있기 때문이다. SELECT '홍'||'길동' FROM DUAL
- 문자열 트림 함수: 공백 없애기
SELECT LTRIM(' HELLO ') FROM DUAL
: 왼쪽 공백만 없애기SELECT RTRIM(' HELLO ') FROM DUAL
: 오른쪽 공백만 없애기SELECT TRIM(' HELLO ') FROM DUAL
: 양쪽 공백 없애기
- 소문자/대문자 변경 함수
- 주로 어디에 쓰는가? 회원의 아이디가 ‘NEWLEC’인 회원을 조회하되, 대소문자를 가리지 않을 때
SELECT * FROM MEMBER WHERE UPPER(ID)='NEWLEC'
: 혹은 LOWER(ID)=’newlec’이라고 해도 대소문자 가리지 않고 검색 가능
SELECT LOWER('ASdf') FROM DUAL
: 다 소문자로SELECT UPPER('ASdf') FROM DUAL
: 다 대문자로
- 주로 어디에 쓰는가? 회원의 아이디가 ‘NEWLEC’인 회원을 조회하되, 대소문자를 가리지 않을 때
- 문자열 대치 함수 REPLACE와 TRANSLATE(문자열, 찾는 문자열, 대치할 문자열)
SELECT REPLACE('WHERE WE ARE', 'WE', 'YOU') FROM DUAL
: 출력 결과 “WHERE YOU ARE”. WE를 YOU로 대치하는 것SELECT TRANSLATE('WHERE WE ARE', 'WE', 'YOU') FROM DUAL
: 출력 결과 “YHORO YO ARO”. W를 Y로 E를 O로 대치하는 것. U는 아무것도 대치되는 것이 없으므로 무효.- 회원의 주소를 빈칸없이 출력하기
SELECT REPLACE(ADDRESS, ' ', '') FROM MEMBER;
24강 - 문자열 내장 함수 #2
- 문자열 패딩 함수(채우는 함수)
SELECT LPAD('HELLO', 10, '0') FROM DUAL
: 출력결과 “00000HELLO”SELECT RPAD('HELLO', 10, '0') FROM DUAL
: 출력결과 “HELLO00000”
- 문자열 검색 함수 INSTR
- INSTR(문자열, 검색문자열, 찾을 시작위치, 찾을 수)
SELECT INSTR('ALL WE NEED TO IS JUST TO...','TO') FROM DUAL
: 결과 13(TO의 위치)가 반환된다.SELECT INSTR('ALL WE NEED TO IS JUST TO...','TO',1,2) FROM DUAL
: 결과 24(TO의 위치)가 반환된다. 이 때 1의 뜻은 1번째 위치부터 찾으라는 소리고, 2의 뜻은 두 번째 TO의 위치를 뜻한다.
- INSTR(문자열, 검색문자열, 찾을 시작위치, 찾을 수)
- 문자열 길이 얻기
SELECT LENGTH('WHERE WE ARE') FROM DUAL
25강 - 숫자 내장 함수(ABS/SIGN/ROUND/TRUNC/CEIL/FLOOR)
- 절댓값 ABS
- SELECT ABS(-35) FROM DUAL;
- 결과는 35
- 음수/양수 알려주는 SIGN
- SELECT SIGN(-35) SIGN(35) SIGN(0)FROM DUAL;
- 결과는 -1, 1, 0
- 반올림 ROUND
- SELECT ROUND(34.56) ROUND(12.3456, 2) FROM DUAL;
- 결과는 35, 12.35이다. ROUND(12.3456, 2)서 2의 뜻은 소수점 이하 2번째까지 출력해라는 소리
- TRUNC(절사 함수), MOD(나머지 연산)
- SELECT 17/5 FROM DUAL: 결과는 3.4가 출력됨
- SELECT TRUNC(17/5) FROM DUAL: 결과는 3이 출력됨
- SELECT MOD(17,5) FROM DUAL: 결과는 2(나머지)가 출력됨
- 제곱 POWER, 제곱근 SQRT
- SELECT POWER(5,2), SQRT(25) FROM DUAL: 결과는 25, 5이다.
26강 - 날짜 함수(SYSDATE/CURRENT_DATE/SYSTIMESTAMP/CURRENT_...
)
- 현재 시간을 얻는 함수
- SELECT SYSDATE, CURRENT_DATE, SYSTIMESTAMP, CURRENT_TIMESTAMP FROM DUAL
- SYS는 오라클 서버 시간을, CURRENT는 사용자 시간을 나타낸다.
- DATE는 년/월/일을, TIMESTAMP는 년/월/일에 시.분.초까지 나타냄
- 세션 시간과 포맷 변경
- ALTER SESSION SET TIME_ZONE=’09:00’: 타임존은 나라마다 다르고, 자신이 있는 나라의 타임존으로 설정가능. 한국은 타임존이 09:00이다.
- ALTER SESSION SET NLS_DATE_FORMAT=’YYYY-MM-DD HH24:MI:SS’: NLS는 NATIONAL LANGUAGE SUPPORT로 전세계 많은 언어에서 쓰이는 표기법을 지원하기 위해서인 느낌이 든다. 이렇게 설정하면 SYSDATE나 CURRENT_DATE을 출력해보면 정한 포멧으로 출력됨을 알 수 있다. 단 TIMESTAMP 계열은 해보니까 반영되지 않는 것 같다.
- 날짜 추출 함수 EXTRACT
- SELECT EXTRACT(YEAR FROM SYSDATE) FROM DUAL: YEAR 대신 MONTH, DAY, MINUTE, SECOND 등이 올 수 있다.
- 가입 회원 중에서 비수기(2,3,11,12월)에 가입한 회원을 조회하시오
SELECT * FROM MEMBER WHERE EXTRACT(MONTH FROM REGDATE) IN (2,3,11,12)
- 날짜 누적 함수 ADD_MONTHS(날짜, 정수)
- SELECT ADD_MONTHS(SYSDATE, 1) FROM DUAL: 현재 13-JAN-2023일 때 결과는 13-FEB-23이 된다.
- 회원 중 가입한지 6개월이 안 되는 회원을 조회하시오:
SELECT * FROM MEMBER WHERE ADD_MONTHS(SYSDATE,-6)<REGDATE
- 날짜 차이 함수 MONTHS_BETWEEN(날짜, 날짜)
SELECT MONTHS_BETWEEN(SYSDATE, REGDATE) FROM MEMBER
: 결과의 단위는 개월이다. 1.5라면 1.5개월, 0.7이면 한달이 안 되는 정도- 회원 중 가입한지 6개월이 안 되는 회원을 조회하시오:
SELECT * FROM MEMBER MONTHS_BETWEEN(SYSDATE, REGDATE)<6
- 다음 요일을 알려주는 함수 NEXT_DAY(현재날짜, 다음요일)
- SELECT NEXT_DAY(SYSDATE, ‘토’) FROM DUAL: 여기서는 SYSDATE(현재 날짜)를 기준으로 바로 다음 토요일 날짜는 언제인지를 반환해줌
- 월의 마지막 일자를 알려주는 함수 LAST_DAY(날짜)
- SELECT LAST_DAY(SYSDATE) FROM DUAL: 만약 SYSDATE가 2023-1-13이라면 출력은 31-JAN-23이 나온다.
- 지정된 범위에서 날짜를 반올림하는/자르는 함수 ROUND/TRUNC(날짜, 포멧)
- 자세한 건 필요할 때 찾아 보기
27강 - 형식 변환 함수(TO_DATE/TO_CHAR/TO_NUMBER/TO_TIMESTAMP)
- NUMBER형식을 문자열(VARCHAR2)로 변환
SELECT TO_CHAR(123456, '$999,999.99') FROM DUAL;
- 포멧문자
- 9: 숫자
- 0: 빈자리를 채우는 문자
- $: 가격 표시
- ,: 천 단위 구분자 표시
- .: 소수점 표시
- 포멧문자가 앞의 숫자보다 짧으면 올바르게 출력되지 않고, 포멧문자가 더 길면 출력은 되는데 앞에 공백이 생긴다.
- DATE 형식을 문자열(VARCHAR2)로 변환
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD PM HH:MI:SS') FROM DUAL;
- 포멧문자
- YYYY/RRRR/YY/YEAR: 년도표시 - 4자리/Y2K/2자리/영문
- MM/MON/MONTH: 월표시 - 2자리/영문 3자리/영문전체
- DD/DAY/DDTH: 일표시 - 2자리/영문/2자리ST
- AM/PM: 오전/오후 표시
- HH/HH24: 시간표시 - 12시간/24시간
- MI, SS: 분과 초 표시 - 0~59분, 0~59초
- 문자열을 날짜 형식으로 변환
SELECT TO_DATE('1111-12-01') FROM DUAL;
SELECT TO_TIMESTAMP('1111-12-01 12:23:34') FROM DUAL;
- 포멧문자: 두번째 파라미터에 포멧문자가 들어갈 수 있도 있다. 포맷문자는 앞과 동일
- 문자열을 숫자 형식으로 변환
SELECT TO_NUMBER('1111') FROM DUAL;
28강 - NULL 관련 함수(NVL/NVL2/NULLIF)와 DECODE 함수
- 반환 값이 NULL인 경우 대체 값을 제공하는 NVL(NULL,대체값) 함수
SELECT NVL(AGE,0) FROM MEMBER;
- NVL: NULL VALUE. 만약 AGE에 NULL이 들어있을 때를 대비
- NULL 관련 함수 중에서 가장 많이 쓰이는 함수
- NVL에서 조건을 더 확장한 NVL2(입력값, NOT NULL 대체값, NULL 대체값)
SELECT NVL2(AGE,AGE/10,0) FROM MEMBER;
- 두 값이 같은 경우 NULL을 반환하는 NULLIF(값1, 값2)
SELECT NULLIF(AGE,19) FROM MEMBER;
- 조건에 따라 값 선택하기 DECODE(기준값, 비교값, 출력값, 비교값, 출력값, 나머지)
SELECT DECODE(SUBSTR(PHONE,1,3), '010','현재번호', '016', '구번호', '기타') FROM MEMBER;
29강 - SELECT 구절과 정렬(ORDER BY)
- SELECT문 구절: FROM, WHERE, GROUP BY, HAVING, ORDER BY
- 위 구절 순서가 바뀌면 안됨.
- WHERE: 레코드 필터링
- GROUP BY: 집계할 때 그룹핑
- HAVING: 집계된 내용을 필터링할 때
- ORDER BY: 정렬
- 정렬
- 정렬순서: ASC, DESC
- 이름을 기준으로 역순으로 정렬해서 조회:
SELECT * FROM MEMBER ORDER BY NAME DESC;
- DESC을 안 써주면 기본값은 ASC임
- 만약 같은 값의 경우 2차 정렬이 필요할 때:
SELECT * FROM MEMBER ORDER BY NAME DESC, REGDATE DESC;
30강 - 집계 함수와 GROUP BY
- 집계 함수: SUM, MIN, MAN, COUNT, AVG
-
회원별 게시글 수를 조회하시오
- 집계할 때 GROUP BY를 써서, 아래 예시에선 작성자 아이디를 기준으로 아이디 별로 NOTICE 몇 번 썼는지(COUNT(ID)) 집계함. 이 때 ID는 항상 차 있으므로(다른 컬럼의 경우 NULL이 있을 수 있고, 그럼 NULL은 제외하고 집계됨) ID로 COUNT함.
SELECT WRITER_ID, COUNT(ID) COUNT FROM NOTICE GROUP BY WRITER_ID ORDER BY COUNT DESC;
- SELECT 구문 실행 순서
- FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY
- 따라서 만약 SELECT 절에서 별칭을 쓰면, 이는 ORDER BY에서는 쓸 수 있지만, 그 앞의 절에서는 별칭 못 씀
31강 - HAVING 절
-
집계 함수 쓸 수 있는 절
- FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY(SELECT 구문 실행 순서)에서 GROUP BY 이후부터 쓸 수 있음. 즉 WHERE절에서 집계 함수 쓸 수 없음
- 회원별 게시글 수를 조회하시오. 단 게시글이 2 이하인 레코드만 출력하시오.
-- 오류!! SELECT WRITER_ID, COUNT(ID) COUNT FROM NOTICE WHERE COUNT(ID)<=2 GROUP BY WRITER_ID;
-- 올바른 구문 SELECT WRITER_ID, COUNT(ID) COUNT FROM NOTICE GROUP BY WRITER_ID HAVING COUNT(ID)<=2;
32강 - 순위함수(ROW_NUMBER(), RANK(), DENSE_RANK())
- 정렬 후에 일련번호 매기고 싶을 때 ROW_NUMBER()
SELECT ROW_NUMBER() OVER (ORDER BY HIT), TITLE, WRITER_ID, HIT FROM NOTICE;
- 그럼 HIT를 기준으로 정렬된 후 일련번호가 매겨져서 출력됨
- 정렬된 상태에서 등수를 붙이고 싶을 때 RANK(), DENSE_RANK()
SELECT RANK() OVER (ORDER BY HIT DESC), TITLE, WRITER_ID, HIT FROM NOTICE;
- DENSE_RANK()도 RANK() 자리에 그대로 붙이면 됨.
- 차이점: 만약 조회수가 4,4,2인 게시글이 있으면, RANK는 여기에 등수를 매길 때 1,1,3으로 매겨짐. DENSE_RANK는 1,1,2로 매겨짐. 즉 등수가 같을 때 그 다음 등수를 어떻게 표시할 것인가에서 차이.
- 그룹 별로 정렬하고 등수 붙이기
SELECT RANK() OVER (PARTITION BY WRITER_ID ORDER BY HIT DESC), TITLE, WRITER_ID, HIT FROM NOTICE;
33강 - 부조회(서브쿼리)
- 서브쿼리 사용해야 하는 경우: 구절의 순서를 바꿔야 하는 경우
- 최신 등록순으로 정렬한 결과에서 상위 5명을 원할 경우
SELECT * FROM NOTICE ORDER BY REGDATE DESC WHERE ROWNUM BETWEEN 1 AND 5;
: 구절 순서 바뀌어서 오류SELECT * FROM (SELECT * FROM NOTICE ORDER BY REGDATE DESC) WHERE ROWNUM BETWEEN 1 AND 5;
: 해결
- 평균 나이 이상인 회원 조회
SELECT * FROM MEMBER WHERE AGE>=(SELECT AVG(AGE) FROM MEMBER)
- 최신 등록순으로 정렬한 결과에서 상위 5명을 원할 경우
34강 - INNER 조인(JOIN)
- 참조 관계가 있는 여러 테이블을 원래대로 합치는 작업이 조인
- 부모 테이블과 자식 테이블: 일대다 관계
- INNER JOIN
- 참조키를 기준으로 일치하는 행만 조인
- 서로 관계가 있는 레코드들은 INNER, 관계가 없는 레코드들은 OUTER
SELECT * FROM MEMBER INNER JOIN NOTICE ON MEMBER.ID=NOTICE.WRITER_ID;
34강 - INNER 조인(JOIN)
- 참조 관계가 있는 여러 테이블을 원래대로 합치는 작업이 조인
- 부모 테이블과 자식 테이블: 일대다 관계
- INNER JOIN
- 참조키를 기준으로 일치하는 행만 조인
- 서로 관계가 있는 레코드들은 INNER, 관계가 없는 레코드들은 OUTER
SELECT * FROM MEMBER INNER JOIN NOTICE ON MEMBER.ID=NOTICE.WRITER_ID;
35강 - LEFT/RIGHT/FULL 아웃터 조인(OUTER JOIN)
- OUTER JOIN
- 참조키를 기준으로 일치하지 않는 행도 포함시키는 조인
SELECT * FROM MEMBER M LEFT/RIGHT/FULL OUTER JOIN NOTICE N ON M.ID=N.WRITER_ID;
- LEFT JOIN은 왼쪽(쿼리문 기준으로 왼쪽인 MEMBER)의 OUTER를 포함시키겠다. RIGHT는 NOTICE의 OUTER를 포함하겠다. FULL은 양쪽 테이블의 OUTER를 포함시키겠다.
36강 - OUTER JOIN을 이용한 게시글 목록 조회
- INNER JOIN보다 OUTER JOIN을 더 많이 쓴다.
- 필드 이름의 충돌문제
- 게시글의 컬럼명을 식별하기 위해 테이블명 사용:
SELECT NOTICE.ID, NOTICE.NAME, MEMBER.NAME FROM MEMBER INNER JOIN NOTICE ON MEMBER.ID=NOTICE.WRITER_ID;
- 컬럼을 지정하는 식별자를 줄이기 위해서 테이블의 별칭 사용:
SELECT N.ID, N.NAME, M.NAME FROM MEMBER M INNER JOIN NOTICE N ON M.ID=N.WRITER_ID;
- 게시글의 컬럼명을 식별하기 위해 테이블명 사용:
- 회원 별 작성한 게시글 수를 조회하시오
- OUTER JOIN은 주인공을 잡는 것. 예로 LEFT면 LEFT가 주인공이라서 관계없는 것도 전부 출력하기. 그리고 주인공을 기준으로 합치기
SELECT M.ID, M.NAME, COUNT(N.ID) FROM MEMBER M LEFT OUTER JOIN NOTICE N ON M.ID=N.WRITER_ID GROUP BY M.ID, M.NAME;
- OUTER JOIN은 주인공을 잡는 것. 예로 LEFT면 LEFT가 주인공이라서 관계없는 것도 전부 출력하기. 그리고 주인공을 기준으로 합치기
37강 - SELF JOIN
- SELF JOIN
- 데이터가 서로 포함 관계를 가지는 경우
- 한 테이블에서 확장하고자 하는 컬럼이 있는데, 그 컬럼의 데이터는 테이블 내에 있다는 것. 그 때 자기를 참조함.
SELECT M.*, B.NAME BOSS_NAME FROM MEMBER M LEFT OUTER JOIN MEMBER B ON M.BOSS_ID=B.ID;
- SELF JOIN도 은근히 많이 쓰인다.
38강 - 오라클 OLD JOIN
- 오라클 INNER JOIN
- ANSI INNER JOIN
SELECT N.ID, N.TITLE, M.NAME FROM MEMBER M JOIN NOTICE N ON M.ID=N.WRITER_ID WHERE M.ID='newlec';
- ORACLE INNER JOIN
SELECT N.ID, N.TITLE, M.NAME FROM MEMBER M, NOTICE N WHERE M.ID=N.WRITER_ID AND M.ID='newlec';
- ANSI INNER JOIN
- 오라클 OUTER JOIN
- ANSI OUTER JOIN
SELECT N.*, M.NAME WRITER_NAME FROM NOTICE N LEFT OUTER JOIN MEMBER M ON N.WRITER_ID=M.ID;
- ORACLE OUTER JOIN: (+)의 뜻은 해당 테이블에 NULL값을 포함시키겠다는 뜻. 결과는 바로 위 코드와 동일. ORACLE OUTER JOIN은 FULL JOIN을 지원하지 않음.
SELECT N.*, M.NAME WRITER_NAME FROM NOTICE N, MEMBER M WHERE N.WRITER_ID=M.ID(+);
- ANSI OUTER JOIN
39강 - 유니온(UNION)
- UNION: 여러 테이블 레코드를 합치는 것. 컬럼의 개수와 자료형만 맞추면 됨. JOIN처럼 컬럼이 늘어나는 것이 아님.
- UNION 연산자: 만약 UNION했을 때, 두 테이블의 특정 레코드가 완전히 똑같다면 중복은 제거하고 합침
SELECT ID, NAME FROM MEMBER UNION SELECT WRITER_ID, TITLE FROM NOTICE;
- MINUS 연산자: A MINUS B하면, A에 있는 레코드 중 B에 있는 레코드와 동일한 것은 지워짐.
- INTERSECT 연산자: A INTERSECT B하면, A와 B의 공통적인 레코드만 남겨짐
- UNION ALL 연산자: UNION은 두 테이블의 특정 레코드가 완전히 똑같다면 중복은 제거하고 합치지만, UNION ALL은 중복되도 지우지 않고 합침.
- UNION 연산자: 만약 UNION했을 때, 두 테이블의 특정 레코드가 완전히 똑같다면 중복은 제거하고 합침
40강 - View(뷰)의 의미와 생성방법
- 뷰의 의미: 테이블을 넓게 보기도 하고, 좁게 보기도 하고.
- 물리적인 데이터구조(table)과 개념적인 데이터구조(view)의 차이
-
게시글 view 생성하기
- 쿼리 문장을 view로 정의해서 사용하기
-- 정의하기 CREATE VIEW NOTICE_VIEW AS SELECT N.ID, N.TITLE, N.WRITER_ID, M.NAME WRITER_NAME, COUNT(C.ID) CNT FROM MEMBER M RIGHT OUTER JOIN NOTICE N ON M.ID=N.WRITER_ID LEFT OUTER JOIN "COMMENT" C ON N.ID=C.NOTICE_ID GROUP BY N.ID, N.TITLE, N.WRITER_ID, M.NAME; -- 사용하기 SELECT * FROM NOTICE_VIEW;
41강 - 데이터 딕셔너리
- 뷰의 의미
- 한 테이블 내에 일부분만 뷰로 만들면, 특정 컬럼을 숨길 수 있음. 사용자 권한에 따라 테이블 내용을 숨기는 보안 역할.
- 뷰는 조회용. 읽기 전용으로 제공할 때 유용.
- 데이터 딕셔너리
- 데이터 딕셔너리는 뷰의 집합으로 사용자 데이터를 제공해 주고 있음
SELECT * FROM USER_TABLES;
등이 있음- 단 GUI 지원하는 SQL DEVELOPER는 데이터 딕셔너리 정보를 SQL 마우스 클릭만으로 조회할 수 있으므로, 지금은 잘 사용하지 않음.
42강 - 도메인 제약조건
- 도메인 제약조건
- 도메인: 유효한 값의 범위
- 컬럼에 유효한 값의 범위가 들어올 수 있도록(도메인이 아닌 값이 올 수 없도록) 제약 조건을 거는 것이 도메인 제약조건
- NOU NULL: 반드시 입력해야 함
- DEFAULT: 반드시 입력해야 하는데, 사용자가 전달하는 값이 아니라 알아서 값을 가져야 하는 경우(EX- 조회수)
- CHECK: 도메인 범위 체크할 수 있도록 하는 체크 제약 조건
- NOT NULL 제약 조건
- 보통 이런 제약 조건은 GUI 도구로 마우스 몇 번 클릭해서 설정함
- 테이블을 생성할 때 적용
CREATE TABLE TEST( ID VARCHAR2(50) NOT NULL, EMAIL VARCHAR2(200) NULL )
- 테이블 생성한 후 적용:
ALTER TABLE TEST MODIFY EMAIL VARCHAR2(200) NOT NULL;
- DEFAULT 제약 조건
- 보통 이런 제약 조건은 GUI 도구로 마우스 몇 번 클릭해서 설정함
- 이것도 위 NOT NULL처럼 쿼리 알고 싶으면 영상 보거나, 구글링하기.
43강 - 체크 제약조건
- 체크 제약조건
- 입력한 데이터의 형식이 맞는지 안 맞는지 비교 후, 안 맞으면 오류냄
- 테이블 생성할 때 설정
CREATE TABLE TEST( ID VARCHAR2(50) NOT NULL, PHONE VARCHAR2(200) CHECK(PHONE LIKE '010-____-____') NOT NULL )
- 테이블 생성 후 적용:
ALTER TABLE TEST ADD CONSTRAINT CK_TEST_PHONE CHECK(PHONE LIKE '010-%-____');
- CK_TEST_PHONE
- 일반적으로 이름 붙일 때
CK_테이블명_컬럼명
으로 함. - 만약 형식에 안 맞는 데이터를 입력하면 “SQL 오류: 체크 제약조건(HR.CK_TEST_PHONE)이 위배되었습니다”같은 느낌으로 나옴. 이 때 이름 붙인게 쓰임
- 일반적으로 이름 붙일 때
44강 - 정규식을 이용한 체크 제약조건
-
정규식 이용한 체크 제약조건
- 오라클이 제공하는 패턴 연산자만으로는 정밀하게 제약걸기 힘들기 때문에, 정규식 이용
ALTER TABLE MEMBER DROP CONSTRAINT MEMBER_PHONE_CHK1; ALTER TABLE MEMBER ADD CONSTRAINT MEMBER_PHONE_CHK1 CHECK(REGEXP_LIKE(PHONE,'^01[01]-\d{3,4}-\d{4}$'));
45강 - Entity 제약조건(Primary Key, Unique)
- 중복된 레코드가 없도록 제한한다.
- Primary Key(기본키): 어떤 컬럼이 null도 포함되면 안 되고, 중복도 안 될 때
- Unique: 어떤 컬럼이 null은 들어올 수 있지만, 중복은 안 될 때
- 일반적으로는 굳이 아래 SQL문 실행해서 제약조건 달지 않고 도구 사용해서 건다
- 테이블 생성할 때
CREATE TABLE TEST(
ID NUMBER,
WRITER_ID VARCHAR2(50) NOT NULL,
CONSTRAINT TEST_ID_PK PRIMARY KEY(ID),
CONSTRAINT TEST_ID_UK UNIQUE(WRITER_ID)
)
- 테이블 생성한 후
ALTER TABLE TEST
ADD CONSTRAINT TEST_ID_PK PRIMARY KEY(ID);
ALTER TABLE TEST
ADD CONSTRAINT TEST_ID_UK UNIQUE(WRITER_ID);
46강 - 시퀀스(Sequence)
- Sequence는 한국말로 일련번호
- 시퀸스 생성
- “다음으로 시작”, “증분”, “최솟값”: 전부 굳이 지정안해도 1이 기본값
- “주기”: 잘 쓰진 않음. 1부터 MAX에 도달한 다음에, 다시 1부터 시작하길 원할 때