Heroku 배포

개요

  • 카카오톡 챗봇 만들기를 Python + FLASK를 통해 간단한 튜토리얼을 만들어본다.

사전준비

  • OBT 참여승인을 받아야 한다.

기본설정

  • 카카오톡 챗봇 버튼 클릭 후, 봇 이름 생성

    • [봇 만들기] - [카카오톡 챗봇]
  • 카카오톡 채널 연결을 진행한다.

Untitled

  • 바탕화면 폴더 생성 : chatbotnos
  • 깃허브 Repo 생성 : chatbotnos

Untitled

  • heroku app 생성 : chatbotnos

Untitled

나만의 주소 생성됨 : https://chatbotnos.herokuapp.com/

로컬 폴더 생성

  • VSCord에서 c 드라이브 경로에 폴더 생성 : chatbotnos
    • 폴더 우클릭 : git bash here
    • git clone [ 깃허브 repo 주소 ]

가상환경 생성

  • 가상환경 생성

    • pip3 install virtualenv
    • virtualenv venv # 노트북으로 진행. 환경설정에 부족한 부분이 있는지 실패
  • virtualenv 명령 실패 시, 다음 명령으로 대응

    • 가상환경 생성 : python -m venv venv
    • 가상환경 진입 : source venv/Scripts/activate

Untitled

Heroku App 구축

  • 간단하게 app 파일을 만들어 Heroku App URL을 확보해보자.

  • 파일 생성 : chatbotnos/app/main.py

    • mkdir app
    • cd app
    • vi main.py
  • 다음 코드 작성

  • 들여쓰기 주의 !!!!

1
2
3
4
5
6
7
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello, World!'
  • 파일 생성 : chatbotnos/wsgi.py
    • app은 폴더를 말하고, main은 main.py를 말한다.
    • 해당 파일에 코드 작성 : vi wsgi.py
1
2
3
4
from app.main import app

if __name__ == "__main__":
app.run(threaded=True, port=5000)
  • Procfile 파일 작성 (오타 x. profile x)

    • 카카오톡 챗봇에서는 포트번호를 입력을 해줘야 한다.
      • localhost:5000 처럼 명시적으로 입력해주는 것으로 생각하면 된다.
  • 파일 생성 : chatbotnos/Procfile

    • vi Procfile
    • 코드 작성
1
web: gunicorn --bind 0.0.0.0:$PORT wsgi:app
  • 파일 생성 : chatbotnos/runtime.txt
    • vi runtime.txt
1
python-3.8.5

heroku CLI 설치

  • 구글링 : heroku cli
    • install the Heroky CLI

Untitled

  • 설치 진행

Untitled

  • 디폴트 설정으로 설치

Untitled

Heroku login

  • Heroku CLI 설치 후 로그인을 진행해야 한다.
  • cmd에서 실행
1
heroku login
  • press any key : 아무 키나 입력한다

Untitled

  • 로그인 창이 출력된다.

Untitled

  • 로그인 진행

.gitignore 파일 작성

https://www.toptal.com/developers/gitignore

  • 해당 링크에서 키워드 입력하고 ‘생성’
  • 키워드 : virtualenv, vs

Untitled

  • 복사 : ctrl + A
  • .gitignore 파일에 붙여넣어 완성

Untitled

Requirements.txt 파일

  • 다음 코드를 실행하여 생성한다.
  • 하는 김에 gunicorn 설치
    • pip install -r requirements.txt
    • pip install gunicorn
    • pip freeze > requirements.txt

Heroku 배포

  • Heroku Project 생성

    • 다른 사용자들과 이름 다르게 진행! = 유일값
    • 언더바 사용불가 : name_name (x)
    • 로컬 폴더명 == Github Repo == Heroku Project 이름
  • 위 코드에서 이미 있는 이름이라 뜨면 다음 코드 실행

  • 기존 Existing App과 연동하려면 배포 전 아래 코드를 선 실행 후, 배포를 진행한다.

    • heroku git:remote -a chatbotnos
1
git init
  • heroku 배포 메뉴얼의 코드
1
2
$ heroku git:clone -a chatbotnos 
$ cd chatbotnos
  • Heroku 배포
    • Heroku에 배포하기 위해서는 크게 아래 코드만 기억한다.
1
2
3
4
$ git add .
$ git commit -am "your_message" # 이 부분만 자유롭게 쓸 수 있다.
$ git push origin main ## Github Repository에 업데이트
$ git push heroku main ## Heroku 코드 배포
  • 배포 완료

Untitled

로그 확인

Kakao 스킬서버

챗봇에 다음과 같은 연산을 도입하게 위한 기능이다

5*10은 무엇인가요?

  • 사칙연산

  • number01 operator number02

  • def cal(number1, number2)

         number 1 * number 2
    

스킬의 역할

  1. 바로 동작에서 출력으로 이동함으로써 달성 불가능한 것을 스킬을 거쳐서 가능케 함
  2. [발화입력 → 동작 → 스킬 → 출력]

Untitled

  • 웰컴 블록(Welcome Block)

웰컴 블록은 사용자가 봇을 처음 사용할 때 받게되는 웰컴메시지를 설정하는 블록.

웰컴블록’이 ‘사용중’일 경우 사용자가 봇을 처음 사용할 때 웰컴블록에 설정된 응답이 전송되고, ‘미사용’일 경우 응답을 설정하는 부분이 비활성화 되고 OFF로 표기.

  • 폴백 블록(Fallback Block)

폴백 블록은 사용자의 발화 의도가 어떠한 블록과도 매칭이 되지 않을 때 (=봇이 사용자의 발화 의도를 이해하지 못할 때)의 응답을 설정하는 블록.

  • 탈출 블록(Exit Block)

탈출 블록은 봇의 되묻기 상황에서 사용자가 대화를 초기화하거나 탈출하고 싶을때 쓰는 사용자 명령어를 정의하는 블록.

스킬 처리 과정

  • 봇 시스템 = 주문 블록
  • 사용자 발화 : ‘피자 주문 할께요’
  • 주문 블록 — [스킬 호출] —> 스킬 서버
  • 주문 블록 <— [스킬 응답] — 스킬 서버
  • 챗봇 답변 : ‘피자 주문 완료’

Untitled

서버와 DB 구조

  • 서버가 2개 필요
  • 폭백 블록 → ←챗봇 API 서버 ↔ 챗봇 엔진 서버 ↔ 학습 DB

Untitled

스킬 사용

  • 카카오 챗봇 → 스킬 → 생성
  • 단, 서버 주소가 필요하다.

Untitled

  • 주소 끝에 다음과 같은 형식으로 입력

    • 단순 테스트 응답 스킬 주소 끝에 입력 : .com/api/sayHello
    • 단순 계산기 텍스트 응답 스킬 주소 끝에 입력 : .com/api/calCulator
  • body = request.get_json() # 사용자가 입력한 데이터

  • responseBody 는 일종의 템플릿. 이 틀에서 응답 형식으로 작성한다.

  • simpletext

스킬 사용 코드 예시 : SimpleImage

간단한 이미지형 출력 요소입니다. 이미지 링크 주소를 포함하면 이를 스크랩하여 사용자에게 전달합니다. 이미지 링크 주소가 유효하지 않을 수 있기 때문에, 대체 텍스트를 꼭 포함해야 합니다.

상세 필드

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"version": "2.0",
"template": {
"outputs": [
{
"simpleImage": {
"imageUrl": "http://k.kakaocdn.net/dn/83BvP/bl20duRC1Q1/lj3JUcmrzC53YIjNDkqbWK/i_6piz1p.jpg",
"altText": "보물상자입니다"
}
}
]
}
}

엔티티 설정

  • 엔티티 → 나의 엔티티 관리 → (엔티티 생성)

Untitled

  • 이 엔티티와 동일한 이름으로 변수를 생성하고 코드 작성

  • def clas(opt_operator, number01, number02) : # 메인로직!!!

  • sys.number 사용 가능한지 체크

Untitled

  • 파라미터 설정

Untitled

  • 사용자 발화 : 다음과 같이 발화에서 엔티티를 설정

Untitled

  • 로그값 확인이 가능한 것을 사용 : aws, heroku, 구름IDE 중에서 선택

heroku project 생성 시 주의

  • 이름 다르게 생성! = 유일값
  • heroku-kakap-chatbot은 사용 불가
  • 언더바 _ 사용불가
  • 다음 3가지를 같은 이름으로 생성
    • 로컬 폴더명 == Github Repo == Heroku Project 이름

Heroku 가입

Heroku란?

헤로쿠(Heroku)는 간단히 git을 통해 무료로 웹 사이트를 호스팅할 수 있게 해주는 서비스다. 무료인 이유는 해당 도메인으로 몇 시간 동안 요청이 없으면 수면상태로 전환해 초기 접속이 늦기 때문이다.그래서 개인 블로그 서버나 포트폴리오 결과물 배포 용으로 가볍게 활용하기 좋은 호스팅 플랫폼이다. 현재 공식적으로 Node.js, Ruby, Java, PHP, Python, Go, Scala, Cjojure 언어를 지원한다.

Heroku 배포 준비

  1. 배포를 위한 소스코드 파일이 있어야할 것!

  2. 헤로쿠 배포작업은 Git으로 이루어지니 Git이 설치되어 있을 것! (깃 설치) (깃 설치 도움말 포스팅)

  3. 헤로쿠 계정 생성

  • 깃 설치 및 환경 설정은 “깃 설치” 글에 걸어둔 사이트에서 설치파일을 받아 설치하면 되고 이를포함한 환경설정은 옆 “깃 설치 도움말 포스팅” 글을 참고하길 바란다.

Heroku 회원가입

  • 구글링 : heroku
  • 회원가입

https://www.heroku.com/

Untitled

  • 정보를 입력하고 회원가입 진행

Untitled

  • e-mail 에서 회원가입 메일을 수신
  • 메일의 링크를 이용

Untitled

  • 비밀번호 설정

Untitled

  • 가입 완료

Untitled

  • 로그인하면 다음 대시보드가 나타난다

Untitled

Reference

https://blog.naver.com/dsz08082/221952990439?isInf=true

Kakao Chatbot open builder

챗봇 제작

챗봇 설정

  • 프로필 설정 : 모든 사항에 관해 공개로 전환

Untitled

  • 운영 채널 연결 : ‘tutorial’ 챗봇 ↔ ‘휴먼주식도우미챗봇’ 채널
    • 배포만 하면 카카오톡에서 검색 가능하다.

Untitled

  • 배포
    • 첫 배포 : 이제 카카오톡에서 검색이 가능하며 사용이 가능해진다.
    • 그 이후의 배포 : 작성한 블록 시나리오 등이 실제 카카오톡에 반영된다.

Untitled

  • 관리자 초대
    • 조원들과 챗봇을 공동으로 관리하기 위해 설정

Untitled

시나리오 작성

  • 앞서 제작된 시나리오 구상안을 토대로 챗봇의 시나리오, 블록 작성

  • 시나리오 생성

  • 주요 기능을 기반으로 시나리오 생성

Untitled

  • 0 메뉴

    • 주요 기능을 모두 사용할 수 있는 메뉴를 호출할 시나리오를 의도하고 생성
  • 1 주식이 처음이신가요?

    • 주식 초심자를 위해 ‘계정 생성 방법’과 ‘주식 거래하는 방법’ 등을 설명하기 위한 시나리오
  • 2 주식 단어

    • 입문자를 위한 간단한 주식 용어를 설명하고 더 많은 용어를 공부하기 원하는 사용자를 위한 추가적으로 자료를 담을 예정
  • 3 종목별 시세를 알려드릴까요

    • 종목 시세를 확인 가능하도록 크롤링한 데이터를 출력할 시나리오를 계획
  • 4 현재 인기 많은 종목 알려드릴까요?

    • 처음 주식하는 사람들에게 주식을 추천하기 위한 기능을 넣을 시나리오
  • 5 꼭 알아야 할 이슈

    • 초심자가 현재의 주식 관련 흐름을 파악하는데 도움을 줄 정보를 알려줘야 할 시나리오 생성

블록 작성

  • 시나리오 아래에 구상안을 구현할 블록들을 작성

  • 블록명을 넘버링하여 상위 하위를 구분 가능하게 함.

  • 상위에서 하위로 넘어갈 수 있게 제작

    • 예를 들어, 1-1 블록에서 1-1-1 블록이나 1-1-2 블록으로 이동 가능
  • 거의 모든 블록에 돌아가기 기능 탑재. 반대로 하위에서 상위로 이동하도록 구현

  • 0 메뉴얼 블록

    • 0 메뉴얼 : 주요 기능 사용과 개선사항 전달이 가능한 메뉴를 출력하는 블록

Untitled

  • 1 주식이 처음이신가요 블록
    • 다음 두 가지 기능 중 하나를 선택하고 호출하는 블록
    • 1-1 계정 만드는 법 알아보기
      • 1-1-1 오프라인으로 계좌 개설하기
      • 1-1-2 온라인으로 계좌 개설하기
    • 1-2 기초 상식 알아보기
      • 1-2-1 주식은 이렇게 거래합니다
      • 1-2-2 주식 시장의 결제 방식
      • 1-2-3 증권사 수수료, 증권거래세

Untitled

  • 2 주식 단어 목록 블록
    • 간단한 단어와 그 뜻이 담긴 여러 블록과 연결
    • 더 많은 용어를 알고 싶은 초심자를 위한 자료의 링크가 연결된 버튼을 제작

Untitled

  • 3 종목별 시세 확인 블록
    • 종목 시세를 확인 가능하도록 크롤링한 데이터를 출력할 예정
    • 임시로 시가총액 확인 가능한 링크를 연결

Untitled

  • 4 최근 인기 종목 알려드립니다 블록
    • 거래상위, 상승주, 하락주를 확인 가능한 링크를 연결
    • 매수상위, 매도상위를 기반으로 인기 많은 종목을 출력할 예정
    • 임시로 매수상위, 매도상위 확인 가능한 링크를 연결

Untitled

  • 5 꼭 알아야할 이슈,뉴스 블록
    • 주식 관련 뉴스를 확인할 수 있게 설정
    • 매일 뉴스를 찾아서 확인하기 번거로운 사용자들을 위한 관련 기능 추천

Untitled

제네릭 메뉴 설정

  • +시나리오 밑에 ‘시나리오 설정’ 에서 설정
  • 주요 기능과 같은 이름으로 제네릭 메뉴 생성
  • 각 제네릭 메뉴를 주요 기능을 사용할 수 있는 블록과 연결한다.

Untitled

배포 : 챗봇 시나리오 반영 여부 확인

  • 배포 후 카카오톡에서 의도대로 기능이 작동하는 시험

Untitled

Untitled

Untitled

Untitled

피드백 받습니다.

  • 카카오톡 검색 : 휴먼주식도우미챗봇

Untitled

  • 개선사항 전달 or 상담직원 연결

Untitled

Untitled

  • 개선사항을 적어주세요

Untitled

  • 채널에서 확인하고 개선

Untitled

Kakao Chatbot Start

카카오 챗봇

카카오톡 오픈 빌더 챗봇 만들기 1편 - Data Science | DSChloe

개요

  • 카카오톡 오픈 빌더 챗봇을 만드는 과정을 보여준다.
  • 회원가입이 필요할 수 있다.
  • 카카오톡 채널을 만든다.

등록하기

  • 구글 검색창에서 카카오톡 오픈 빌더를 검색한다.

Untitled

  • 로그인

Untitled

  • 챗봇 관리자센터 OBT 참여 신청 메뉴가 나오는지 확인한다.
  • 신청 메뉴가 나오면 참여 신청

카카오톡 채널 관리자 가입

  • 카카오톡 관리자 채널에서 새로운 채널을 만든다.

Untitled

  • 채널을 개설한다.
    • 프로필 사진, 소개글은 추후에 작성이 가능하다.

Untitled

  • 개설이 완료되었다.

Untitled

  • 채널로 이동한다

Untitled

Untitled

  • 챗봇 관리자센터 OBT 참여를 신청한다.

• 대시보드로 화면이 넘어가면 이제 챗봇 서비스를 시작하면 된다.

Untitled

카카오계정 (kakao.com)

챗봇 제작

Heroku 가입

스킬 서버 이론

heroku 배포

스킬 사용

스킬 사용 : 구름 IDE

크롤링 기초

PL/SQL - ML

사전준비

  • 두 파일을 다운로드

hr.csv

pl_sql_ml_modeling.sql

  • SQL Developer에서 데이터 임포트한다.

    • hr.csv를 임포트해야 한다.
  • 새 SQL 워크시트에서 pl_sql_ml_modeling.sql 내용을 넣는다.

    • 메모장으로 열어서 내용을 복사하고 워크시트에 붙이면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
-- 의사 결정 트리 모델로 퇴사자 예측 모델 만들기
-- 데이터셋 : https://www.kaggle.com/datasets/pankeshpatel/hrcommasep
-- 기존에 있을지도 모를 HR 데이터 삭제
-- 퇴사 여부 예측 (LEFT)

SELECT COUNT(*) FROM HR_DATA_MAIN;
-- 14999

-- 훈련 데이터와 테스트 데이터로 분리
DROP TABLE HR_DATA_TRAINING;
CREATE TABLE HR_DATA_TRAINING
AS
SELECT *
FROM HR_DATA_MAIN
WHERE EMP_ID < 10500;

DROP TABLE HR_DATA_TEST;

CREATE TABLE HR_DATA_TEST
AS
SELECT *
FROM HR_DATA_MAIN
WHERE EMP_ID >= 10500;

-- 머신 러닝 모델의 환경설정을 위한 정보가 들어있는 테이블을 생성합니다.
-- URL : https://docs.oracle.com/database/121/ARPLS/d_datmin.htm#ARPLS192
DROP TABLE DTSETTINGS;
CREATE TABLE DTSETTINGS
AS
SELECT *
FROM TABLE (DBMS_DATA_MINING.GET_DEFAULT_SETTINGS)
WHERE SETTING_NAME LIKE '%GLM%';

BEGIN
INSERT INTO DTSETTINGS
VALUES ('ALGO_NAME', 'ALGO_DECISION_TREE');

INSERT INTO DTSETTINGS
VALUES (DBMS_DATA_MINING.TREE_IMPURITY_METRIC, 'TREE_IMPURITY_ENTROPY'); -- 모델의 핵심엔진은 엔트로피로 설정
COMMIT;
END;
/

-- 머신 러닝 모델을 생성합니다.
BEGIN
DBMS_DATA_MINING.DROP_MODEL('DT_MODEL');
END;
/

BEGIN
DBMS_DATA_MINING.CREATE_MODEL (
MODEL_NAME => 'DT_MODEL',
MINING_FUNCTION => DBMS_DATA_MINING.CLASSIFICATION,
DATA_TABLE_NAME => 'HR_DATA_TRAINING',
CASE_ID_COLUMN_NAME => 'EMP_ID',
TARGET_COLUMN_NAME => 'LEFT',
SETTINGS_TABLE_NAME => 'DTSETTINGS');
END;
/

-- 5. 생성된 모델을 확인합니다.
SELECT MODEL_NAME,
ALGORITHM,
MINING_FUNCTION
FROM ALL_MINING_MODELS
WHERE MODEL_NAME = 'DT_MODEL';

-- 6. 생성된 모델의 환경설정 내용을 확인합니다.
SELECT SETTING_NAME, SETTING_VALUE
FROM ALL_MINING_MODEL_SETTINGS
WHERE MODEL_NAME = 'DT_MODEL';

-- 7. 실제 값과 예측 값과 예측 확률을 출력합니다.
SELECT EMP_ID, T.LEFT 실제값,
PREDICTION (DT_MODEL USING *) 예측값,
PREDICTION_PROBABILITY (DT_MODEL USING *) "모델이 예측한 확률"
FROM HR_DATA_TEST T;

-- 8. 학습한 머신러닝 모델의 성능을 확인합니다.
DROP TABLE HR_DATA_TEST_MATRIX_2;
CREATE OR REPLACE VIEW VIEW_HR_DATA_TEST
AS
SELECT EMP_ID, PREDICTION(DT_MODEL USING *) PREDICTED_VALUE,
PREDICTION_PROBABILITY(DT_MODEL USING * ) PROBABILITY
FROM HR_DATA_TEST;

SET SERVEROUTPUT ON

DECLARE
V_ACCURACY NUMBER;
BEGIN
DBMS_DATA_MINING.COMPUTE_CONFUSION_MATRIX (
ACCURACY => V_ACCURACY,
APPLY_RESULT_TABLE_NAME => 'VIEW_HR_DATA_TEST',
TARGET_TABLE_NAME => 'HR_DATA_TEST',
CASE_ID_COLUMN_NAME => 'EMP_ID',
TARGET_COLUMN_NAME => 'LEFT',
CONFUSION_MATRIX_TABLE_NAME => 'HR_DATA_TEST_MATRIX_2',
SCORE_COLUMN_NAME => 'PREDICTED_VALUE',
SCORE_CRITERION_COLUMN_NAME => 'PROBABILITY',
COST_MATRIX_TABLE_NAME => NULL,
APPLY_RESULT_SCHEMA_NAME => NULL,
TARGET_SCHEMA_NAME => NULL,
COST_MATRIX_SCHEMA_NAME => NULL,
SCORE_CRITERION_TYPE => 'PROBABILITY');
DBMS_OUTPUT.PUT_LINE('**** MODEL ACCURACY ****: ' || ROUND(V_ACCURACY,4));
END;
/
  • Reference : 오라클 SQL과 PL/SQL을 다루는 기술

PL/SQL practice02

  • PL/SQL은 일반 프로그래밍 언어에서 제공하는 많은 기능이 탑재되어 있다.
  • 다른 프로그래밍 언어와 다른 점은 PL/SQL은 DB에 직접 탑재되어 컴파일되고 실행되어 성능 면에서도 우수하고, DB 관련 처리를 할 때 수많은 기능을 제공한다

프로시저

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 프로시저
-- 함수와 다르게 로직 처리만 수행 / 결괏값 반환 (X)
-- p.290
SELECT * FROM JOBS;

-- 프로시저 생성
CREATE OR REPLACE PROCEDURE my_new_job_proc
(p_job_id IN JOBS.JOB_ID%TYPE
, p_job_title IN JOBS.JOB_TITLE%TYPE
, p_min_sal IN JOBS.MIN_SALARY%TYPE
, p_max_sal IN JOBS.MAX_SALARY%TYPE
)
IS
BEGIN
INSERT INTO JOBS(job_id
, job_title
, min_salary
, max_salary
, create_date
, update_date)
VALUES(p_job_id, p_job_title, p_min_sal, p_max_sal, SYSDATE, SYSDATE);

COMMIT;
END;

프로시저 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
-- 프로시저 실행
-- exec
EXEC my_new_job_proc ('SM_JOB1', 'Sample JOB1', 1000, 5000);

-- 확인
SELECT *
FROM jobs
WHERE job_id = 'SM_JOB1';

-- 프로시저 업데이트
-- 끊기면 안되고, 계속 실행이 되어야 함

CREATE OR REPLACE PROCEDURE my_new_job_proc
(p_job_id IN JOBS.JOB_ID%TYPE
, p_job_title IN JOBS.JOB_TITLE%TYPE
, p_min_sal IN JOBS.MIN_SALARY%TYPE
, p_max_sal IN JOBS.MAX_SALARY%TYPE
)
IS
vn_cnt NUMBER := 0;
BEGIN

-- 동일한 JOB_ID가 있는지 체크
SELECT COUNT(*)
INTO vn_cnt
FROM JOBS
WHERE job_id = p_job_id;

-- 없으면 INSERT
IF vn_cnt = 0 THEN
INSERT INTO JOBS(job_id
, job_title
, min_salary
, max_salary
, create_date
, update_date)
VALUES(p_job_id, p_job_title, p_min_sal, p_max_sal, SYSDATE, SYSDATE);
ELSE -- 있으면 UPDATE
UPDATE JOBS
SET job_title = p_job_title
, min_salary = p_min_sal
, max_salary = p_max_sal
, update_date = SYSDATE
WHERE job_id = p_job_id;
END IF;
COMMIT;
END;

프로시저 실행

1
2
3
4
5
6
7
-- 프로시저 실행
-- exec
EXEC my_new_job_proc ('SM_JOB1', 'Sample JOB1', 1000, 5000);

SELECT *
FROM jobs
WHERE job_id = 'SM_JOB1';
1
2
3
-- p.293
-- 한줄로 실행. (이유 모름)
EXECUTE my_new_job_proc (p_job_id => 'SM_JOB1', p_job_title => 'Sample JOB1', p_min_sal => 2000, p_max_sal => 7000);
1
SELECT * FROM jobs WHERE job_id = 'SM_JOB1';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
-- 매개변수 디폴트 설정
CREATE OR REPLACE PROCEDURE my_new_job_proc
(p_job_id IN JOBS.JOB_ID%TYPE
, p_job_title IN JOBS.JOB_TITLE%TYPE
, p_min_sal IN JOBS.MIN_SALARY%TYPE := 10
, p_max_sal IN JOBS.MAX_SALARY%TYPE := 100
)
IS
vn_cnt NUMBER := 0;
BEGIN

-- 동일한 JOB_ID가 있는지 체크
SELECT COUNT(*)
INTO vn_cnt
FROM JOBS
WHERE job_id = p_job_id;

-- 없으면 INSERT
IF vn_cnt = 0 THEN
INSERT INTO JOBS(job_id
, job_title
, min_salary
, max_salary
, create_date
, update_date)
VALUES(p_job_id, p_job_title, p_min_sal, p_max_sal, SYSDATE, SYSDATE);
ELSE -- 있으면 UPDATE
UPDATE JOBS
SET job_title = p_job_title
, min_salary = p_min_sal
, max_salary = p_max_sal
, update_date = SYSDATE
WHERE job_id = p_job_id;
END IF;
COMMIT;
END;
1
2
EXECUTE my_new_job_proc('SM_JOB1', 'Sample JOB1');
SELECT * FROM jobs WHERE job_id = 'SM_JOB1';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
-- OUT, IN OUT 매개변수
CREATE OR REPLACE PROCEDURE my_new_job_proc
(p_job_id IN JOBS.JOB_ID%TYPE
, p_job_title IN JOBS.JOB_TITLE%TYPE
, p_min_sal IN JOBS.MIN_SALARY%TYPE := 10
, p_max_sal IN JOBS.MAX_SALARY%TYPE := 100
, p_upd_date OUT JOBS.UPDATE_DATE%TYPE -- 갱신일자 값을 반환한다!
)
IS
vn_cnt NUMBER := 0;
vn_cur_date JOBS.UPDATE_DATE%TYPE := SYSDATE;
BEGIN

-- 동일한 JOB_ID가 있는지 체크
SELECT COUNT(*)
INTO vn_cnt
FROM JOBS
WHERE job_id = p_job_id;

-- 없으면 INSERT
IF vn_cnt = 0 THEN
INSERT INTO JOBS(job_id
, job_title
, min_salary
, max_salary
, create_date
, update_date)
VALUES(p_job_id, p_job_title, p_min_sal, p_max_sal, SYSDATE, SYSDATE);
ELSE -- 있으면 UPDATE
UPDATE JOBS
SET job_title = p_job_title
, min_salary = p_min_sal
, max_salary = p_max_sal
, update_date = SYSDATE
WHERE job_id = p_job_id;
END IF;
COMMIT;
END;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
-- OUT, IN OUT 매개변수
CREATE OR REPLACE PROCEDURE my_new_job_proc
(p_job_id IN JOBS.JOB_ID%TYPE
, p_job_title IN JOBS.JOB_TITLE%TYPE
, p_min_sal IN JOBS.MIN_SALARY%TYPE := 10
, p_max_sal IN JOBS.MAX_SALARY%TYPE := 100
, p_upd_date OUT JOBS.UPDATE_DATE%TYPE -- 갱신일자 값을 반환한다!
)
IS
vn_cnt NUMBER := 0;
vn_cur_date JOBS.UPDATE_DATE%TYPE := SYSDATE;
BEGIN

-- 동일한 JOB_ID가 있는지 체크
SELECT COUNT(*)
INTO vn_cnt
FROM JOBS
WHERE job_id = p_job_id;

-- 없으면 INSERT
IF vn_cnt = 0 THEN
INSERT INTO JOBS(job_id
, job_title
, min_salary
, max_salary
, create_date
, update_date)
VALUES(p_job_id, p_job_title, p_min_sal, p_max_sal, vn_cur_date, vn_cur_date);
ELSE -- 있으면 UPDATE
UPDATE JOBS
SET job_title = p_job_title
, min_salary = p_min_sal
, max_salary = p_max_sal
, update_date = vn_cur_date
WHERE job_id = p_job_id;
END IF;

-- OUT 매개변수에 갱신 일자 할당
p_upd_date := vn_cur_date;
COMMIT;
END;

SET SERVEROUTPUT ON
DECLARE
vd_cur_date JOBS.UPDATE_DATE%TYPE;
BEGIN
my_new_job_proc('SM_JOB1', 'Sample JOB1', 2000, 6000, vd_cur_date);
DBMS_OUTPUT.PUT_LINE(vd_cur_date);
END;
  • Reference : 오라클 SQL과 PL/SQL을 다루는 기술

PL/SQL practice01

  • PL/SQL은 일반 프로그래밍 언어에서 제공하는 많은 기능이 탑재되어 있다.
  • 다른 프로그래밍 언어와 다른 점은 PL/SQL은 DB에 직접 탑재되어 컴파일되고 실행되어 성능 면에서도 우수하고, DB 관련 처리를 할 때 수많은 기능을 제공한다

IF문

1
2
3
4
5
6
7
8
9
10
11
12
13
-- IF문

SET SERVEROUTPUT ON
DECLARE
vn_num1 NUMBER := 1;
vn_num2 NUMBER := 2;
BEGIN
IF vn_num1 >= vn_num2 THEN
DBMS_OUTPUT.PUT_LINE(vn_num1 || '이 큰 수');
ELSE
DBMS_OUTPUT.PUT_LINE(vn_num2 || '이 큰 수');
END IF;
END;

실행)

Untitled

ELSE IF문

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
DECLARE 
vn_salary NUMBER := 0; -- 변수 초기화
vn_department_id NUMBER := 0; -- 변수 초기화
BEGIN
vn_department_id := ROUND(DBMS_RANDOM.VALUE (10, 120), -1);
SELECT salary
INTO vn_salary
FROM employees
WHERE department_id = vn_department_id
AND ROWNUM = 1;

DBMS_OUTPUT.PUT_LINE(vn_salary);

IF vn_salary BETWEEN 1 AND 3000 THEN
DBMS_OUTPUT.PUT_LINE('낮음');
ELSIF vn_salary BETWEEN 3001 AND 6000 THEN
DBMS_OUTPUT.PUT_LINE('중간');
ELSIF vn_salary BETWEEN 6001 AND 10000 THEN
DBMS_OUTPUT.PUT_LINE('높음');
ELSE
DBMS_OUTPUT.PUT_LINE('최상위');
END IF;
END;

중첩 IF문

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- 중첩 IF문
DECLARE
vn_salary NUMBER := 0;
vn_department_id NUMBER := 0;
vn_commission NUMBER := 0;
BEGIN
vn_department_id := ROUND(DBMS_RANDOM.VALUE (10,120), -1);
SELECT salary, commission_pct
INTO vn_salary, vn_commission
FROM employees
WHERE department_id = vn_department_id
AND ROWNUM = 1;
DBMS_OUTPUT.PUT_LINE(vn_salary);

IF vn_commission > 0 THEN
IF vn_commission > 0.15 THEN
DBMS_OUTPUT.PUT_LINE(vn_salary * vn_commission);
END IF;
ELSE
DBMS_OUTPUT.PUT_LINE(vn_salary);
END IF;
END;

CASE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- CASE (p.277)
DECLARE
vn_salary NUMBER := 0; -- 변수 초기화
vn_department_id NUMBER := 0; -- 변수 초기화
BEGIN
vn_department_id := ROUND(DBMS_RANDOM.VALUE (10, 120), -1);
SELECT salary
INTO vn_salary
FROM employees
WHERE department_id = vn_department_id
AND ROWNUM = 1;

DBMS_OUTPUT.PUT_LINE(vn_salary);

CASE WHEN vn_salary BETWEEN 1 AND 3000 THEN
DBMS_OUTPUT.PUT_LINE('낮음');
WHEN vn_salary BETWEEN 3001 AND 6000 THEN
DBMS_OUTPUT.PUT_LINE('낮음');
WHEN vn_salary BETWEEN 6001 AND 10000 THEN
DBMS_OUTPUT.PUT_LINE('높음');
ELSE
DBMS_OUTPUT.PUT_LINE('최상위');
END CASE;
END;

LOOP 문

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- LOOP
-- 3단
DECLARE
vn_base_num NUMBER := 3;
vn_cnt NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(vn_base_num || '*' || vn_cnt || '= '
|| vn_base_num * vn_cnt);
vn_cnt := vn_cnt + 1;

EXIT WHEN vn_cnt > 9; -- vn_cnt가 9보다 크면 루프 종료
END LOOP;
END;

WHILE 문

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- WHILE 
DECLARE
vn_base_num NUMBER := 3;
vn_cnt NUMBER := 1;
BEGIN
-- 조건식이 참일 때만 실행되는 것
-- 조건식이 거짓이 되면 실행 중지
WHILE vn_cnt <= 9 -- vn_cnt가 9보다 크면 루프 종료
LOOP
DBMS_OUTPUT.PUT_LINE(vn_base_num || '*' || vn_cnt || '= '
|| vn_base_num * vn_cnt);
vn_cnt := vn_cnt + 1;
END LOOP;
END;

WHILE (EXIT)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- WHILE (EXIT)
-- WHILE
DECLARE
vn_base_num NUMBER := 3;
vn_cnt NUMBER := 1;
BEGIN
-- 조건식이 참일 때만 실행되는 것
-- 조건식이 거짓이 되면 실행 중지
WHILE vn_cnt <= 9 -- vn_cnt가 9보다 크면 루프 종료
LOOP
DBMS_OUTPUT.PUT_LINE(vn_base_num || '*' || vn_cnt || '= '
|| vn_base_num * vn_cnt);

-- Break (= EXIT)
EXIT WHEN vn_cnt = 5; -- vn_cnt값이 5가 되면 루프 종료
vn_cnt := vn_cnt + 1;
END LOOP;
END;

FOR-LOOP

1
2
3
4
5
6
7
8
9
10
-- FOR-LOOP 
-- 인덱스값
DECLARE
vn_base_num NUMBER := 3;
BEGIN
FOR i IN 1..9
LOOP
DBMS_OUTPUT.PUT_LINE (vn_base_num || '*' || i || '= ' || vn_base_num * i);
END LOOP;
END;

CONTINUE

1
2
3
4
5
6
7
8
9
10
-- CONTINUE
DECLARE
vn_base_num NUMBER := 3;
BEGIN
FOR i IN 1..9
LOOP
CONTINUE WHEN i = 5;
DBMS_OUTPUT.PUT_LINE (vn_base_num || '*' || i || '= ' || vn_base_num * i);
END LOOP;
END;

GOTO 문

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- GOTO문
-- 사용자 정의 함수와 약간 비슷

DECLARE
vn_base_num NUMBER := 3;
BEGIN
<<third>>
FOR i IN 1..9
LOOP
DBMS_OUTPUT.PUT_LINE (vn_base_num || '*' || i || '= ' || vn_base_num * i);
IF i = 3 THEN
GOTO fourth;
END IF;
END LOOP;

<<fourth>>
vn_base_num := 4;
FOR i IN 1..9
LOOP
DBMS_OUTPUT.PUT_LINE (vn_base_num || '*' || i || '= ' || vn_base_num * i);
END LOOP;
END;

-- NULL문
-- 검색 찾기 링크 참조
  • Reference : 오라클 SQL과 PL/SQL을 다루는 기술

PL/SQL start

PL/SQL

  • SQL은 비절차적 언어
    • 파이썬, R, 자바 등과는 다르다
  • ‘DB 프로그래밍’이라 하면 SQL을 사용한 DML문을 사용하는 것을 지칭할 수도 있지만, 실제 복잡한 비즈니스 로직을 만드는 것은 PL/SQL을 사용해 구현하는 것이 보통이다.
  • PL/SQL은 일반 프로그래밍 언어에서 제공하는 많은 기능이 탑재되어 있다.
  • 다른 프로그래밍 언어와 다른 점은 PL/SQL은 DB에 직접 탑재되어 컴파일되고 실행되어 성능 면에서도 우수하고, DB 관련 처리를 할 때 수많은 기능을 제공한다.

둘째 마당

  • 복잡한 비즈니스 로직을 처리하는 —> 프로그래밍

PL/SQL 기본 구조

  • PL/SQL 소스 프로그램의 기본 단위를 블록(Block)이라고 하는데, 블록은 선언부, 실행부, 예외 처리부로 구성된다.

  • 이 블록은 다시 이름이 없는 블록과 이름이 있는 블록으로 구분할 수 있는데 전자에 속하는 것이 익명 블록이며, 함수, 프로시저, 패키지 등이 후자에 속한다.

  • 오라클 SQL과 PL/SQL을 다루는 기술 260p

  • 이름이 없는 익명 블록anonymous block을 사용해 PL/SQL 코드를 작성해 보자.

  • 먼저 변수를 선언하고 값을 할당해 이 값을 출력하는 익명 블록을 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
-- PL/SQL
-- 블록 단위로 진행
-- 선언부
SET SERVEROUTPUT ON
SET TIMING ON -- 경과시간 확인
DECLARE
vi_num NUMBER; -- 변수 선언
BEGIN
-- 실행 (코드 실행)
vi_num := 100;
DBMS_OUTPUT.PUT_LINE(vi_num);
END; -- 블록 종료
-- / PL/SQL 자체가 종료
  • 만약 현재 로그온 한 SQLPlus를 종료하고 다시 접속한다면 “SET SERVEROUTPUT ON”이란 명령어를 다시 실행해야 출력 결과를 볼 수 있다.
  • 참고로 SQLPlus 상에서 PL/SQL 블록을 실행했을 때 총 소요시간도 알 수 있는데, 이를 위해서는 SET TIMING ON 명령어를 실행하면 된다.

실행)

Untitled

상수

  • 상수는 변수와는 달리 한 번 값을 할당하면 변하지 않는다
  • 상수명 CONSTANT 데이터타빙 : = 상수값;
1
2
3
4
5
6
-- p.264
DECLARE
a INTEGER := 2**2*3**2; -- 4 * 9
BEGIN
DBMS_OUTPUT.PUT_LINE('a = ' || TO_CHAR(a));
END;

실행)

Untitled

DML문

  • SQL문 중 DDL은 PL/SQL 상에서 직접 쓸 수 없고 DML문만 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- DM문
-- PL/SQL 테이블과 연동해서 특정 로직을 처리하는 것

DECLARE
vs_emp_name VARCHAR2(80); -- 사원명 변수
vs_dep_name VARCHAR2(80); -- 부원명 변수
BEGIN
SELECT a.emp_name, b.department_name
INTO vs_emp_name, vs_dep_name
FROM employees a
, departments b
WHERE a.department_id = b.department_id
AND a.employee_id = 100;
DBMS_OUTPUT.PUT_LINE(vs_emp_name || ' - ' || vs_dep_name);
END;

실행)

Untitled

데이터 타입

1
2
3
4
5
6
7
8
9
10
11
12
DECLARE
vs_emp_name employees.emp_name%TYPE; -- 사원명 변수
vs_dep_name departments.department_name%TYPE; -- 부원명 변수
BEGIN
SELECT a.emp_name, b.department_name
INTO vs_emp_name, vs_dep_name
FROM employees a
, departments b
WHERE a.department_id = b.department_id
AND a.employee_id = 100;
DBMS_OUTPUT.PUT_LINE(vs_emp_name || ' - ' || vs_dep_name);
END;

실행)

Untitled

데이터 크기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- SQL과 PL/SQL에서 사용할 수 있는 데이터 크기는 다름
-- PL/SQL > SQL보다 더 큰 크기로 사용 가능
DROP TABLE ch08_varchar2;
CREATE TABLE ch08_varchar2(
VAR1 VARCHAR2(4000)
);

INSERT INTO ch08_varchar2 (VAR1)
VALUES ('tQbADHDjqtRCvosYCLwzbyKKrQCdJubDPTHnzqvjRwGxhQJtrVbXsLNlgeeMCemGMYpvfoHUHDxIPTDjleABGoowxlzCVipeVwsMFRNzZYgHfQUSIeOITaCKJpxAWwydApVUlQiKDgJlFIOGPOKoJsoemqNbOLdZOBcQhDcMLXuYjRQZDIpgpmImgiwzcLkSilCmLrSbmFNsKEEpzCHDylMvkYPKPNeuJxLvJiApNCYzrMcflECbxwNTKSxaEwVvCYnTnFfMFgDqxobWcSmMJrNTQIVOeWlPaMTfRHsrlFSukppmljmOojPSgJiSbQcgtWWOwUNNYFGtgCGBsIcTGAiHWBxtYVXecoJgJCAJptIVmVTZSKliRLoPYTIUpksBuQaqFHLhCkosWChoMjbqgLtBIRBynsKjKiLrdeHVvZanNVElDjLWwlCDhbpsAVQMTzjzhoKIJBdthynMBMVjeNmsKAjdAYhPZKmuKOuMloQdkqPjoKbfjDEeATciMrXiMQorMhYmBlMODBbyLLIkbmtZdPcWGSuxFEUwXnWpvnunEgcLelSneRIpgRNTzTkHqgLbpxoHzCYgSWlIAvKljCnmWiPWGGwlUFOudRSdoqUxntyhNYEiVXtMObywEltTImawnElpmeiWwlTjGTFceqyjhNqiDLxwduubykWzDmFSJNvVvDZibrCpAReqQjlQZcxuVqjKGKvoDuEcQPQeDzmdMYSOTIQdPDNfDffCOUWflHSQhvVTiYumBQIoyznWNITGZkefknJpGEutUnhBgLPQTWTBeTYccqlLrxvRjfJpdpfVDqqfKCngemIEDDHNdvBxCqKDTrrJAumXMKgpWLIHctQuACeNaKnffpYXiioLxZDrxpuZPPUGpRsCtoQuBfogkKuusVATkMyajKTPSyTQbfhZepRjNdrhkymqKvsAcThYbMSMnkKcLWFPAMeGysBVKkQtFMPvRBoDszlSZcMYzwxkKQwJnuVnDxShYiHFlzgDWqhZoqeypyFVBNDtHkiVzHkQisYLbsbVneJyHbHdtaIFLVbfTqbkGQTEjFlPiGUddPUIoLWALrbKcLwBizwhJvaXkvOphcGWpdNAhxgehCvjcQFSFhxrBuANKjyWncWAUpKKJcfQCsQlLfpqdMhjWGkAMMWUaDfCrGtmtkiIZOdNapEnvfFKiHAhBhejgKSuyKXFQXyCaLwwvonHsceJKgjtnYVZvBCYYBSqNCqVqCGewootJJsqrCnmiteMZBbyMPnIrdcielnGUYmwiOPmEqKGvxDmDRTDRumnSRcnvgxLbaiQIuzdslEIMquvvwmvgaumqPkduNyfRtXErCPvDYLelhjNNOjbGryRpTtDHxIJebMEtKryUyZRIdADeTEBExwHMRHzAYFizYiesaMhNIsOUzUTmyEMuFQrsUEtjwhUWIvADNlrcxPZwRazPMMvdVZssmXbXuCkRoPYNGLPwUmrWrrIgQoMSGMPvTcbHnbtleyKYmOMgymANQBZDMoqAOzMHrAVunIiykCudFVNObNgXOoyfQRICbFsWygSZXufipvrWWmRnBWYdoKmIRewOObUjiNDdQsxQIXtlbPSSngfQPfeQKOolVASXIuAmeODKtSOPaEaFKcedGzzsbrPlsPnRRuYFeVdhyufpjFVVrTPczSQkmPYXercLMmVEaDmJXKTqEVNSKeOshDCDJwdINFsLhAuKIIfOdjSEndDwumQLvePVjzNoIfUELOANeshoNgwVhFADjtUIjIhQAIyRnzSoxSRSWklITMgdjQZTthwsnBVLWyfSsAdLzOnEqmMCGBlTYGjtqvKbBoATRwkPkOTSbUhZClVzjiLLIFEMuptuodeRKXUaBfUhVTtasFsZdVnKtEfLldJYsxjlrBADRqhEBEmBKxlXKgEhiKcwAdztcETMUteJwadfaZLEBRjwJOGaIMhsfAxtuBQWyQLGXPDlFQmkcMsKsGUlQBEAubDqbuBYqXLZgmhPftLkYaCYGReLCVXssOxzJFJwnxKJzaaYzfVpbHYBtiBeQZRilJZqrrMTrVtYAcwGxAAddwtlxzdZebfZHjzqRmrrBPNbkVHqjCHtVKUjIDPVSrtyEsPRPoyyPOFOSBcgClTzlAIPmPMkdlpFHctzKGpyQMInMwPKojVErCOrHbCsZoEXqyOcHReSybmxwYabyioVnDxPEvskutVHLWQTNudmKICoaoSGKqONrBmvtGNBKAaJxCRKTDOIqrJOsQVOmGxmuIDEddVYvDwILTyushOAiXbkRIKgNLnFJdOagmiOHKRBKIIkxkOUeZWMRNlqpJdFgKjrGhIzrgBtgjVOtZAskKRbqzRVwLUoUAtRpRkoRQNLIrbLmmjZTugXJBNCscnMguKVAFDKpODtCsmdlBvQGALeBGUitYBxLYhJxeVcAnTWmTAvCITzdzqiBfEudEIBmkDAXIFmoOmsTMZDOnhXYrgMDlDbjednYWWJbGhrXFrxMQmQSmRBwoOqWGbGmjZNlJCvSHvmtZUkIScWXVdfSsdvdyQNpGFIOuteXhCMLmmEHrMucEmFbCIOHTJINAuIUOPfAfijIPkZjppGCCSRJNXWNCmliwUgABkHWuelUWeLsyVKVcZWOSeiQBQibCQJQUgGkTrXZxdBLsgjeMIwOyORDBpywuvlrLScRNhvaCYaKKRvOZeqBebUWWFhNnIRJvedFNfFPgWZJgNRaUpyYWFNiXJfAqNjyCEQYwAdFBQKKolwrufmJOfrToJFEsoNjaphcNvfWGIjKrKZSoSJEsbRqNVcoprpcGrnBgcNAnWUFpRldcPJkPfaoLKRCmVyMAWMXmnScodKisCTqllZEWQQSCFETxLNntgdcFEFRsTSIhuewwrHIlOeCcRqkzgQhKnKyHZHdFsMEKvPywLbjaspVxUMEkVzCGcGoTmaBjUMwJuAYdSTaYGDHHWDrvGgMVTtehpzfgofkmqtamffJbCKOzJgPsHNEnFarjADJGyKLwwitCiBXIraUdZtZwNjUtGbWqxksepVYztIBrimByoYQfUQgOndzFmhnuSmhYWvHliWUHgbvBIkYasDElNsjcCLtMvjQEhJjWvlnAscPwOYfelrfgfRAZGBxdFlMNkfYEWLbkfUhbRPHoDZsaAQdoKhAAWzOcHoAkkHPQMNIxgHNJaqEFBqCuMYEtLpMnIiMCWWEPnBYgYrxlXFGYpQWUNFevwcEUvUzDeSZNrdmahAfjeLSAGjHVnqyTzJkiVXjDJXzOiszXQCErQwwDMMqjLxWebJwNAVdrXeyMDRYXmLMDnuWLVaShVGhlgvbjOdOnhCDTNVazYDnzstqxjOuWbLcDaavRumKUOQXBQwKtdFgOzXiQKWFporrIcylIHlTmTKAIpBqNUbkajLTlwAHieCcqPIJYhegwQhWpYZdfxpQXDKtYzsrmnvdiTKgXfXKlIHPHlxQtqXGhMVPOBAKVZJfkrDNEwnQFwgfoHJSqQxTzRswVLrtFgpVzKcLilgznElWUfhERyeUrCcFCuGJddlFHJrXsqRdUjqUwaBmJVNwjRbCFiVMOSFuNctNVzhmhUpoddsMPUFMvNIMsMjHIWYiLjhSajZqpDkMvUOUCbYKfNHGpdUeWGUtDXHDNSCEXqYrhWhvnISnjfoBMCwwptksarPImRZaRxBMjoBdlmRGlIuQZDzCLnxxioATnGVFFTATUpeypOCaCeJAvPLxEXYzlCgXvXirGSZFyZPPSCdOSHxeELRsetFrWgqPNNpwgbgBEYPOSpLWeVdqOxPaQnidyPVMmELzeJPWgNsWBdPJPjhkdGpeAYZfrBNqdbOwzbtLiWMPafjgWQNcWKqmcleWLcMJoGSAEIUyFuzElZKXonHOMDdGMtSKEFUWdfPfnDecKNhIjAKRYmkXgpPAzlKIOpViZPkZdozzAoWwDnXkfDikvkXcQaoBtzKkcRhNpJRYaGTkdnlfotsJZsLqpYaWoK');

commit;

DECLARE
vs_sql_varchar2 VARCHAR2(4000);
vs_plsql_varchar2 VARCHAR2(32767);
BEGIN

-- ch08_varchar2 테이블의 값을 변수에 담는다.
SELECT VAR1
INTO vs_sql_varchar2
FROM ch08_varchar2;

-- PL/SQL 변수에 4000 BYTE 이상 크기의 값을 넣는다.
vs_plsql_varchar2 := vs_sql_varchar2 || ' - ' || vs_sql_varchar2 || ' - ' || vs_sql_varchar2;

-- 각 변수 크기를 출력한다.
DBMS_OUTPUT.PUT_LINE('SQL VARCHAR2 길이 : ' || LENGTHB(vs_sql_varchar2));
DBMS_OUTPUT.PUT_LINE('PL/SQL VARCHAR2 길이 : ' || LENGTHB(vs_plsql_varchar2));
END;

Untitled

두 수의 합

1
2
3
4
5
6
7
8
9
10
-- 두 수의 합
accept p_num1 prompt '첫번째 숫자를 입력하세요 ~ '
accept p_num2 prompt '두번째 숫자를 입력하세요 ~ '

DECLARE
v_sum number(10);
BEGIN
v_sum := &p_num1 + &p_num2;
DBMS_OUTPUT.PUT_LINE('TOtal : ' || v_sum);
END;

실행) 1 2

Untitled

데이터 입력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
DROP table emp;
DROP table dept;

CREATE TABLE DEPT
(DEPTNO number(10),
DNAME VARCHAR2(14),
LOC VARCHAR2(13) );

INSERT INTO DEPT VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO DEPT VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO DEPT VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO DEPT VALUES (40, 'OPERATIONS', 'BOSTON');

CREATE TABLE EMP (
EMPNO NUMBER(4) NOT NULL,
ENAME VARCHAR2(10),
JOB VARCHAR2(9),
MGR NUMBER(4) ,
HIREDATE DATE,
SAL NUMBER(7,2),
COMM NUMBER(7,2),
DEPTNO NUMBER(2) );

INSERT INTO EMP VALUES (7839,'KING','PRESIDENT',NULL,'81-11-17',5000,NULL,10);
INSERT INTO EMP VALUES (7698,'BLAKE','MANAGER',7839,'81-05-01',2850,NULL,30);
INSERT INTO EMP VALUES (7782,'CLARK','MANAGER',7839,'81-05-09',2450,NULL,10);
INSERT INTO EMP VALUES (7566,'JONES','MANAGER',7839,'81-04-01',2975,NULL,20);
INSERT INTO EMP VALUES (7654,'MARTIN','SALESMAN',7698,'81-09-10',1250,1400,30);
INSERT INTO EMP VALUES (7499,'ALLEN','SALESMAN',7698,'81-02-11',1600,300,30);
INSERT INTO EMP VALUES (7844,'TURNER','SALESMAN',7698,'81-08-21',1500,0,30);
INSERT INTO EMP VALUES (7900,'JAMES','CLERK',7698,'81-12-11',950,NULL,30);
INSERT INTO EMP VALUES (7521,'WARD','SALESMAN',7698,'81-02-23',1250,500,30);
INSERT INTO EMP VALUES (7902,'FORD','ANALYST',7566,'81-12-11',3000,NULL,20);
INSERT INTO EMP VALUES (7369,'SMITH','CLERK',7902,'80-12-11',800,NULL,20);
INSERT INTO EMP VALUES (7788,'SCOTT','ANALYST',7566,'82-12-22',3000,NULL,20);
INSERT INTO EMP VALUES (7876,'ADAMS','CLERK',7788,'83-01-15',1100,NULL,20);
INSERT INTO EMP VALUES (7934,'MILLER','CLERK',7782,'82-01-11',1300,NULL,10);

commit;

drop table salgrade;

create table salgrade
( grade number(10),
losal number(10),
hisal number(10) );

insert into salgrade values(1,700,1200);
insert into salgrade values(2,1201,1400);
insert into salgrade values(3,1401,2000);
insert into salgrade values(4,2001,3000);
insert into salgrade values(5,3001,9999);

commit;

조회

1
2
3
4
5
6
7
8
9
10
11
12
13
-- 사원번호를 찾아라
-- 사원번호를 입력하면 해당 사원의 급여가 나오도록 출력하세요!
-- 7782
ACCEPT p_empno prompt '사원 번호를 입력하세요 ~'
DECLARE
v_sal number(10);
BEGIN
SELECT SAL INTO v_sal
FROM emp
WHERE empno = &p_empno;

DBMS_OUTPUT.PUT_LINE('월급은 '|| v_sal);
END;

실행) 7893

Untitled

조건절

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 조건절 & 반복문
-- 사원 이름을 입력합니다. 받는 급여가 고소득자인지, 중간 소득자, 저 소득자
-- KING, SCOTT
SELECT * FROM emp;

ACCEPT p_ename prompt '사원 이름을 입력하세요 ~ '
DECLARE
-- 변수 선언
v_ename emp.ename%TYPE := upper('&p_ename');
v_sal emp.sal%TYPE;
BEGIN
SELECT sal into v_sal
FROM emp
WHERE ename = v_ename;
DBMS_OUTPUT.PUT_LINE('급여 ' || v_sal);

-- 조건식
IF v_sal >= 3500 THEN
DBMS_OUTPUT.PUT_LINE('고 소득자');
ELSIF v_sal >= 2000 THEN
DBMS_OUTPUT.PUT_LINE('중간 소득자');
ELSE
DBMS_OUTPUT.PUT_LINE('저 소득자');
END IF;
END;

실행) KING

Untitled

반복문

  • 구구단 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
-- 반복문 
-- 구구단!
2 x 1 = 2 = 2 x 1
2 x 2 = 4 = 2 x 2
DECLARE
v_count number(10) := 0;
BEGIN
LOOP
v_count := v_count + 1;
DBMS_OUTPUT.PUT_LINE('2 x ' || v_count || ' = ' || 2 * v_count);
EXIT WHEN v_count = 9;
END LOOP;
END;
  • Reference : 오라클 SQL과 PL/SQL을 다루는 기술

PL/SQL 실습01

PL/SQL 실습02

PL/SQL - ML

Oracle_practice

Oracle 실습-테스트

SQL-Developer

  • SQL - Developer 열기
  • 슬랙에서 dataset 다운로드
  • SQL - Developer 에서 데이터 임포트

SQL 서브쿼리 연습문제

  • 문제 1. 2015년 평균 기대수명보다 높은 모든 정보를 조회하세요.
  • 테이블명 : populations
  • 조건식에 서브쿼리를 설정
  • 메인쿼리 서브쿼리를 설정

답)

1
2
3
4
5
6
SELECT *
FROM populations
WHERE life_expectancy > (SELECT AVG(life_expectancy)
FROM populations
WHERE year = 2015 )
AND year=2015;

실행)

Untitled

  • 문제 2. subquery_countries 테이블에 있는 capital과 매칭되는 cities 테이블의 정보를 조회하라
    • 조회할 컬럼명은 name, country_code, urbanarea_pop

답)

1
2
3
4
5
6
7
8
9
10
SELECT 
name
, country_code
, urbanarea_pop
FROM
cities
WHERE name IN
(SELECT capital
FROM sub_countries)
ORDER BY urbanarea_pop DESC;

실행)

Untitled

  • 문제 3.
  • 조건 1. economies 테이블에서 country code, inflation rate, unemployment rate를 조회한다.
  • 조건 2. inflation rate 오름차순으로 정렬한다.
  • 조건 3. subquery_countries 테이블내 gov_form 컬럼에서
  • Constitutional Monarchy 또는 Republic이 들어간 국가는 제외한다.
  • Select fields
  • 데이터셋

답)

1
2
3
4
5
6
7
8
9
10
11
12
SELECT 
code
, inflation_rate
, unemployment_rate
FROM
economics
WHERE year = 2015
AND code NOT IN (SELECT code
FROM sub_countries
WHERE (gov_form='Republic' OR gov_form LIKE '%Republic%')
)
ORDER BY inflation_rate;

실행)

Untitled

  • 문제 4. 2010년 각 대륙별 inflation_rate가 가장 심한 국가와 inflation_rate를 구하세요.
  • 힌트 1. 아래 쿼리 실행
    SELECT country_name, continent, inflation_rate FROM sub_countries INNER JOIN economics USING (code) WHERE year = 2015;
  • 2015년 기준으로, 각 대륙별 inflation_rate가 가장 높은 나라를 추출하는 코드를 작성한다.
  • inflation_rate가 높은 순

답)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT sc.country_name, sc.continent, ec.inflation_rate
FROM sub_countries sc
INNER JOIN economics ec
ON sc.code = ec.code
WHERE year = 2015
AND inflation_rate IN(
SELECT MAX(inflation_rate) AS max_inf
FROM(
SELECT sc.country_name, sc.continent, ec.inflation_rate
FROM sub_countries sc
INNER JOIN economics ec
-- USING(code) 대신 ON 쿼리를 작성
ON sc.code = ec.code
WHERE year = 2015)
GROUP BY continent
);

실행)

Untitled

SQL 윈도우 함수 연습문제

  • 문제 1. 각 행에 숫자를 1, 2, 3, …, 형태로 추가한다. (row_n 으로 표시)
  • row_n 기준으로 오름차순으로 출력
  • 테이블명에 alias를 적용한다.

실행)

1
2
3
4
SELECT
ROWNUM as row_n
, sm.*
FROM summer_medals sm;

실행)

Untitled

  • 문제 2. 올림픽 년도를 오름차순 순번대로 작성을 한다.
  • 힌트 : 서브쿼리와 윈도우 함수를 이용한다.

답)

1
2
3
4
5
6
7
SELECT
year,
ROW_NUMBER() OVER(ORDER BY YEAR) AS Row_N
FROM(
SELECT DISTINCT Year
FROM summer_medals
)Years;

실행)

Untitled

  • 문제 3.
  • (1) WITH 절 사용하여 각 운동선수들이 획득한 메달 갯수를 내림차순으로 정렬하도록 합니다.
  • (2) (1) 쿼리를 활용하여 그리고 선수들의 랭킹을 추가한다.
  • 상위 5개만 추출 : OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY
  • WITH AS (1번 쿼리)
  • 2번 쿼리

답)

1
2
3
4
5
6
7
8
9
10
11
12
13
WITH medals AS(
SELECT
Athlete
, COUNT(*) AS Medals
FROM summer_medals
GROUP BY Athlete)
SELECT
medals
, Athlete
, ROW_NUMBER() OVER (ORDER BY Medals DESC) AS ROW_N
FROM medals
ORDER BY Medals DESC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY;

실행)

Untitled

  • 문제 4
  • 다음쿼리를 실행한다.
  • 남자 69KG 역도 경기에서 매년 금메달리스트 조회하도록 합니다.
    SELECT Year, Country AS champion FROM summer_medals WHERE Discipline = 'Weightlifting' AND Event = '69KG' AND Gender = 'Men' AND Medal = 'Gold';
  • 기존 쿼리에서 매년 전년도 챔피언도 같이 조회하도록 합니다.
  • LAG & WITH 절 사용

답)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
WITH Weightlifting_Gold AS (
SELECT
-- Return each year's champions' countries
Year
, Country AS champion
FROM summer_medals
WHERE
Discipline = 'Weightlifting' AND
Event = '69KG' AND
GENDER = 'Men' AND
Medal = 'Gold')
SELECT
Year
, Champion
, LAG(Champion) OVER (ORDER BY Year ASC) AS Last_Champion
FROM Weightlifting_Gold
ORDER BY Year ASC;

실행)

Untitled

  • Reference : 오라클 SQL과 PL/SQL을 다루는 기술