1. MVC 패턴 (Model View Controller)
: 유지보수를 위해 사용함
2. [과제] 전화번호부 6.0 : 데이터베이스 이용하여 수정하기
package phonebook06.db;
// 전화번호부 6.0 : 파일이 아닌 데이터 베이스로 변경
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
// CONTROLLER 객체
// 어플리케이션의 동작, 데이터 처리(CRUD), (Business logic 담당)
public class PhonebookManager implements Pb, Closeable {
// TODO : DB를 위한 변수를 선언
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// singleton적용
private PhonebookManager() {
// TODO :
// JDBC 프로그래밍
// 클래스 로딩
// 연결 Connection
// Statement (필요하다면) 생성
try {
// JDBC 드라이버 클래스를 메모리에 로드
Class.forName(DRIVER); // 동적 클래스 로딩
//System.out.println("드라이버 클래스 로딩 성공");
// DB와 connection을 맺음
conn = DriverManager.getConnection(URL, USER, PASSWD);
//System.out.println("DB Connection 연결");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
private static PhonebookManager instance = null;
public static PhonebookManager getInstance() {
if (instance == null) {
instance = new PhonebookManager();
}
return instance;
} // end getInstance()
// 전화번호부 생성 등록
@Override
public int insert(String name, String phoneNum, String memo) {
// 매개변수 검증 : 이름 필수
if (name == null || name.trim().length() == 0) {
throw new PhonebookException("insert() 이름입력오류: ", Pb.ERR_EMPTY_STRING);
}
int cnt = 0;
// TODO
// SQL_INSERT 사용하여 INSERT
// PreparedStatement 사용.., 여기서 close도 필요(PreparedStatement만...!!)
try {
// "INSERT INTO phonebook(pb_uid, pb_name, pb_phonenum, pb_memo)
// VALUES(phonebook_seq.nextval, ?, ?, ?)"
pstmt = conn.prepareStatement(SQL_INSERT);
pstmt.setString(1, name);
pstmt.setString(2, phoneNum);
pstmt.setString(3, memo);
cnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(pstmt != null) {pstmt.close();}
if(rs != null) {rs.close();}
} catch (SQLException e) {
e.printStackTrace();
}
}
return 1;
}
@Override
public PhonebookModel[] selectAll() {
// TODO
// SQL_SELECT_ALL 사용하기
PhonebookModel[] pb = null;
int rowCount = 0;
try {
// Row 행의 값을 찾기
pstmt = conn.prepareStatement(SQL_COUNT_ALL);
rs = pstmt.executeQuery();
while(rs.next()) {
rowCount = rs.getInt("cnt");
}
pstmt.close();
rs.close();
// SQL_SELECT_ALL 사용하기
// SELECT * FROM phonebook ORDER BY pb_uid DESC
pstmt = conn.prepareStatement(SQL_SELECT_ALL);
rs = pstmt.executeQuery();
pb = new PhonebookModel[rowCount];
int Number = 0;
while(rs.next()) {
pb[Number] = new PhonebookModel(rs.getInt(COL_LABEL_UID),
rs.getString(COL_LABEL_NAME),
rs.getString(COL_LABEL_PHONENUM),
rs.getString(COL_LABEL_MEMO),
rs.getDate(COL_LABEL_REGDATE));
Number++;
} // end while
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(pstmt != null) {pstmt.close();}
if(rs != null) {rs.close();}
} catch (SQLException e) {
e.printStackTrace();
}
}
return pb;
}
// 특정 uid 의 데이터 검색 리턴
// 못찾으면 null 리턴
@Override
public PhonebookModel selectByUid(int uid) {
// TODO : 옵션
PhonebookModel pb = null;
try {
// "SELECT pb_uidFROM phonebook WHERE pb_uid = ?"
pstmt = conn.prepareStatement(SQL_SELECT_CHOOSE);
pstmt.setInt(1, uid);
rs = pstmt.executeQuery();
while(rs.next()) {
pb = new PhonebookModel(rs.getInt(COL_LABEL_UID),
rs.getString(COL_LABEL_NAME),
rs.getString(COL_LABEL_PHONENUM),
rs.getString(COL_LABEL_MEMO),
rs.getDate(COL_LABEL_REGDATE));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(pstmt != null) {pstmt.close();}
if(rs != null) {rs.close();}
return pb;
} catch (SQLException e) {
e.printStackTrace();
}
}
return null; // 못찾으면 null 리턴
}// end selectByUid()
@Override
public int updateByUid(int uid, String name, String phoneNum, String memo) {
// 매개변수 검증
if (uid < 1)
throw new PhonebookException("update() uid 오류: " + uid, Pb.ERR_UID);
int cnt = 0;
// TODO
// SQL_UPDATE_BY_UID 사용
try {
// "UPDATE phonebook SET pb_name = ?,
// pb_phonenum = ?, pb_memo = ? WHERE pb_uid = ?"
pstmt = conn.prepareStatement(SQL_UPDATE_BY_UID);
pstmt.setString(1, name);
pstmt.setString(2, phoneNum);
pstmt.setString(3, memo);
pstmt.setInt(4, uid);
cnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(pstmt != null) {pstmt.close();}
if(rs != null) {rs.close();}
} catch (SQLException e) {
e.printStackTrace();
}
}
return cnt;
}
@Override
public int deleteByUid(int uid) {
// 매개변수 검증
if (uid < 1)
throw new PhonebookException("deleteByUid() uid 오류: " + uid, Pb.ERR_UID);
int cnt = 0;
// TODO
// SQL_DELETE_BY_UID 사용
try {
// "DELETE FROM phonebook WHERE pb_uid = ?"
pstmt = conn.prepareStatement(SQL_DELETE_BY_UID);
pstmt.setInt(1, uid);
cnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(pstmt != null) {pstmt.close();}
if(rs != null) {rs.close();}
} catch (SQLException e) {
e.printStackTrace();
}
}
return cnt;
}
// 현재 데이터중 가장 큰 uid 값을 찾아서 리턴
private int getMaxUid() {
int maxUid = 0;
try {
//"SELECT MAX(pb_uid) FROM phonebook"
pstmt = conn.prepareStatement(SQL_SELECT_MIX);
rs = pstmt.executeQuery();
while(rs.next()) {
maxUid = rs.getInt("uid_max");
return maxUid;
} // end while
System.out.println(rs);
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(pstmt != null) {pstmt.close();}
if(rs != null) {rs.close();}
} catch (SQLException e) {
e.printStackTrace();
}
}
return maxUid;
}
@Override
public void close() throws IOException {
// TODO
// ResultSet
// Statement
// Connection
// 들 close()
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
} // end close()
} // end PhonebookManager
// 예의 클래스 정의
// 예외발생하면 '에러코드' + '에러메세지'를 부여하여 관리하는게 좋다.
class PhonebookException extends RuntimeException {
private int errCode = Pb.ERR_GENERIC;
public PhonebookException() {
super("Phonebook 예외 발생");
}
public PhonebookException(String msg) {
super(msg);
}
public PhonebookException(String msg, int errCode) {
super(msg);
this.errCode = errCode;
}
// Throwable 의 getMessage 를 오버라이딩 가능
@Override
public String getMessage() {
String msg = "ERR-" + errCode + "]" + Pb.ERR_STR[errCode] + " " + super.getMessage();
return msg;
}
} // end PhonebookException
package phonebook06.db;
// Controller 인터페이스
// 동작 정의하기 전에 '동작 설계'
public interface Pb {
public static final String VERSION = "전화번호부 6.0";
// 만들고자 하는 응용프로그램의
// '동작' 을 설계하는 것이 인터페이스다.
// 이 인터페이스는 View 와 Controller 의 연결고리 역할을 하여. 모듈단위 유지관리를 용이하게 해준다.
// [동작 설계]
// 1. 이름(name)과 전화번호(phoneNum)과 (memo) 값이 주어지면 전화번호 데이터(PhonebookModel)를
// 생성하여 저장, 날짜(regDate) 는 생성한 날짜로, uid 값은 자동 증가 값으로
// 성공하면 1, 실패하면 0 리턴
// 2. 현재 전화번호부에 저장된 전화번호 데이터(PhonebookModel)들을
// 전부 불러들여서 리턴(PhonebookModel 배열로)
// 3. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel)을 찾아서
// 리턴, 없으면 null 리턴
// 4. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel)을 찾아서
// 주어진 이름(name) 과 전화번호(phoneNum) 과 (memo) 값 으로 변경.
// 성공하면 1, 실패하면 0 리턴
// 5. 특정 uid 값을 가진 전화번호 데이터(PhonebookModel) 을 찾아서 삭제.
// 성공하면 1, 실패하면 0 리턴
public abstract int insert(String name, String phoneNum, String memo); /* 1 */
public abstract PhonebookModel[] selectAll(); /* 2 */
public abstract PhonebookModel selectByUid(int uid); /* 3 */
public abstract int updateByUid(int uid, String name, String phoneNum, String meno); /* 4 */
public abstract int deleteByUid(int uid); /* 5 */
//public static final int QUERY_FAIL = 0;
// 에러코드 상수
public static final int ERR_GENERIC = 0; // 일반 오류
public static final int ERR_INDEXOUTOFRANGE = 1; // 인덱스 범위 벗어남
public static final int ERR_EMPTY_STRING = 2; // 입력문자열이 empty (혹은 null)인 경우
public static final int ERR_UID = 3; // uid가 없는경우
// 에러문자열
public static final String[] ERR_STR = {
"일반오류", // 0
"인덱스오류", // 1
"문자열오류", // 2
"UID 오류" // 3
};
// 1. Oracle 연동을 위한 정보들(상수들)을 정의(세팅)
// JDBC 드라이버 클래스 정보
public static final String DRIVER =
"oracle.jdbc.driver.OracleDriver";
// DB 서버 정보
public static final String URL =
"jdbc:oracle:thin:@localhost:1521:XE";
// DB 사용자 계정 정보
public static final String USER = "scott0316";
public static final String PASSWD = "tiger0316";
public static final String TBL_NAME = "phonebook";
public static final String COL_LABEL_UID = "pb_uid";
public static final String COL_LABEL_NAME = "pb_name";
public static final String COL_LABEL_PHONENUM = "pb_phonenum";
public static final String COL_LABEL_MEMO = "pb_memo";
public static final String COL_LABEL_REGDATE = "pb_regdate";
public static final String SEQ_NAME = "phonebook_seq";
// 쿼리 문자열
// PreparedStatement 방식
public static final String SQL_COUNT_ALL
= "SELECT COUNT(*) cnt FROM " + TBL_NAME;
public static final String SQL_SELECT_ALL
= "SELECT * FROM " + TBL_NAME
+ " ORDER BY " + COL_LABEL_UID + " DESC";
public static final String SQL_INSERT
= "INSERT INTO " + TBL_NAME
+ "(pb_uid, pb_name, pb_phonenum, pb_memo) "
+ "VALUES(" + SEQ_NAME + ".nextval, ?, ?, ?)";
public static final String SQL_UPDATE_BY_UID
= "UPDATE " + TBL_NAME + " SET "
+ COL_LABEL_NAME + " = ?, "
+ COL_LABEL_PHONENUM + " = ?, "
+ COL_LABEL_MEMO + " = ? "
+ "WHERE " + COL_LABEL_UID + " = ?";
public static final String SQL_DELETE_BY_UID
= "DELETE FROM " + TBL_NAME
+ " WHERE " + COL_LABEL_UID + " = ?";
// TODO : 추가로 필요한 쿼리문
//SELECT * FROM phonebook WHERE pb_uid = 3;
public static final String SQL_SELECT_CHOOSE
= "SELECT * FROM " + TBL_NAME + " WHERE "
+ COL_LABEL_UID + " = ?";
//SELECT MAX(pb_uid) FROM phonebook;
public static final String SQL_SELECT_MIX
= "SELECT MAX(" + COL_LABEL_UID
+ ") uid_max FROM " + TBL_NAME;
}
package phonebook06.db;
import java.io.IOException;
import java.util.InputMismatchException;
import java.util.Scanner;
// VIEW 객체
// 사용자와의 입출력 담당 (UI,..)
public class PhonebookMain {
private Scanner sc;
private PhonebookManager pbCtrl; // CONTROLLER 객체
public static void main(String[] args) {
PhonebookMain app = new PhonebookMain();
app.init(); // 초기화
app.run(); // 실행
app.exit(); // 종료
} // end main()
// 응용프로그램을 초기화
public void init() {
sc = new Scanner(System.in);
pbCtrl = PhonebookManager.getInstance(); // CONTROLLER 생성
}
// 응용프로그램 구동
public void run() {
System.out.println(Pb.VERSION);
int menu;
while(true) {
showMenu(); // 메뉴 표시
try {
menu = sc.nextInt(); // 메뉴 입력
sc.nextLine();
switch(menu) {
case Menu.MENU_INSERT:
insertPhoneBook();
break;
case Menu.MENU_LIST:
listPhonebook();
break;
case Menu.MENU_DELETE:
deletePhonebook();
break;
case Menu.MENU_UPDATE:
updatePhonebook();
break;
case Menu.MENU_QUIT:
pbCtrl.close(); // 파일 저장
System.out.println("프로그램을 종료합니다");
return;
default:
System.out.println("잘못 입력하셨습니다");
} // end switch
} catch (PhonebookException ex) {
System.out.println(ex.getMessage());
} catch (InputMismatchException ex) {
System.out.println("잘못된 입력입니다");
sc.nextLine();
} catch (IOException e) {
e.printStackTrace();
}
} // end while
} // end run()
// 응용프로그램 종료
public void exit() {
sc.close();
}
// 전화번호부 입력
public void insertPhoneBook() {
// VIEW 역할 : 사용자 입출력
System.out.println("-- 입력 메뉴 --");
// 이름, 전화번호, 이메일 입력
System.out.print("이름 입력:");
String name = sc.nextLine();
System.out.print("전화번호 입력:");
String phoneNum = sc.nextLine();
System.out.print("메모 입력:");
String memo = sc.nextLine();
// CONTROLLER에 연결
int result = pbCtrl.insert(name, phoneNum, memo);
System.out.println(result + " 개의 전화번호 입력 성공");
} // end insertPhoneBook()
// 전화번호부 열람 (전체)
public void listPhonebook() {
// CONTROLLER 연결
PhonebookModel[] data = pbCtrl.selectAll();
// VIEW 역할 : 사용자 입출력
System.out.println("총 " + data.length + " 명의 전화번호 출력");
for(PhonebookModel e : data) {
System.out.println(e);
}
} // end listPhonebook()
// 전화번호부 수정
public void updatePhonebook() {
// VIEW : 사용자 입출력
System.out.println("--- 수정 메뉴 ---");
System.out.println("수정할 번호 입력:");
int uid = sc.nextInt();
sc.nextLine();
if(pbCtrl.selectByUid(uid) == null) {
System.out.println("존재하지 않는 uid : " + uid);
return;
}
// 이름, 전화번호, 이메일 입력
System.out.print("이름 입력:");
String name = sc.nextLine();
System.out.print("전화번호 입력:");
String phoneNum = sc.nextLine();
System.out.print("메모 입력:");
String memo = sc.nextLine();
// CONTROLLER 연결
int result = pbCtrl.updateByUid(uid, name, phoneNum, memo);
System.out.println(result + " 개의 전화번호 수정 성공");
} // end updatePhonebook()
// 전화번호부 삭제
public void deletePhonebook() {
// VIEW : 사용자 입출력
System.out.println("--- 삭제 메뉴 ---");
System.out.println("삭제할 번호 입력:");
int uid = sc.nextInt();
sc.nextLine();
// CONTROLLER
int result = pbCtrl.deleteByUid(uid);
System.out.println(result + " 개의 전화번호 삭제 성공");
} // end deletePhonebook()
public void showMenu() {
System.out.println();
System.out.println("전화번호부 프로그램");
System.out.println("------------------");
System.out.println(" [0] 종료");
System.out.println(" [1] 입력");
System.out.println(" [2] 열람");
System.out.println(" [3] 수정");
System.out.println(" [4] 삭제");
System.out.println("------------------");
System.out.print("선택: ");
}
} // end class
package phonebook06.db;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
// MODEL 객체
// 데이터 표현 객체
public class PhonebookModel implements Serializable{
/**
*
*/
private static final long serialVersionUID = -7457878413286439105L;
// 멤버변수
private int uid; // unique id
private String name; // 이름
private String phoneNum; // 전화번호
private String memo; // 메모
private Date regDate; // 등록일시
// 기본생성자
public PhonebookModel() {
this.name = "";
this.phoneNum = "";
this.memo = "";
this.regDate = new Date(); // 생성되는 현재시간.
}
// 매개변수 생성자
public PhonebookModel(String name, String phoneNum, String email) {
this();
this.name = name;
this.phoneNum = phoneNum;
this.memo = email;
}
public PhonebookModel(int uid, String name, String phoneNum, String email, Date regDate) {
super();
this.uid = uid;
this.name = name;
this.phoneNum = phoneNum;
this.memo = email;
this.regDate = regDate;
}
// getter & setter
public int getUid() {return uid;}
public void setUid(int uid) {this.uid = uid;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getPhoneNum() {return phoneNum;}
public void setPhoneNum(String phoneNum) {this.phoneNum = phoneNum;}
public String getMemo() {return memo;}
public void setMemo(String memo) {this.memo = memo;}
public Date getRegDate() {return regDate;}
public void setRegDate(Date regDate) {this.regDate = regDate;}
@Override
public String toString() {
String str = String.format("%3d|%s|%s|%s|%20s",
uid, name, phoneNum, memo,
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(regDate));
return str;
}
}
package phonebook06.db;
public interface Menu {
public static final int MENU_QUIT = 0;
public static final int MENU_INSERT = 1;
public static final int MENU_LIST = 2;
public static final int MENU_UPDATE = 3;
public static final int MENU_DELETE = 4;
}
** 쌤이 선택한 코드..!!
package phonebook06.db;
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
// CONTROLLER 객체
// 어플리케이션의 동작, 데이터 처리(CRUD), (Business logic 담당)
public class PhonebookManager implements Pb, Closeable {
// DB 를 위한 변수들 선언
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// singleton적용
private PhonebookManager() {
// JDBC 프로그래밍
// 클래스 로딩
// 연결 Connection
// Statement (필요하다면) 생성
try {
Class.forName(DRIVER);
System.out.println("드라이버 로딩 성공");
conn = DriverManager.getConnection(URL, USER, PASSWD);
System.out.println("DB Connection 성공");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
private static PhonebookManager instance = null;
public static PhonebookManager getInstance() {
if (instance == null) {
instance = new PhonebookManager();
}
return instance;
} // end getInstance()
// 전화번호부 생성 등록
@Override
public int insert(String name, String phoneNum, String memo) {
// 매개변수 검증 : 이름 필수
if (name == null || name.trim().length() == 0) {
throw new PhonebookException("insert() 이름입력오류: ", Pb.ERR_EMPTY_STRING);
}
int cnt = 0;
// SQL_INSERT 사용하여 INSERT
// PreparedStatement 사용..
try {
pstmt = conn.prepareStatement(SQL_INSERT);
pstmt.setString(1, name); // 첫번째 ? 는 1부터 시작! ,type 도 잘보기
pstmt.setString(2, phoneNum); // ?개수 = index개수 인듯 , ?개수만큼 데이터추가해줘야한다. 하나라도 없으면 에러 ;
pstmt.setString(3, memo);
cnt = pstmt.executeUpdate(); // DML은 정수값을 리턴한다 그 이유는??
System.out.println(cnt + " 개 행(row) INSERT성공");
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null)
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return cnt;
}
@Override
public PhonebookModel[] selectAll() {
// SQL_SELECT_ALL 사용
ArrayList<PhonebookModel> pbList = new ArrayList<PhonebookModel>();
try {
pstmt = conn.prepareStatement(SQL_SELECT_ALL);
rs = pstmt.executeQuery();
while (rs.next()) {
String name = rs.getString(COL_LABEL_NAME);
String phonenum = rs.getString(COL_LABEL_PHONENUM);
String memo = rs.getString(COL_LABEL_MEMO);
String Date = rs.getString(COL_LABEL_REGDATE);
SimpleDateFormat transFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date to = transFormat.parse(Date);
// System.out.println(name + " | " + phonenum + " | " + memo);
PhonebookModel pb = new PhonebookModel(rs.getInt(COL_LABEL_UID), name, phonenum, memo, to);
pbList.add(pb);
//pb.setUid(rs.getInt(COL_LABEL_UID));// ********* 여기어려워요 생각합시다!!
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
return pbList.toArray(new PhonebookModel[pbList.size()]);
}
// 특정 uid 의 데이터 검색 리턴
// 못찾으면 null 리턴
@Override
public PhonebookModel selectByUid(int uid) {
PhonebookModel pb = null;
try {
pstmt = conn.prepareStatement(SQL_SELECT_BY_UID);
pstmt.setInt(1, uid);
rs = pstmt.executeQuery();
if (rs.next()) {
String name = rs.getString(COL_LABEL_NAME);
String phonenum = rs.getString(COL_LABEL_PHONENUM);
String memo = rs.getString(COL_LABEL_MEMO);
pb = new PhonebookModel(name, phonenum, memo);
pb.setUid(rs.getInt(COL_LABEL_UID));// ********* 여기어려워요 생각합시다!!
}
} catch (SQLException e) {
e.printStackTrace();
}
return pb;// 못찾으면 null 리턴
}// end selectByUid()
@Override
public int updateByUid(int uid, String name, String phoneNum, String memo) {
// 매개변수 검증
if (uid < 1)
throw new PhonebookException("update() uid 오류: " + uid, Pb.ERR_UID);
if (name == null || name.trim().length() == 0) // 이름 필수
throw new PhonebookException("update() 이름입력 오류: ", Pb.ERR_EMPTY_STRING);
int cnt = 0;
// SQL_UPDATE_BY_UID 사용
try {
pstmt = conn.prepareStatement(SQL_UPDATE_BY_UID);
pstmt.setString(1, name);
pstmt.setString(2, phoneNum);
pstmt.setString(3, memo);
pstmt.setInt(4, uid);
cnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return cnt;
}
@Override
public int deleteByUid(int uid) {
// 매개변수 검증
if (uid < 1)
throw new PhonebookException("deleteByUid() uid 오류: " + uid, Pb.ERR_UID);
int cnt = 0;
// SQL_DELETE_BY_UID 사용
try {
pstmt = conn.prepareStatement(SQL_DELETE_BY_UID);
pstmt.setInt(1, uid);
cnt = pstmt.executeUpdate();
System.out.println(cnt + " 개 행(row) DELETE성공");
} catch (SQLException e) {
e.printStackTrace();
}
return cnt;
}
// 현재 데이터중 가장 큰 uid 값을 찾아서 리턴
private int getMaxUid() {
int maxUid = 0;
// TODO : 옵션
return maxUid;
}
@Override
public void close() throws IOException {
// ResultSet
// Statement
// Connection
// 들 close()..
try {
if (rs != null)rs.close();
if (pstmt != null)pstmt.close();
if (conn != null)conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} // end PhonebookManager
// 예의 클래스 정의
// 예외발생하면 '에러코드' + '에러메세지'를 부여하여 관리하는게 좋다.
class PhonebookException extends RuntimeException {
private int errCode = Pb.ERR_GENERIC;
public PhonebookException() {
super("Phonebook 예외 발생");
}
public PhonebookException(String msg) {
super(msg);
}
public PhonebookException(String msg, int errCode) {
super(msg);
this.errCode = errCode;
}
// Throwable 의 getMessage 를 오버라이딩 가능
@Override
public String getMessage() {
String msg = "ERR-" + errCode + "]" + Pb.ERR_STR[errCode] + " " + super.getMessage();
return msg;
}
} // end PhonebookException
package phonebook06.db;
// Controller 인터페이스
// 동작 정의하기 전에 '동작 설계'
public interface Pb {
public static final String VERSION = "전화번호부 6.0";
public abstract int insert(String name, String phoneNum, String memo); /* 1 */
public abstract PhonebookModel[] selectAll(); /* 2 */
public abstract PhonebookModel selectByUid(int uid); /* 3 */
public abstract int updateByUid(int uid, String name, String phoneNum, String memo); /* 4 */
public abstract int deleteByUid(int uid); /* 5 */
//public static final int QUERY_FAIL = 0;
// 에러코드 상수
public static final int ERR_GENERIC = 0; // 일반 오류
public static final int ERR_INDEXOUTOFRANGE = 1; // 인덱스 범위 벗어남
public static final int ERR_EMPTY_STRING = 2; // 입력문자열이 empty (혹은 null) 인 경우
public static final int ERR_UID = 3; // uid 가 없는 경우
// 에러문자열
public static final String[] ERR_STR = {
"일반오류", // 0
"인덱스오류", // 1
"문자열오류", // 2
"UID 오류", // 3
};
// 1. Oracle 연동을 위한 정보들(상수들)을 정의(세팅)
// JDBC 드라이버 클래스 정보
public static final String DRIVER =
"oracle.jdbc.driver.OracleDriver";
// DB 서버 정보
public static final String URL =
"jdbc:oracle:thin:@localhost:1521:XE";
// DB 사용자 계정 정보
public static final String USER = "scott0316";
public static final String PASSWD = "tiger0316";
public static final String TBL_NAME = "phonebook";
public static final String COL_LABEL_UID = "pb_uid";
public static final String COL_LABEL_NAME = "pb_name";
public static final String COL_LABEL_PHONENUM = "pb_phonenum";
public static final String COL_LABEL_MEMO = "pb_memo";
public static final String COL_LABEL_REGDATE = "pb_regdate";
public static final String SEQ_NAME = "phonebook_seq";
// 쿼리 문자열
// PreparedStatement 방식
public static final String SQL_COUNT_ALL
= "SELECT COUNT(*) cnt FROM " + TBL_NAME;
public static final String SQL_SELECT_ALL
= "SELECT * FROM " + TBL_NAME
+ " ORDER BY " + COL_LABEL_UID + " DESC";
public static final String SQL_INSERT
= "INSERT INTO " + TBL_NAME
+ "(pb_uid, pb_name, pb_phonenum, pb_memo) "
+ "VALUES(" + SEQ_NAME + ".nextval, ?, ?, ?)"
;
public static final String SQL_UPDATE_BY_UID
= "UPDATE " + TBL_NAME + " SET "
+ COL_LABEL_NAME + " = ?, "
+ COL_LABEL_PHONENUM + " = ?, "
+ COL_LABEL_MEMO + " = ? "
+ "WHERE " + COL_LABEL_UID + " = ?"
;
public static final String SQL_DELETE_BY_UID
= "DELETE FROM " + TBL_NAME
+ " WHERE " + COL_LABEL_UID + " = ?"
;
public static final String SQL_SELECT_BY_UID
= "SELECT * FROM "+TBL_NAME
+ " WHERE "+ COL_LABEL_UID+ " = ?"
;
}
3. [폴더] 04_SingleRowFunction
1) SingleRow1_String.sql
-- INSTR() 함수
SELECT INSTR('A*B*C*', '*', 1, 1) FROM dual; -- 2
SELECT INSTR('A*B*C*', '*', 1, 2) FROM dual; -- 4
SELECT INSTR('A*B*C*', '*', 3, 2) FROM dual; -- 6
-- 음수 인덱스의 경우 검색도 음의 방향으로 진행
SELECT INSTR('A*B*C*', '*', -4, 1) FROM dual; -- 2
-- 없으면 0 리턴
SELECT INSTR('A*B*C*', '*', -4, 2) FROM dual; -- 0
SELECT INSTR('A*B*C*', '*', -2, 2) FROM dual; -- 2
-- t_student 테이블 : tel 칼럼을 사용하여 101번 학과(deptno1)의
-- 학생의 이름과 전화번호, ‘)’ 가 나오는 위치 출력
--SELECT name, tel, INSTR(tel, ')', 1, 1) AS 위치
SELECT name, tel, INSTR(tel, ')') AS 위치
FROM t_student
WHERE deptno1 = 101;
-- t_student 테이블 : 1전공이 101 인 학생의 이름과 전화번호,
-- 지역번호를 출력하세요. 지역번호는 숫자만! / substr(), instr() 사용
SELECT name, tel, SUBSTR(tel, 1, INSTR(tel, ')')- 1) 지역변수
FROM t_student
WHERE deptno1 = 101;
-- LTRIM(), RTRIM(), TRIM 함수
SELECT
LTRIM('슈퍼슈퍼슈가맨', '슈퍼') LTRIM,
LTRIM('슈퍼슈퍼슈가맨', '슈') LTRIM,
' 슈퍼슈가맨',
LTRIM(' 슈퍼슈가맨', ' ') LTRIM,
LTRIM(' 슈퍼슈가맨') LTRIM, -- 디폴트로 '공백' 제거
RTRIM('우측 공백 제거 ') RTRIM,
TRIM(' 슈퍼맨 ') TRIM, -- 좌우 공백 제거
LTRIM('******10000', '*')
FROM dual;
-- p_dept2 테이블에서 부서명(dname) 을 출력하되
-- 오른쪽 끝에 ‘부’ 라는 글자는 제거하고 출력하세요.
SELECT * FROM t_dept2;
SELECT dname, RTRIM(dname, '부') RTRIM예제
FROM t_dept2;
-- REPLACE 함수
SELECT REPLACE('슈퍼맨 슈퍼걸', '슈퍼', '파워') REPLACE
FROM dual;
SELECT REPLACE('아버지가 방에 들어간다', ' ', '')
FROM dual;
-- t_student 테이블에서 102번 학과(deptno1) 의 학생들의 이름을 출력하되
-- 성 부분은 ‘#’ 으로 표시되게 출력하세요 / replace() , substr()사용
SELECT name, REPLACE(name, SUBSTR(name, 1, 1), '#') 학생
FROM t_student
WHERE deptno1 = 102;
-- t_student 테이블에서 101번 학과(deptno1)의 학생들의 이름을
-- 출력하되 가운데 글자만 ‘#’으로 표시되게 출력하세요 / replace() , substr()사용
SELECT name, REPLACE(name, SUBSTR(name, 2, 1), '#') 학생
FROM t_student
WHERE deptno1 = 101;
-- t_student 테이블에서 1전공(deptno1)이 101번인 학생들의
-- 이름과 주민등록번호를 출력하되 주민등록번호의 뒤 7자리는 ‘*’ 로 표시되게 출력
SELECT name, jumin, REPLACE(jumin, SUBSTR(jumin, 7, 7), '*******') 주민번호
FROM t_student
WHERE deptno1 = 101;
-- t_student 테이블에서 다음과 같이 1전공(deptno1)이 102번인
-- 학생들의 이름(name)과 전화번호(tel), 전화번호에서 국번 부분만 ‘#’ 처리하여 출력하세요.
-- 단 모든 국번은 3자리로 간주합니다. replace() / substr() / instr() 사용
SELECT name, tel, REPLACE(tel, SUBSTR(tel, instr(tel, ')', 1) + 1, 3), '###') 전화번호
FROM t_student
WHERE deptno1 = 102;
2) SingleRow2_Null.sql
SELECT * FROM t_professor;
-- null값과의 연산 결과는 null이다!!
SELECT name, pay, bonus
FROM t_professor;
-- 그룹함수에서는 동작, null은 연산에서 제외되어 동작
SELECT SUM(pay), SUM(bonus) FROM t_professor;
-- nvl() 함수
SELECT name, pay, bonus,
pay + bonus 총지급액,
pay + nvl(bonus, 0) 총지급액
FROM t_professor;
-- Professor 테이블 : 101번 학과 교수들의 이름(name), 급여(pay),
-- 보너스(bonus), 연봉(pay) 출력하기. 연봉은 pay * 12 + bonus 로 계산,
-- bonus 가 없는 교수는 0 으로 계산
SELECT name 이름, pay 급여, bonus 보너스,
nvl(bonus, 0) bonus,
pay * 12 + nvl(bonus, 0) 연봉
FROM t_professor WHERE deptno = 101;
SELECT name 이름, pay 급여,
nvl2(bonus, bonus, 0) bonus,
nvl2(bonus, pay * 12 + bonus, pay * 12) 연봉
FROM t_professor WHERE deptno = 101;
3) SingleRow3_Conversion.sql
-- 묵시적 자동 형변환
SELECT 1 + 1 FROM dual;
SELECT '1' + 1 FROM dual; -- 자바와 반대..!!
SELECT to_number('1') + 1 FROM dual;
-- 형변환 함수 : TO_CHAR, TO_NUMBER, TO_DATE
-- ####################################
-- TO_CHAR (날짜 -> 문자)
SELECT
SYSDATE,
TO_CHAR(SYSDATE, 'YYYY') 연도4자리,
TO_CHAR(SYSDATE, 'RRRR') 연도Y2K버그이후,
TO_CHAR(SYSDATE, 'YY') 연도2자리,
TO_CHAR(SYSDATE, 'YEAR') 연도영문
FROM dual;
SELECT
TO_CHAR(SYSDATE, 'DD') 일숫자2자리,
TO_CHAR(SYSDATE, 'DDTH') 몇번째날,
TO_CHAR(SYSDATE, 'DAY') 요일,
TO_CHAR(SYSDATE, 'Dy') 요일앞자리
FROM dual;
SELECT
TO_CHAR(SYSDATE, 'MM') 월2자리,
TO_CHAR(SYSDATE, 'MON') 월3자리,
TO_CHAR(SYSDATE, 'MONTH') 월전체,
TO_CHAR(SYSDATE, 'MON', 'NLS_DATE_LANGUAGE=ENGLISH') 월영문3자리,
TO_CHAR(SYSDATE, 'MONTH', 'NLS_DATE_LANGUAGE=ENGLISH') "월영문전체(대)",
TO_CHAR(SYSDATE, 'month', 'NLS_DATE_LANGUAGE=ENGLISH') "월영문전체(소)",
TO_CHAR(SYSDATE, 'Month', 'NLS_DATE_LANGUAGE=ENGLISH') "월영문전체(첫글자대)"
FROM dual;
SELECT
TO_CHAR(SYSDATE, 'HH24') 시24hr,
TO_CHAR(SYSDATE, 'HH') 시12hr,
TO_CHAR(SYSDATE, 'MI') 분,
TO_CHAR(SYSDATE, 'SS') 초,
TO_CHAR(SYSDATE, 'HH:MI:SS')
FROM dual;
SELECT SYSDATE, TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') 날짜
FROM dual;
SELECT SYSDATE, TO_CHAR(SYSDATE, 'YYYY"년"MM"월"DD"일" HH24"시"MI"분"SS"초"') 날짜
FROM dual;
-- t_student 테이블의 생일(birthday) 이 3월인 학생의 이름(name) 과 생일(birthday) 를
-- 다음과 같은 형식으로 출력하세요 (TO_CHAR 사용)
SELECT name, TO_CHAR(birthday, 'YYYY-MM-DD') 생일
FROM t_student
WHERE TO_CHAR(birthday, 'MM') = '03';
-- TO_CHAR : 대소문자 지정
SELECT
SYSDATE,
TO_CHAR(SYSDATE, 'Dy Month DD, YYYY', 'NLS_DATE_LANGUAGE=ENGLISH') AS A1,
TO_CHAR(SYSDATE, 'dy month dd, YYYY', 'NLS_DATE_LANGUAGE=ENGLISH') AS A2,
TO_CHAR(SYSDATE, 'DY MONTH DD, YYYY', 'NLS_DATE_LANGUAGE=ENGLISH') AS A3
FROM DUAL;
-- TO_CHAR() 함수 - 숫자를 문자로 변환
SELECT
1234,
TO_CHAR(1234, '99999') "9하나당 1자리",
TO_CHAR(1234, '099999') "빈자리 0으로",
TO_CHAR(1234, '$9999') "빈자리 $로",
TO_CHAR(1234.1264, '9999.99') "소숫점이하2자리",
TO_CHAR(123456789, '999,999,999') "천단위에 구분기호"
FROM dual;
-- t_professor 테이블에서 101번 학과 교수들의 이름(name), 연봉(pay) 를 출력하세요,
-- 단, 연봉은 (pay * 12) + bonus로 계산하고 천 단위 구분 기호로 표시하세요.
-- (nvl() / to_char() 사용)
SELECT name, TO_CHAR((pay*12) + nvl(bonus, 0), '99,999') 연봉
FROM t_professor
WHERE deptno=101;
-- ####################################
-- TO_NUMBER() 함수 - 숫자로 변환
SELECT TO_NUMBER('123.45') FROM dual;
-- ####################################
-- TO_DATE() 함수 - 문자 -> 날짜로 변환
SELECT
TO_DATE('2020-04-08', 'YYYY-MM-DD') TO_DATE
FROM dual;
-- t_professor 테이블에서 2000년 이전에 입사한 교수명과 입사일,
-- 현재 연봉과 10% 인상 후 연봉을 아래와 같은 양식으로 출력하세요.
-- 연봉은 보너스(bonus)를 제외한 pay * 12 로 계산하고
-- 연봉과 인상후 연봉은 천단위 구분 기호를 추가하여 출력하세요
SELECT name,
TO_CHAR(hiredate, 'YYYY-MM-DD') 입사일,
TO_CHAR(pay * 12, '99,9999') 연봉,
TO_CHAR(pay * 12 * 1.1, '99,999') 인상후
FROM t_professor
WHERE To_CHAR(hiredate, 'YYYY') < '2000';
4) SingleRow4_Number.sql
-- 숫자 관련 단일행 함수들.
-- ROUND() 함수 : 반올림
SELECT
'ROUND',
ROUND(12.34) "(12.34)",
ROUND(12.536) "(12.536)",
ROUND(12.536, 2) "(12.536, 2)", -- 소숫점 3자리에서 반올림
ROUND(16.345, -1) "(16.345, -1)" -- 1의 자리에서 반올림
FROM dual;
-- TRUNC() 함수 : 자르기
SELECT
'TRUNC',
TRUNC(12.345) "(12.345)", -- 소수점 자름 (디폴트)
TRUNC(12.345, 2) "(12.345, 2)", -- 소수점 3자리부터 자름
TRUNC(12.345, -1) "(12.345, -1)" -- 1의 자리부터 자름
FROM dual;
-- ROUND() TRUNC() CEIL() FLOOR() 함수 비교
SELECT
'12.5' "12.5",
ROUND(12.5) "ROUND",
TRUNC(12.5) "TRUNC",
CEIL(12.5) "CEIL",
FLOOR(12.5) "FLOOR"
FROM dual;
SELECT
'-12.5' "-12.5",
ROUND(-12.5) "ROUND",
TRUNC(-12.5) "TRUNC",
CEIL(-12.5) "CEIL",
FLOOR(-12.5) "FLOOR"
FROM dual;
SELECT
'12.1' "12.1",
ROUND(12.1) "ROUND",
TRUNC(12.1) "TRUNC",
CEIL(12.1) "CEIL",
FLOOR(12.1) "FLOOR"
FROM dual;
-- 오라클은 % 연산자 없음
-- MOD() : 나머지 연산
SELECT
MOD(12, 10) "MOD(12, 10)",
MOD(12.6, 4.1) "MOD(12.6, 4.1)"
FROM dual;
-- POWER() 제곱
SELECT
POWER(3, 2) "POWER(3, 2)",
POWER(-3, 3) "POWER(-3, 3)",
POWER(10, -2) "POWER(10, -2)",
POWER(2, 1/2) "sqrt(2)",
POWER(27, 1/3) "POWER(27, 1/3)"
FROM dual;
5) SingleRow5_DateTime.sql
-- 날짜 함수
SELECT SYSDATE FROM dual;
-- 기본적인 날짜 함수
SELECT
SYSDATE "오늘",
SYSDATE + 1 "내일(24hr뒤)",
SYSDATE - 2 "그저께",
SYSDATE + 1/24 "한시간뒤"
FROM dual;
-- 일자 차이 계산
SELECT
SYSDATE "오늘",
TO_DATE('2020-03-16 09:00:00', 'YYYY-MM-DD hh:mi:ss') "시작한날",
SYSDATE - TO_DATE('2020-03-16 09:00:00', 'YYYY-MM-DD hh:mi:ss') "경과"
FROM dual;
-- MONTHS_BETWEEN : 날짜 사이의 개월수
SELECT
-- 규칙1 : 두 날짜 중 큰 날짜를 먼저 써야 양수 값으로 나옴
MONTHS_BETWEEN('2012-03-01', '2012-01-01') "양수값",
MONTHS_BETWEEN('2012-01-01', '2012-03-01') "음수값",
-- 규칙2: 두 날짜가 같은 달에 속해 있으면 특정 규칙으로 계산된 값
MONTHS_BETWEEN('2012-02-29', '2012-02-01') "2/29-2/1",
MONTHS_BETWEEN('2012-04-30', '2012-04-01') "4/30-4/1",
MONTHS_BETWEEN('2012-05-31', '2012-05-01') "5/31-5/1"
FROM dual;
-- t_professor 테이블에서 오늘(SYSDATE)을 기준으로
-- 근속연수, 근속개월, 근속일를 계산해서 출력
-- 날짜양식은 YYYY-MM-DD로, 근속개월, 근속일은
-- 반올림 하여 소수점 1자리까지 표현
SELECT
name "이름",
TO_CHAR(SYSDATE, 'YYYY-MM-DD') "오늘",
TO_CHAR(hiredate, 'YYYY-MM-DD') "입사일",
TO_CHAR(SYSDATE, 'YYYY') - TO_CHAR(hiredate, 'YYYY') "근속연수",
ROUND(MONTHS_BETWEEN(SYSDATE, hiredate), 1) "근속개월",
ROUND(SYSDATE - hiredate, 1) "근속일"
FROM t_professor;
-- ADD_MONTH() 개월 추가
SELECT SYSDATE, ADD_MONTHS(SYSDATE, 3) FROM dual;
SELECT
SYSDATE,
LAST_DAY(SYSDATE) "이번달 마지막날",
NEXT_DAY(SYSDATE, '월') "다음 월요일"
FROM dual;
-- 날짜의 ROUND() 함수 , 하루의 반은 정오 12:00:00 이다. 이를 넘어서면 다음 날짜
-- 날짜의 TRUNC() 함수, 무조건 당일 출력.
-- 원서 접수나 상품 주문 등에서 오전까지 접수된 건은 당일 접수 처리. 오후접수는 익일 처리 등에서 사용.
SELECT
SYSDATE,
ROUND(SYSDATE),
TRUNC(SYSDATE)
FROM dual;
4. [폴더] 05_AggregateFunction
1) Agg1.sql
SELECT * FROM t_professor;
-- 그룹함수에서는 NULL값은 계산에서 제외
SELECT COUNT(*), COUNT(hpage) FROM t_professor;
SELECT COUNT(bonus), SUM(bonus), AVG(bonus) FROM t_professor;
SELECT MAX(hiredate), MIN(hiredate) FROM t_emp;
-- null 허용 컬럼의 그룹함수 적용시
-- nvl, nvl2 사용해야 함
SELECT AVG(bonus), AVG(NVL(bonus, 0))
FROM t_professor;
-- ★★ 불가능하다! SELECT절에 group 함수와 group 함수가 아닌 것과는 절대로 같이 출력 불가 ★★
-- SELECT deptno, AVG(bonus)
-- FROM t_professor;
-- t_professor 테이블에서 ‘학과별’로 교수들의 평균 보너스를 출력하세요
SELECT deptno, ROUND(AVG(NVL(bonus, 0)), 1) 보너스평균
FROM t_professor
GROUP BY deptno;
-- t_professor 테이블 : 학과별(deptno) 그리고 직급별(position)로
-- 교수들의 평균 급여를 계산하여 출력하세요
SELECT deptno, POSITION, avg(pay) 평균급여
FROM t_professor
GROUP BY deptno, position -- 1. 학과별 그룹핑, 2. 직급별 그룹핑
ORDER BY deptno ASC, POSITION ASC;
-- 부서별 평균 급여를 출력하되, 평균 급여가 450보다 많은 학과만 출력
SELECT deptno, avg(pay) 평균급여
FROM t_professor
--WHERE avg(pay) > 450 -- 그룹함수는 WHERE 절에서 사용 불가
GROUP BY deptno;
-- HAVING : 그룹함수 결과에 대한 조건절
SELECT deptno, avg(pay) 평균급여
FROM t_professor
GROUP BY deptno
HAVING avg(pay) > 300;
-- <SELECT 쿼리문 순서>
-- SELECT
-- FROM
-- WHERE
-- GROUP BY
-- HAVING
-- ORDER BY
-- t_emp 테이블: 매니저별(MGR)로 관리하는 직원들의 ‘매니저’, ‘직원수’와
-- ‘급여총액’과 ‘급여평균’과 ‘교통비 (COMM) 평균’ 지급액 을 출력하세요.
-- 단 사장님은 (job = president)제외
SELECT
mgr 매니저,
COUNT(*) 직원수,
SUM(sal) 급여총액,
TRUNC(AVG(sal)) 급여평균,
AVG(NVL(comm, 0)) 교통비평균
FROM t_emp
WHERE job <> 'president'
GROUP BY mgr;
-- t_professor 테이블 : 직위가 정교수 혹은 조교수 인 분들 중에서 ‘과별(deptno)’로
-- 과번호, 소속교수 총수, 근속일 평균, 급여평균, 보너스 평균을 출력해보세요
SELECT
deptno,
count(*) 총인원,
ROUND(AVG(SYSDATE - hiredate), 1) 근속평균,
AVG(pay) 급여평균,
AVG(NVL(bonus, 0)) 보너스평균
FROM t_professor
WHERE POSITION LIKE '%교수'
GROUP BY deptno;
-- t_student 테이블 : 학과별(deptno1)로, 학과번호,
-- 최대몸무게 - 최소몸무게 차이 값을 출력해보세요
SELECT deptno1 학과, max(weight) - min(weight) 최대최소몸무게차
FROM t_student
GROUP BY deptno1;
-- 그 차이가 30 이상인것만 출력하려면?
SELECT deptno1 학과, max(weight) - min(weight) 최대최소몸무게차
FROM t_student
GROUP BY deptno1
HAVING max(weight) - min(weight) >= 30;
5. [폴더] 06_Join
1) Join1.sql
-- JOIN
-- 20 레코드
-- FORM 절에 테이블에도 별칭(alias)를 줄 수 있다!
SELECT s.studno, s.name, s.deptno1
FROM t_student s;
-- 12 레코드
SELECT d.deptno, d.dname
FROM t_department d;
-- 240 레코드
SELECT *
FROM t_student s, t_department d;
-- 240 레코드에 5개의 컬럼만 뽑아냄..!!
SELECT s.studno, s.name, s.deptno1,
d.deptno, d.dname
FROM t_student s, t_department d; -- Oracle 방식
SELECT s.studno, s.name, s.deptno1,
d.deptno, d.dname
FROM t_student s
CROSS JOIN t_department d; -- ANSI 방식
-- 카티션곱 (Cartesian Product)
-- 두개의 테이블을 JOIN 하게 되면,
-- 각 테이블의 레코드들의 모든 조합이 출력된다.
-- WHERE 나 ON 등으로 JOIN 조건이 주어지지 않으면
-- 모든 카티션곱이 출력된다.
-- Equi Join (등가 Join)
-- Oracle 방식
SELECT s.name "학생이름", s.deptno1 "학과번호", d.dname "학과이름"
FROM t_student s, t_department d
WHERE s.deptno1 = d.deptno; -- 조건(Equi Join)
-- ANSI 방식
SELECT s.name "학생이름", s.deptno1 "학과번호", d.dname "학과이름"
FROM t_student s JOIN t_department d ON s.deptno1 = d.deptno; -- 조건(Equi Join)
SELECT * FROM t_student;
-- 제2전공은?
SELECT s.name "학생이름", s.deptno2 "제2학과", d.dname "제2학과이름"
FROM t_student s, t_department d
WHERE s.deptno2 = d.deptno;
-- t_student 테이블, t_professor 테이블 을 join하여
-- ‘학생이름’, ‘지도교수 번호’, ‘지도교수이름’ 을 출력하세요
SELECT * FROM t_student;
SELECT * FROM t_professor;
-- Oracle 방식
SELECT s.name "학생이름", p.profno "지도교수 번호", p.name "지도교수 이름"
FROM t_student s, t_professor p
WHERE s.profno = p.profno; -- join 조건
-- ANSI 방식
-- JOIN하고 ON은 쌍으로 들어가야 함..!!
SELECT s.name "학생이름", p.profno "지도교수 번호", p.name "지도교수 이름"
FROM t_student s JOIN t_professor p
ON s.profno = p.profno; -- join 조건
-- t_student, t_department, t_professor 테이블을 join하여
-- 학생의 이름, 학과이름, 지도교수 이름 을 출력하세요
SELECT * FROM t_student;
SELECT * FROM t_department;
SELECT * FROM t_professor;
-- Oracle 방식
SELECT s.name "학생이름", d.dname "학과이름", p.name "교수이름"
FROM t_student s, t_department d, t_professor p
WHERE s.deptno1 = d.deptno AND s.profno = p.profno;
-- ANSI 방식
SELECT s.name "학생이름", d.dname "학과이름", p.name "교수이름"
FROM t_student s
JOIN t_department d ON s.deptno1 = d.deptno
JOIN t_professor p ON s.profno = p.profno;
-- t_emp2 직원 테이블과 t_post 직급 테이블을 조회하여
-- 사원의 이름과 직급, 현재연봉, 해당직급의 연봉의 하한금액(s_pay)과
-- 상한금액(e_pay)을 출력하세요
SELECT * FROM t_emp2;
SELECT * FROM t_post;
-- Oracle 방식
SELECT
e.name 사원이름, e.post 현재직급, e.pay 현재연봉,
p.s_pay 하한금액, p.e_pay 상한금액
FROM t_emp2 e, t_post p
WHERE e.post = p.post;
-- ANSI 방식
SELECT
e.name 사원이름, e.post 현재직급, e.pay 현재연봉,
p.s_pay 하한금액, p.e_pay 상한금액
FROM t_emp2 e JOIN t_post p ON e.post = p.post;
-- t_student - t_professor 테이블 join 하여 제1전공(deptno1)이
-- 101번인 학생들의 학생이름과 지도교수 이름을 출력하세요
-- Oracle 방식
SELECT s.name 학생이름, p.name 교수이름
FROM t_student s, t_professor p
WHERE s.profno = p.profno AND s.deptno1 = 101;
-- ANSI 방식
SELECT s.name 학생이름, p.name 교수이름
FROM t_student s JOIN t_professor p ON s.profno = p.profno
WHERE s.deptno1 = 101;
-- JOIN 조건은 반드시 ON으로 해야하나
-- 일반적인 조건은 WHERE을 써도 되고 ON 다음에 AND를 써도 된다
SELECT s.name 학생이름, p.name 교수이름
FROM t_student s JOIN t_professor p ON s.profno = p.profno
AND s.deptno1 = 101;
2) Join2.sql
-- 비등가 조인(Non-Equi Join)
SELECT * FROM t_customer;
SELECT * FROM t_gift;
-- t_customer 테이블, t_gift 테이블을 join하여
-- 고객의 마일리지 포인트별로 받을수 있는 상품을 조회하여
-- 고객의 이름(c_name)과 포인트(c_point), 상품명(g_name)을 출력하세요
-- BETWEEN ~ AND ~ 사용
-- Oracle 방식
SELECT c.c_name 고객명, c.c_point POINT, g.g_name 상품명
FROM t_customer c, t_gift g
WHERE c.c_point BETWEEN g.g_start AND g.g_end;
-- ANSI 방식
SELECT c.c_name 고객명, c.c_point POINT, g.g_name 상품명
FROM t_customer c JOIN t_gift g
ON c.c_point BETWEEN g.g_start AND g.g_end;
-- 앞 예제에서 조회한 상품의
-- 이름(g_name)과 필요수량이 몇개인지 조회
-- (그룹함수 동원되어야 한다)
-- Oracle 방식
SELECT g.g_name 상품명, COUNT(*) 필요수량
FROM t_customer c, t_gift g
WHERE c.c_point BETWEEN g.g_start AND g.g_end
GROUP BY g.g_name;
-- ANSI 방식
SELECT g.g_name 상품명, COUNT(*) 필요수량
FROM t_customer c JOIN t_gift g
ON c.c_point BETWEEN g.g_start AND g.g_end
GROUP BY g.g_name;
-- t_student 테이블과 t_exam01 시험성적 테이블,
-- t_credit 학점 테이블을 조회하여 학생들의 이름과 점수와 학점을 출력하세요
SELECT * FROM t_student;
SELECT * FROM t_exam01;
SELECT * FROM t_credit;
-- Oracle 방식
SELECT s.name 학생이름, e.total 점수, c.grade 학점
FROM t_student s, t_exam01 e, t_credit c
WHERE s.studno = e.studno AND e.total BETWEEN c.min_point AND c.max_point;
-- ANSI 방식
SELECT s.name 학생이름, e.total 점수, c.grade 학점
FROM t_student s
JOIN t_exam01 e ON s.studno = e.studno
JOIN t_credit c ON e.total BETWEEN c.min_point AND c.max_point;
-- t_customer 와 t_gift 테이블 join : 자기 포인트(c_point)보다
-- 낮은 포인트의 상품 중 한가지를 선택할수 있다고 할때 '산악용자전거'를
-- 선택할 수 있는 고객명(c_name)과 포인트(c_point), 상품명(g_name)을 출력하세요
SELECT * FROM t_gift;
-- Oracle 방식
SELECT c.c_name 고객명, c.c_point 뽀인트, g.g_name 상품명
FROM t_customer c, t_gift g
WHERE c.c_point >= g.g_start AND g.g_name = '산악용자전거';
-- ANSI 방식
-- 일반 조건문은 WHERE, AND 둘 다 사용 가능함
SELECT c.c_name 고객명, c.c_point 뽀인트, g.g_name 상품명
FROM t_customer c JOIN t_gift g
ON c.c_point >= g.g_start
WHERE g.g_name = '산악용자전거';
SELECT c.c_name 고객명, c.c_point 뽀인트, g.g_name 상품명
FROM t_customer c JOIN t_gift g
ON c.c_point >= g.g_start
AND g.g_name = '산악용자전거';
-- t_emp2, t_post 테이블 사용하여
-- 사원들의 이름(name)과 나이, 현재직급(post), ‘예상직급’을 출력하세요.
-- ‘예상직급’은 나이로 계산하며 해당 나이가 받아야 하는 직급을 의미합니다.
-- 나이는 오늘(SYSDATE)을 기준으로 하되 소수점 이하는 절삭하여 계산하세요
-- t_emp2 의 직급(post) 은 null 허용함에 주의)
-- 나이계산은 어떻게? (현재연도 - 생년월일연도) + 1,
-- SYSDATE, TO_CHAR() 사용
SELECT * FROM t_emp2;
SELECT * FROM t_post;
-- Oracle 방식
SELECT
e.name 이름,
(TO_CHAR(SYSDATE, 'YYYY') - TO_CHAR(e.birthday, 'YYYY') + 1) "현재나이",
NVL(e.post, ' ') "현재직급", p.post "예상직급"
FROM t_emp2 e, t_post p
WHERE (TO_CHAR(SYSDATE, 'YYYY') - TO_CHAR(e.birthday, 'YYYY') + 1)
BETWEEN p.S_AGE AND p.E_AGE;
-- ANSI 방식
SELECT e.name "이름",
(TO_CHAR(SYSDATE, 'YYYY') - TO_CHAR(e.birthday, 'YYYY') + 1) "현재나이",
NVL(e.post, ' ') "현재직급", p.post "예상직급"
FROM t_emp2 e INNER JOIN t_post p
ON (TO_CHAR(SYSDATE, 'YYYY') - TO_CHAR(e.birthday, 'YYYY') + 1)
BETWEEN p.S_AGE AND p.E_AGE ;
-- OUTER Join (아우터 조인) : Oracle 방식은 없어..!!!
-- (ANSI 구문 only)
-- t_student 테이블과 t_professor 테이블 Join 하여
-- 학생이름과 지도교수 이름을 출력하세요.
-- 단! 지도교수가 결정되지 않은 학생의 명단도 함께 출력하세요
-- 기존의 INNER Join 방식으로 먼저 만들어 보고
-- OUTER Join 을 해보고 비교해보자
SELECT s.name 학생이름, p.name 교수이름
FROM t_student s LEFT OUTER JOIN t_professor p
ON s.profno = p.profno;
SELECT s.name 학생이름, p.name 교수이름
FROM t_student s RIGHT OUTER JOIN t_professor p
ON s.profno = p.profno;
-- t_student, t_professor 테이블 join :
-- 학생이름과 지도교수 이름을 출력,
-- 단! 지도 학생이 결정되지 않은 교수 명단도 출력
SELECT s.name 학생이름, p.name 교수이름
FROM t_student s FULL OUTER JOIN t_professor p
ON s.profno = p.profno;
-- t_student, t_professor 테이블 join :
-- 학생이름과 지도교수 이름을 출력,
-- 단! 지도 학생이 결정되지 않은 교수 명단,
-- 지도교수가 결정되지 않은 학생명단 모두 출력
-- ** ORACLE 에는 FULL OUTER 가 있는데 MySql 에는 없다..
-- UNION 과 조합해야 함
SELECT s.name "학생이름", p.name "교수이름"
FROM t_student s FULL OUTER JOIN t_professor p
ON s.profno = p.PROFNO;
-- SELF Join
-- 만약 원하는 데이터가 하나의 테이블에 다 들어 있을때 사용...!!
-- t_dept2 테이블에서 부서명 과 그 부서의 상위부서명을 출력하세요
-- Oracle 방식
SELECT d1.dname 부서명, d2.dname 상위부서명
FROM t_dept2 d1, t_dept2 d2
WHERE d1.pdept = d2.dcode;
-- ANSI 방식
SELECT d1.dname 부서명, d2.dname 상위부서명
FROM t_dept2 d1 JOIN t_dept2 d2
ON d1.pdept = d2.dcode;
-- t_professor 테이블 : 교수번호, 교수이름, 입사일,
-- 그리고 자신보다 입사일 빠른 사람의 인원수를 출력하세요,
-- 단 자신보다 입사일이 빠른 사람수를 오름차순으로 출력하세요
-- hint: left outer 사용 / 그룹함수 사용
-- left outer 사용해야하는 이유는 조인형 교수님 때문임..!!
SELECT * FROM t_professor;
SELECT
a.profno "교수번호", a.name "교수명", a.HIREDATE "입사일",
count(b.hiredate) "빠른분들"
FROM
t_professor a LEFT OUTER JOIN t_professor b
ON b.HIREDATE < a.HIREDATE
GROUP BY
a.profno, a.name, a.HIREDATE
ORDER BY 4;
6. INNER Join(이너 조인), OUTER Join(아우터 조인)
** INNER Join : Join에 참여하는 모든 테이블에 데이터가 존재하는 경우에만 결과 값을 출력
** OUTER Join : 한쪽 테이블에 데이터가 있고 한쪽 테이블에 없는 경우 데이터가 있는 쪽 테이블의 내용을 전부 출력
** 필연적으로 OUTER Join 은 DB 성능에 나쁜 영향 줌.
7. equi join(등가조인), non-equi join(비등가조인) -> INNER JOIN
8. LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN -> OUTER JOIN
'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글
2020.04.10 (0) | 2020.04.10 |
---|---|
2020.04.09 (0) | 2020.04.09 |
2020.04.07 (0) | 2020.04.07 |
2020.04.06 (0) | 2020.04.06 |
2020.04.03 (0) | 2020.04.03 |