JDBC 연습1

 

▼ 코드 예시 보기 (더보기 클릭) ▼
더보기
더보기

 

 

class JDBCTemplate
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCTemplate {

	public static Connection getConnection() {
		
		Connection con = null;
		Properties prop = new Properties();
		
		try {
			prop.load(new FileReader("config/connection-info.properties"));
			
			String driver = prop.getProperty("driver");
			String url = prop.getProperty("url");
			
			Class.forName(driver);
			
			con = DriverManager.getConnection(url, prop);
			
			/* Application1에서 getAutoCommit()의 결과 true - 기본 값
			 * 프로그램 내에서 commit과 rollback을 판단하여 수행하고자 하므로 setAutoCommit(false) 설정
			 *  */
			con.setAutoCommit(false);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return con;
	}
	
	public static void close(Connection con) {
		
		try {
			if(con != null && !con.isClosed()) {
				con.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	}
	
	public static void close(Statement stmt) {
		try {
			if(stmt != null && !stmt.isClosed()) {
				stmt.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public static void close(ResultSet rset) {
		try {
			if(rset != null && !rset.isClosed()) {
				rset.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/* 자동 commit에서 수동 commit으로 설정 변경 후 코드 추가 */
	public static void commit(Connection con) {
		try {
			if(con != null && !con.isClosed()) {
				con.commit();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public static void rollback(Connection con) {
		try {
			if(con != null && !con.isClosed()) {
				con.rollback();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	
	
	
	
	
}

 

 

Application1
package com.greedy.section01.transaction;

import static com.greedy.common.JDBCTemplate.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;

public class Application1 {

	public static void main(String[] args) {
		
		Connection con = getConnection();
		
		try {
			System.out.println("autoCommit의 현재 설정 값 : " + con.getAutoCommit());
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		PreparedStatement pstmt = null;
		int result = 0;
		
		Properties prop = new Properties();
		
		try {
			prop.loadFromXML(new FileInputStream("mapper/menu-query.xml"));
			
			String query = prop.getProperty("insertMenu");
			
			pstmt = con.prepareStatement(query);
			pstmt.setString(1, "정어리비빔밥");
			pstmt.setInt(2, 50000);
			pstmt.setInt(3, 4);
			pstmt.setString(4, "Y");
			
			result = pstmt.executeUpdate();
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(pstmt);
			close(con);
		}
		
		if(result > 0) {
			System.out.println("메뉴 등록 성공!");
		} else {
			System.out.println("메뉴 등록 실패!");
		}
		
	}

}

 

 

Application2
package com.greedy.section01.transaction;

import static com.greedy.common.JDBCTemplate.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;

public class Application2 {

	public static void main(String[] args) {
		
		Connection con = getConnection();
		
		try {
			System.out.println("autoCommit의 현재 설정 값 : " + con.getAutoCommit());
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		PreparedStatement pstmt1 = null;
		PreparedStatement pstmt2 = null;
		
		int result1 = 0;
		int result2 = 0;
		
		Properties prop = new Properties();
		
		try {
			prop.loadFromXML(new FileInputStream("mapper/menu-query.xml"));
			
			String query1 = prop.getProperty("insertCategory");
			String query2 = prop.getProperty("insertMenu");
			
			pstmt1 = con.prepareStatement(query1);
			pstmt1.setString(1, "기타");
			pstmt1.setInt(2, 1);
			
			result1 = pstmt1.executeUpdate();
			
			System.out.println("result1 : " + result1);
			
			pstmt2 = con.prepareStatement(query2);
			pstmt2.setString(1, "정어리비빔밥");
			pstmt2.setInt(2, 50000);
			/* TBL_CATEGORY 에 존재하지 않는 CATEGORY_CODE를 TBL_MENU 테이블의 CATEGORY_CODE 값으로 삽입하려고 하면
			 * 부모 키를 찾지 못하는 외래키 제약조건 위반 오류가 발생한다.
			 * */
			pstmt2.setInt(3, 0);
			pstmt2.setString(4, "Y");
			
			result2 = pstmt2.executeUpdate();
			
			System.out.println("result2 : " + result2);
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(pstmt1);
			close(pstmt2);
			
			/* 트랜잭션(논리적인 기능 수행 단위) 관리를 위해 2개의 insert가 모두 잘 동작했는지 판단하여
			 * 잘 동작했을 경우 commit, 둘 중 하나라도 잘 동작하지 않았을 경우 rollback을 수행한다.
			 * */
			if(result1 > 0 && result2 > 0) {
				System.out.println("신규 카테고리와 메뉴 등록 성공!");
				commit(con);
			} else {
				System.out.println("신규 카테고리와 메뉴 등록 실패!");
				rollback(con);
			}
			
			close(con);
		}
	
		
	}

}

 

 

 

 

 

 

 

 

JDBC 연습2

 

▼ 코드 예시 보기 (더보기 클릭) ▼
더보기
더보기

 

 

class MenuDAO
import static com.greedy.common.JDBCTemplate.close;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import com.greedy.section02.service.model.dto.CategoryDTO;
import com.greedy.section02.service.model.dto.MenuDTO;

public class MenuDAO {
	
	private Properties prop = new Properties();
	
	public MenuDAO() {
		try {
			prop.loadFromXML(new FileInputStream("mapper/menu-query.xml"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/* 신규 카테고리 등록용 메소드 */
	public int insertNewCategory(Connection con, CategoryDTO newCategory) {
		
		PreparedStatement pstmt = null;
		int result = 0;
		String query = prop.getProperty("insertCategory");
		
		try {
			pstmt = con.prepareStatement(query);
			pstmt.setString(1, newCategory.getName());
			pstmt.setObject(2, newCategory.getRefCategoryCode());
			
			result = pstmt.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(pstmt);
		}
		
		return result;
	}

	/* 현재 카테고리 코드 조회 */
	public int selectLastCategoryCode(Connection con) {
		
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		int newCategoryCode = 0;
		
		String query = prop.getProperty("getCurrentSequence");
		
		try {
			pstmt = con.prepareStatement(query);
			rset = pstmt.executeQuery();
			
			if(rset.next()) {
				newCategoryCode = rset.getInt("CURRVAL");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(rset);
			close(pstmt);
		}
		
		return newCategoryCode;
	}

	/* 신규 메뉴 등록용 메소드 */
	public int insertNewMenu(Connection con, MenuDTO newMenu) {
		
		PreparedStatement pstmt = null;
		int result = 0;
		String query = prop.getProperty("insertMenu");
		
		try {
			pstmt = con.prepareStatement(query);
			pstmt.setString(1, newMenu.getName());
			pstmt.setInt(2, newMenu.getPrice());
			pstmt.setInt(3, newMenu.getCategoryCode());
			pstmt.setString(4, newMenu.getOrderableStatus());
			
			result = pstmt.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(pstmt);
		}
		
		return result;
	}
	
	
	
	
	
	
	

}

 

 

CategoryDTO
public class CategoryDTO {
	
	private int code;
	private String name;
	private Integer refCategoryCode;
	
	public CategoryDTO() {}

	public CategoryDTO(int code, String name, Integer refCategoryCode) {
		super();
		this.code = code;
		this.name = name;
		this.refCategoryCode = refCategoryCode;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getRefCategoryCode() {
		return refCategoryCode;
	}

	public void setRefCategoryCode(Integer refCategoryCode) {
		this.refCategoryCode = refCategoryCode;
	}

	@Override
	public String toString() {
		return "CategoryDTO [code=" + code + ", name=" + name + ", refCategoryCode=" + refCategoryCode + "]";
	}
	
	
}

 

 

class MenuDTO
/* DTO(Data Transfer Object) */
public class MenuDTO {

	private int code;
	private String name;
	private int price;
	private int categoryCode;
	private String orderableStatus;
	
	public MenuDTO() {}

	public MenuDTO(int code, String name, int price, int categoryCode, String orderableStatus) {
		super();
		this.code = code;
		this.name = name;
		this.price = price;
		this.categoryCode = categoryCode;
		this.orderableStatus = orderableStatus;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public int getCategoryCode() {
		return categoryCode;
	}

	public void setCategoryCode(int categoryCode) {
		this.categoryCode = categoryCode;
	}

	public String getOrderableStatus() {
		return orderableStatus;
	}

	public void setOrderableStatus(String orderableStatus) {
		this.orderableStatus = orderableStatus;
	}

	@Override
	public String toString() {
		return "MenuDTO [code=" + code + ", name=" + name + ", price=" + price + ", categoryCode=" + categoryCode
				+ ", orderableStatus=" + orderableStatus + "]";
	}
}

 

 

 

class MenuService
import java.sql.Connection;

import com.greedy.section02.service.model.dao.MenuDAO;
import com.greedy.section02.service.model.dto.CategoryDTO;
import com.greedy.section02.service.model.dto.MenuDTO;

import static com.greedy.common.JDBCTemplate.*;

/* Service의 역할
 * 1. Connection 생성
 * 2. DAO의 메소드 호출 
 * 3. 트랜잭션의 제어
 * 4. Connection 닫기
 * */
public class MenuService {

	/* 신규 메뉴 등록용 서비스 메소드 */
	public void registNewMenu() {
		
		/* 1. Connection 생성 */
		Connection con = getConnection();
		
		/* 2. DAO 메소드 호출 */
		MenuDAO menuDAO = new MenuDAO();
		
		/* 2-1. 카테고리 등록 */
		CategoryDTO newCategory = new CategoryDTO();
		newCategory.setName("기타");
		newCategory.setRefCategoryCode(null);
		
		int result1 = menuDAO.insertNewCategory(con, newCategory);
		
		/* 방금 입력한 마지막 카테고리 번호 조회 */
		int newCategoryCode = menuDAO.selectLastCategoryCode(con);
		
		/* 2-2. 메뉴 등록 */
		MenuDTO newMenu = new MenuDTO();
		newMenu.setName("메롱메롱스튜");
		newMenu.setPrice(40000);
		newMenu.setCategoryCode(newCategoryCode);
		newMenu.setOrderableStatus("Y");
		
		int result2 = menuDAO.insertNewMenu(con, newMenu);
		
		/* 3. 트랜잭션 제어 */
		if(result1 > 0 && result2 > 0) {
			System.out.println("신규 카테고리와 메뉴를 추가하였습니다.");
			commit(con);
		} else {
			System.out.println("신규 카테고리와 메뉴를 추가하지 못했습니다.");
			rollback(con);
		}
		
		/* 4. Connection 반납 */
		close(con);
		
	}

}

 

 

Application
import com.greedy.section02.service.model.service.MenuService;

public class Application {

	public static void main(String[] args) {
		
		new MenuService().registNewMenu();
		
	}

}

 

 

 

 

 

 

 

 

 

JDBC 연습3

 

▼ 코드 예시 보기 (더보기 클릭) ▼
더보기
더보기

 

 

order-query
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<entry key="selectAllCategory">
		SELECT
		       A.CATEGORY_CODE
		     , A.CATEGORY_NAME
		  FROM TBL_CATEGORY A
	</entry>
	<entry key="selectMenuByCategory">
        SELECT
               A.MENU_CODE
             , A.MENU_NAME
             , A.MENU_PRICE
             , A.CATEGORY_CODE
             , A.ORDERABLE_STATUS
          FROM TBL_MENU A
         WHERE A.ORDERABLE_STATUS = 'Y'
           AND A.CATEGORY_CODE = ?	
	</entry>
	
	
	
	
	
</properties>

 

 

class JDBCTemplate 


import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCTemplate {

	public static Connection getConnection() {
		
		Connection con = null;
		Properties prop = new Properties();
		
		try {
			prop.load(new FileReader("config/connection-info.properties"));
			
			String driver = prop.getProperty("driver");
			String url = prop.getProperty("url");
			
			Class.forName(driver);
			
			con = DriverManager.getConnection(url, prop);
			
			/* Application1에서 getAutoCommit()의 결과 true - 기본 값
			 * 프로그램 내에서 commit과 rollback을 판단하여 수행하고자 하므로 setAutoCommit(false) 설정
			 *  */
			con.setAutoCommit(false);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return con;
	}
	
	public static void close(Connection con) {
		
		try {
			if(con != null && !con.isClosed()) {
				con.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	}
	
	public static void close(Statement stmt) {
		try {
			if(stmt != null && !stmt.isClosed()) {
				stmt.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public static void close(ResultSet rset) {
		try {
			if(rset != null && !rset.isClosed()) {
				rset.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/* 자동 commit에서 수동 commit으로 설정 변경 후 코드 추가 */
	public static void commit(Connection con) {
		try {
			if(con != null && !con.isClosed()) {
				con.commit();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public static void rollback(Connection con) {
		try {
			if(con != null && !con.isClosed()) {
				con.rollback();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	
	
	
	
	
}

 

 

class OrderDAO

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import static com.greedy.common.JDBCTemplate.close;
import com.greedy.section01.model.dto.CategoryDTO;
import com.greedy.section01.model.dto.MenuDTO;
import com.greedy.section01.model.dto.OrderDTO;
import com.greedy.section01.model.dto.OrderMenuDTO;

public class OrderDAO {
	
	private Properties prop = new Properties();
	
	public OrderDAO() {
		try {
			prop.loadFromXML(new FileInputStream("mapper/order-query.xml"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/* 모든 카테고리 조회용 메소드 */
	public List<CategoryDTO> selectAllCategory(Connection con) {
		
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		
		List<CategoryDTO> categoryList = null;
		
		String query = prop.getProperty("selectAllCategory");
		
		try {
			pstmt = con.prepareStatement(query);
			rset = pstmt.executeQuery();
			
			categoryList = new ArrayList<>();
			
			while(rset.next()) {
				CategoryDTO category = new CategoryDTO();
				category.setCode(rset.getInt("CATEGORY_CODE"));
				category.setName(rset.getString("CATEGORY_NAME"));
				
				categoryList.add(category);
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(rset);
			close(pstmt);
		}
		
		return categoryList;
	}
	
	/* 카테고리별 메뉴 조회용 메소드 */
	public List<MenuDTO> selectMenuByCategory(Connection con, int categoryCode){
		
		PreparedStatement pstmt = null;
		ResultSet rset = null;
		
		List<MenuDTO> menuList = null;
		
		String query = prop.getProperty("selectMenuByCategory");
		
		try {
			pstmt = con.prepareStatement(query);
			pstmt.setInt(1, categoryCode);
			
			rset = pstmt.executeQuery();
			
			menuList = new ArrayList<>();
			
			while(rset.next()) {
				MenuDTO menu = new MenuDTO();
				menu.setCode(rset.getInt("MENU_CODE"));
				menu.setName(rset.getString("MENU_NAME"));
				menu.setPrice(rset.getInt("MENU_PRICE"));
				menu.setCategoryCode(rset.getInt("CATEGORY_CODE"));
				menu.setOrderableStatus(rset.getString("ORDERABLE_STATUS"));
				
				menuList.add(menu);
			}
		
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(rset);
			close(pstmt);
		}
		
		return menuList;
	}

	/* 주문 정보 입력용 메소드 */
	public int insertOrder(Connection con, OrderDTO order) {
		
		PreparedStatement pstmt = null;
		int result = 0;
		
		String query = prop.getProperty("insertOrder");
		
		try {
			pstmt = con.prepareStatement(query);
			pstmt.setString(1, order.getDate());
			pstmt.setString(2, order.getTime());
			pstmt.setInt(3, order.getTotalOrderPrice());
			
			result = pstmt.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(pstmt);
		}
		
		return result;
	}

	/* 주문 메뉴별 입력용 메소드 */
	public int insertOrderMenu(Connection con, OrderMenuDTO orderMenu) {
		
		PreparedStatement pstmt = null;
		int result = 0;
		
		String query = prop.getProperty("insertOrderMenu");
		
		try {
			pstmt = con.prepareStatement(query);
			pstmt.setInt(1, orderMenu.getMenuCode());
			pstmt.setInt(2, orderMenu.getOrderAmount());
			
			result = pstmt.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(pstmt);
		}

		return result;
	}
	
	
}

 

 

 class CategoryDTO
public class CategoryDTO {
	
	private int code;
	private String name;
	private Integer refCategoryCode;
	
	public CategoryDTO() {}

	public CategoryDTO(int code, String name, Integer refCategoryCode) {
		super();
		this.code = code;
		this.name = name;
		this.refCategoryCode = refCategoryCode;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getRefCategoryCode() {
		return refCategoryCode;
	}

	public void setRefCategoryCode(Integer refCategoryCode) {
		this.refCategoryCode = refCategoryCode;
	}

	@Override
	public String toString() {
		return "CategoryDTO [code=" + code + ", name=" + name + ", refCategoryCode=" + refCategoryCode + "]";
	}
	
	
}

 

 

class MenuDTO
/* DTO(Data Transfer Object) */
public class MenuDTO {

	private int code;
	private String name;
	private int price;
	private int categoryCode;
	private String orderableStatus;
	
	public MenuDTO() {}

	public MenuDTO(int code, String name, int price, int categoryCode, String orderableStatus) {
		super();
		this.code = code;
		this.name = name;
		this.price = price;
		this.categoryCode = categoryCode;
		this.orderableStatus = orderableStatus;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public int getCategoryCode() {
		return categoryCode;
	}

	public void setCategoryCode(int categoryCode) {
		this.categoryCode = categoryCode;
	}

	public String getOrderableStatus() {
		return orderableStatus;
	}

	public void setOrderableStatus(String orderableStatus) {
		this.orderableStatus = orderableStatus;
	}

	@Override
	public String toString() {
		return "MenuDTO [code=" + code + ", name=" + name + ", price=" + price + ", categoryCode=" + categoryCode
				+ ", orderableStatus=" + orderableStatus + "]";
	}
}

 

 

class OrderService


import java.sql.Connection;
import java.util.List;

import com.greedy.section01.model.dao.OrderDAO;
import com.greedy.section01.model.dto.CategoryDTO;
import com.greedy.section01.model.dto.MenuDTO;
import com.greedy.section01.model.dto.OrderDTO;
import com.greedy.section01.model.dto.OrderMenuDTO;

import static com.greedy.common.JDBCTemplate.*;

public class OrderService {
	
	private OrderDAO orderDAO = new OrderDAO();

	/* 카테고리 전체 조회용 메소드 */
	public List<CategoryDTO> selectAllCategory() {
		
		/* 1. Connection 생성 */
		Connection con = getConnection();
		
		/* 2. DAO의 모든 카테고리 조회용 메소드를 호출하여 결과 리턴 받기 */
		List<CategoryDTO> categoryList = orderDAO.selectAllCategory(con);
		
		/* 3. 커넥션 닫기 (select는 commit, rollback X) */
		close(con);
		
		/* 4. 반환 받은 값 리턴하기 */
		return categoryList;
	}

	/* 카테고리별 메뉴 조회용 메소드 */
	public List<MenuDTO> selectMenuByCategory(int categoryCode) {
		/* 1. Connection 생성 */
		Connection con = getConnection();
		
		/* 2. DAO의 해당 카테고리 메뉴를 조회하는 메소드로 categoryCode 전달하며 조회 */
		List<MenuDTO> menuList = orderDAO.selectMenuByCategory(con, categoryCode);
		
		/* 3. Connection 닫기 */
		close(con);
		
		/* 4. 반환받은 값 리턴하기 */
		return menuList;
	}

	/* 주문 정보 등록용 메소드 */
	public int registOrder(OrderDTO order) {
		/* 1. Connection 생성 */
		Connection con = getConnection();
		
		/* 2. 리턴할 값 초기화 */
		int result = 0;
		
		/* 3. DAO 메소드로 전달 받은 값 넘겨서 insert */
		/* 3-1. Order table insert */
		int orderResult = orderDAO.insertOrder(con, order);
		
		/* 3-2. Order Menu table insert */
		List<OrderMenuDTO> orderMenuList = order.getOrderMenuList();
		int orderMenuResult = 0;
		for(OrderMenuDTO orderMenu : orderMenuList) {
			orderMenuResult += orderDAO.insertOrderMenu(con, orderMenu);
		}
		
		/* 4. 성공 여부 판단 후 트랜잭션 처리 */
		if(orderResult > 0 && orderMenuResult == orderMenuList.size()) {
			commit(con);
			result = 1;
		} else {
			rollback(con);
		}
		
		/* 5. Connection 닫기 */
		close(con);
		
		/* 6. 결과 값 반환 */
		return result;
	}

	
}

 

 

class Application
import com.greedy.section01.view.OrderMenu;

public class Application {

	public static void main(String[] args) {
		new OrderMenu().displayMenu();
	}

}

 

 

class OrderMenu


import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import com.greedy.section01.model.dto.CategoryDTO;
import com.greedy.section01.model.dto.MenuDTO;
import com.greedy.section01.model.dto.OrderDTO;
import com.greedy.section01.model.dto.OrderMenuDTO;
import com.greedy.section01.model.service.OrderService;

public class OrderMenu {
	
	private OrderService orderService = new OrderService();

	public void displayMenu() {
		
		/* 반복
		 * ------------------------
		 * 1. 카테고리 조회
		 * 2. 해당 카테고리의 메뉴 조회
		 * 3. 사용자에게 어떤 메뉴를 주문 받을 것인지 입력
		 * 4. 주문할 수량 입력
		 * ------------------------
		 * 5. 주문 
		 * */
		
		Scanner sc = new Scanner(System.in);
		
		List<OrderMenuDTO> orderMenuList = new ArrayList<>();
		int totalOrderPrice = 0;
		
		do {
			System.out.println("=============== 음식 주문 프로그램 ==================");
			
			List<CategoryDTO> categoryList = orderService.selectAllCategory();
			for(CategoryDTO category : categoryList) {
				System.out.println(category.getName());
			}
			
			System.out.println("=================================================");
			System.out.print("주문하실 카테고리를 선택해주세요 : ");
			String inputCategory = sc.nextLine();
			
			int categoryCode = 0;
			for(CategoryDTO category : categoryList) {
				if(category.getName().equals(inputCategory)) {
					categoryCode = category.getCode();
				}
			}
			
			System.out.println("================ 주문 가능 메뉴 ====================");
			List<MenuDTO> menuList = orderService.selectMenuByCategory(categoryCode);
			for(MenuDTO menu : menuList) {
				System.out.println(menu);
			}
			
			System.out.print("주문하실 메뉴를 선택해주세요 : ");
			String inputMenu = sc.nextLine();
			
			int menuCode = 0;
			int menuPrice = 0;
			
			for(int i = 0; i < menuList.size();  i++) {
				MenuDTO menu = menuList.get(i);
				if(menu.getName().equals(inputMenu)) {
					menuCode = menu.getCode();
					menuPrice = menu.getPrice();
				}
			}
			
			System.out.print("주문하실 수량을 입력하세요 : ");
			int orderAmount = sc.nextInt();
			
			OrderMenuDTO orderMenu = new OrderMenuDTO();
			orderMenu.setMenuCode(menuCode);
			orderMenu.setOrderAmount(orderAmount);
			
			orderMenuList.add(orderMenu);
			totalOrderPrice += (menuPrice * orderAmount);
			
			System.out.print("계속 주문하시겠습니까?(예/아니오) : ");
			sc.nextLine();
			boolean isContinue = sc.nextLine().equals("예") ? true : false;
			
			if(!isContinue) break;
			
		} while(true);
		
		for(OrderMenuDTO orderMenu : orderMenuList) {
			System.out.println(orderMenu);
		}
		
		java.util.Date orderTime = new java.util.Date();
		SimpleDateFormat dateFormat = new SimpleDateFormat("yy/MM/dd");
		SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
		String date = dateFormat.format(orderTime);
		String time = timeFormat.format(orderTime);
		
		OrderDTO order = new OrderDTO();
		order.setDate(date);
		order.setTime(time);
		order.setTotalOrderPrice(totalOrderPrice);
		order.setOrderMenuList(orderMenuList);
		
		int result = orderService.registOrder(order);
		
		if(result > 0) {
			System.out.println("주문에 성공하셨습니다.");
		} else {
			System.out.println("주문에 실패하셨습니다.");
		}
	}

}

 

 

 

 

 

 

 

'Programming > JDBC' 카테고리의 다른 글

JDBC 응용 연습(2)  (0) 2022.02.07
JDBC 개요  (0) 2022.01.28

 

 

JDBC

 

 

 

 

 

JDBC(Java DataBase Connectivity)란?



자바에서 데이터베이스에 접근할 수 있게 해주는 Programming API

 

 

 

 

 

JDBC 사용 클래스


DriverManager
데이터 원본에 JDBC드라이버를 통하여 커넥션을 만드는 역할
Class.forName() 메소드를 통해 생성되며 반드시 예외처리를 해야 함
직접 인스턴스 생성이 불가능하고 getConnection() 메소드를 사용하여 인스턴스 생성 가능

 

Connection
특정 데이터 원본과 연결 된 커넥션을 나타내며 Statement 인스턴스를 생성할 때도
Connection 인스턴스를 사용하여 createStatement() 메소드를 호출하여 생성
SQL문장을 실행시키기 전에 우선 Connection 인스턴스가 있어야 함

 

Statement
Connection 클래스의 createStatement() 메소드를 호출하여 얻어지며 생성 된
Statement 인스턴스로 SQL 질의문을 String에 담아 인자로 전달하여 executeQuery()
메소드를 호출하여 SQL 질의 수행
ex) 

try {
String query = "SELECT ID, LAST_NAME FROM EMP";
stmt = conn.createStatement();
rset = stmt.executeQuery(query);
} catch (SQLException e) {
e.printStackTrace();
}

 

PreparedStatement
Connection 클래스의 preparedStatement() 메소드를 사용하여 인스턴스 생성
SQL 질의문을 위치홀더(placeholder)인 ?로 표현되는 String으로 정의 PreparedStatement는 위치홀더라는 개념에 해당되는 인수가 많아서 특정 값만 바꾸어 여러 번 실행해야 할 때 사용하면 유용함
(Statement는 SQL문장을 매번 컴파일 하지만 PreparedStatement는 한번만 컴파일 하므로 실행 속도가 빠름)
ex)

try {
String query = "INSERT INTO MEMBER VALUES(?,?)";
pstmt = conn.preparedStatement(query);
pstmt.setString(1, id);
pstmt.setString(2, password);
} catch (SQLException e) {
e.printStackTrace();
}

 

ResultSet
SELECT문을 사용한 질의 성공 시 ResultSet을 반환
SQL 질의문에 의해 생성 된 테이블을 담고 있으며 커서(cursor)로 특정 행에 대한 참조 조작
String id = rset.getString("ID");
String pwd = rset.getString(2);

 

 

 

 

 

 

JDBC 코딩 절차


 

 

 

해당 Driver로부터 Connection instance 획득

 

 

 

 

Statement에서 제공하는 메소드를 사용하여 SQL문 실행

 

 

 

 

Statement에서 제공하는 메소드를 사용하여 SQL문 실행

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Programming > JDBC' 카테고리의 다른 글

JDBC 응용 연습(2)  (0) 2022.02.07
JDBC 응용 연습(1)  (0) 2022.02.04

 

 

ORACLE OBJECT(SYNONYM)

 

 

 

 

 

 

SYNONYM


  • 사용자가 다른 사용자의 객체를 참조할 때 [사용자ID].[테이블명]으로 표시해야한다.
  • 이처럼 길게 표현되는 것을 동의어(SYNONYM)로 설정하고 간단히 사용할 수 있다.

 

 

1. 비공개 동의어
  • 객체에 대한 접근 권한을 부여받은 사용자가 정의한 동의어
  • 해당 사용자만 사용할 수 있다.

 

2. 공개 동의어
  • -권한을 주는 사용자(DBA)가 정의한 동의어
  • -모든 사용자가 사용할 수 있다.(PUBLIC)
    ex) DUAL

 

CREATE SYNONYM EMP FOR EMPLOYEE;

 

-- SYSTME 계정
	GRANT CREATE SYNONYM TO EMPLOYEE;

-- EMPLOYEE 계정
	CREATE SYNONYM EMP FOR EMPLOYEE;

 

    SELECT
   		 *
    FROM EMPLOYEE;

 

 

    SELECT
   		 *
    FROM EMP;

 

-- SYSTME 계정
CREATE PUBLIC SYNONYM DEPT FOR EMPLOYEE.DEPARTMENT;


-- SYSTME 계정

    SELECT
        *
    FROM EMPLOYEE.DEPARTMENT;

    SELECT
        *
    FROM DEPT;

 

EMPLOYEE 계정

    SELECT
   	 *
    FROM DEPARTMENT;

    SELECT
    	*
    FROM DEPT;

 

 

-- SYSTME 계정
	DROP PUBLIC SYNONYM DEPT;


-- EMPLOYEE 계정
	DROP SYNONYM EMP;

 

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기

 

 

-- 동의어(SYNONYM)
-- 다른 데이터베이스가 가진 객체에 대한 별명 혹은 줄임말
-- 여러 사용자가 테이블을 공유할 경우
-- 다른 사용자가 테이블에 접근할 때 '사용자명.테이블명'으로 표현하는데
-- 동의어를 사용하면 간단하게 사용할 수 있다.

-- 생성 방법
-- CREATE SYNONYM 줄임말 FOR 사용자명.객체명;
CREATE SYNONYM EMP FOR EMPLOYEE;


SELECT
       E.*
  FROM EMP E;

-- <시스템 계정으로 실행>
GRANT CREATE SYNONYM TO C##EMPLOYEE;

 

-- 동의어의 구분
-- 1. 비공개 동의어
-- 객체에 대한 접근 권한을 부여 받은 사용자가 정의한 동의어
-- 2. 공개 동의어
-- 모든 권한을 주는 사용자가 정의한 동의어
-- 모든 사용자가 사용할 수 있음(PUBLIC)

-- <시스템 계정으로 실행>
CREATE PUBLIC SYNONYM DEPT FOR C##EMPLOYEE.DEPARTMENT;

-- 공개 동의어로 설정되어 시스템 계정에서도 조회 가능
SELECT
       D.*
  FROM DEPT D;
-- 비공개 동의어로 설정되어 시스템 계정에서는 조회 불가
SELECT
       E.*
  FROM EMP E;

 

 

 

 

 

 

 

 

권한과 ROLE


 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기

 

-- 권한과 ROLE

-- <사용자 관리>
-- : 사용자의 계정과 암호설정, 권한 부여

-- 보안을 위한 데이터베이스 관리자
-- : 사용자가 데이터베이스의 객체(테이블, 뷰 등)에 대해
--   특정 권한을 가질 수 있게 하는 권한이 있음
--   다수의 사용자가 공유하는 데이터베이스 정보에 대한 보안 설정
--   데이터베이스에 접근하는 사용자마다 서로 다른 권한과 롤을 부여함

-- 내가 다른 사용자에게 부여한 객체 권한을 조회
-- <SYSTEM 계정으로 실행>
SELECT
       UTPR.*
  FROM USER_TAB_PRIVS_RECD UTPR;
  
-- 나에게 부여된 객체 권한, 객체 이름을 조회
SELECT
       UTPM.*
  FROM USER_TAB_PRIVS_MADE UTPM;

 

-- 1. 시스템 권한 : 데이터베이스 관리자가 가지고 있는 권한으로
--                오라클 접속, 테이블, 뷰, 인덱스 등의 생성 권한
--                CREATE USER(사용자 계정 만들기)
--                DROP USER(사용자 계정 삭제)
--                DROP ANY TABLE(임의의 테이블 삭제)
--                QUERY REWRITE(함수 기반 인덱스 생성 권한)
--                BACKUP ANY TABLE(테이블 백업)

-- 시스템 관리자가 사용자에게 부여하는 권한
-- CREATE SESSION(데이터베이스에 접속)
-- CREATE TABLE(테이블 생성)
-- CREATE VIEW(뷰 생성)
-- CREATE SEQUENCE(시퀀스 생성)

-- <SYSTEM 계정으로 실행>
CREATE USER C##SAMPLE IDENTIFIED BY SAMPLE;

 

-- 생성한 SAMPLE 계정으로 접속 시도 시 접속 권한(CREATE SESSION)이 없어서 접속 불가
GRANT CREATE SESSION TO C##SAMPLE;

-- C##SAMPLE 계정으로 테이블 생성 구문 실행
-- 테이블 생성 권한(CREATE TABLE)이 없어서 생성 불가
GRANT CREATE TABLE TO C##SAMPLE;

CREATE TABLE TEST_TABLE(
  COL1 VARCHAR2(20),
  COL2 NUMBER
);

 

-- WITH ADMIN OPTION
-- : 사용자에게 시스템 권한을 부여할 때 사용함
--   권한을 부여받은 사용자는 다른 사용자에게 권한을 지정할 수 있음
GRANT CREATE SESSION TO C##SAMPLE
WITH ADMIN OPTION;

-- C##SAMPLE2 계정 생성하기 (SYSTEM 계정으로 실행)
CREATE USER C##SAMPLE2 IDENTIFIED BY SAMPLE2;
-- C##SAMPLE 계정으로 C##SAMPLE2 계정에 접속 권한 부여하기
GRANT CREATE SESSION TO C##SAMPLE2;

 

 

-- 2. 객체 권한 : 사용자가 특정 객체(테이블, 뷰, 시퀀스, 함수)를 조작하거나 접근할 수 있는 권한
--               DML(SELECT/INSERT/UPDATE/DELETE)
--               GRANT 권한종류 [(컬럼명)] | ALL
--               ON 객체명 | ROLE 이름 | PUBLIC
--               TO 사용자 이름

-- WITH GRANT OPTION
-- : 사용자가 특정 객체를 조작하거나 접근 할 수 있는 권한을 부여받으면서
--   그 권한을 다른 사용자에게 다시 부여할 수 있는 권한 옵션
GRANT SELECT ON C##EMPLOYEE.EMPLOYEE TO C##SAMPLE
WITH GRANT OPTION;

 

 

-- C##SAMPLE 계정에서 C##EMPLOYEE.EMPLOYEE 조회
SELECT
       EE.*
  FROM C##EMPLOYEE.EMPLOYEE EE;

 

-- C##SAMPLE 계정이 C##SAMPLE2 계정에게 권한 부여 가능
GRANT SELECT ON C##EMPLOYEE.EMPLOYEE TO C##SAMPLE2;

 

-- 권한 철회(REVOKE)
REVOKE SELECT ON C##EMPLOYEE.EMPLOYEE FROM C##SAMPLE;

 

-- 참고
-- WITH GRANT OPTION은 REVOKE시 다른 사용자에게도 부여한 권한을 같이 회수
-- WITH ADMIN OPTION은 특정 사용자의 권한만 회수가 되고 나머지 다른 사용자에게
-- 부여한 권한은 회수가 되지 않음

-- 데이터베이스 ROLE - 권한 제어
-- : 사용자마다 일일히 권한을 부여하는 것은 번거롭기 때문에
--   간편하게 권한을 부여할 수 있는 방법으로 ROLE을 제공한다.

 

 

-- 롤(ROLE)
-- : 사용자에게 보다 간편하게 부여할 수 있도록 여러 개의 권한을 묶어놓는 것
--   사용자에게 부여한 권한을 수정하고자 할 때도 롤만 수정하면
--   그 롤에 대한 권한을 부여 받은 사용자들의 권한이 자동으로 수정된다.
--   롤을 활성화 하거나 비활성화 해서 일시적으로 권한을 부여하고 철회할 수 있다.

SELECT
       GRANTEE
     , PRIVILEGE
  FROM DBA_SYS_PRIVS
-- WHERE GRANTEE = 'RESOURCE';
 WHERE GRANTEE = 'CONNECT';

 

-- 롤의 종류
-- 1. 사전 정의된 롤 : 오라클 설치 시 시스템에서 기본적으로 제공 됨
-- EX) CONNECT ROLE, RESOURCE ROLE

-- 2. 사용자가 정의하는 롤
-- : CREATE ROLE 명령으로 롤 생성
--   롤 생성은 반드시 DBA 권한이 있는 사용자만 할 수 있음
--   CREATE ROLE 롤이름; -- 1. 롤 생성
--   GRANT 권한종류 TO 롤이름; -- 2. 생성 된 롤에 권한 추가
--   GRANT 롤이름 TO 사용자이름; -- 3. 사용자에게 롤 부여

CREATE ROLE C##MYROLE;
GRANT CREATE VIEW, CREATE SEQUENCE TO C##MYROLE;
GRANT C##MYROLE TO C##SAMPLE;

 

-- MYROLE 권한 확인
SELECT
       GRANTEE
     , PRIVILEGE
  FROM DBA_SYS_PRIVS
 WHERE GRANTEE = 'C##MYROLE';

 

-- SAMPLE 계정의 ROLE 확인
SELECT
       DRP.*
  FROM DBA_ROLE_PRIVS DRP
 WHERE GRANTEE = 'C##SAMPLE';

 

 

-- <SAMPLE 계정으로 확인하기>
CREATE SEQUENCE SEQ_TEST;
DROP SEQUENCE SEQ_TEST;

CREATE OR REPLACE FORCE VIEW VIEW_TEST
AS
SELECT 
       E.A
  FROM EEE E;

 

 

 

 

 

 

 

'Programming > ORACLE' 카테고리의 다른 글

VIEW & SEQUENCE & INDEX  (0) 2022.01.27
DML & TCL  (0) 2022.01.25
DDL(Data Definition Language)  (0) 2022.01.24
서브 쿼리 ( Subquery)  (0) 2022.01.21
JOIN  (0) 2022.01.19

 

 

 

 

 

VIEW


  • SELECT 쿼리의 실행 결과를 화면에 저장한 논리적인 가상 테이블이다.
  • 테이블과는 다르게 실질적으로 데이터를 저장하고 있지 않지만, 사용자는 테이블을 사용하는것과
    동일하게 사용할 수 있다.

 

 

 

CREATE OR REPLACE VIEW V_EMPLOYEE(사번,이름,부서,지역)
  AS SELECT
    EMP_ID
        , EMP_NAME
        , DEPT_TITLE
        , NATIONAL_NAME
    FROM EMPLOYEE
    LEFT JOIN DEPARTMENT
   		 ON (DEPT_ID = DEPT_CODE)
    LEFT JOIN LOCATION
   		 ON (LOCATION_ID = LOCAL_CODE)
    LEFT JOIN NATIONAL
    USING (NATIONAL_CODE);
    
    
    

    SELECT
    *
    FROM V_EMPLOYEE;

 

 

서브쿼리의 SELECT절에 함수가 사용된 경우 반드시 별칭을 지정해 주어야 한다.
   CREATE OR REPLACE VIEW V_EMP_JOB(사번,이름,직급,성별,근무년수)
      AS SELECT
        EMP_ID
            , EMP_NAME
            , JOB_NAME
            , DECODE(SUBSTR(EMP_NO,8,1),1,’남‘
            ,2,’여’)
            , EXTRACT(YEAR FROM SYSDATE) –
            EXTRACT(YEAR FROM HIRE_DATE)
        FROM EMPLOYEE
        JOIN JOB USING(JOB_CODE);

 

 

 

   CREATE OR REPLACE VIEW V_JOB
     AS SELECT
          JOB_CODE
        , JOB_NAME
        FROM JOB;

 

 

  • 생성된 뷰를 가지고 DML구문(INSERT, UPDATE, DELETE) 사용 가능하다.
  • 생성된 뷰에 요청한 DML구문이 베이스 테이블도 변경한다.
    INSERT
    	INTO V_JOB
    VALUES(‘J8’,’인턴’);
    SELECT
    	*
    FROM V_JOB;

    SELECT
    *
    FROM JOB;

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기

 

-- VIEW(뷰)
-- SELECT 쿼리문을 저장한 객체이다.
-- 실질적인 데이터를 저장하고 있지 않은 논리적인 테이블
-- 테이블을 사용하는 것과 동일하게 사용할 수 있다.
-- 1) 복잡한 SELECT문을 다시 작성할 필요가 없음
-- 2) 민감한 데이터를 숨길 수 있음
-- CREATE [OR REPLACE] VIEW 뷰이름 AS 서브쿼리

-- 사번, 이름, 직급명, 부서명, 근무지역을 조회하고,
-- 그 결과를 V_RESULT_EMP 라는 뷰를 생성해서 저장하세요
CREATE OR REPLACE VIEW V_RESULT_EMP
AS
SELECT E.EMP_ID 
     , E.EMP_NAME
     , J.JOB_NAME
     , D.DEPT_TITLE
     , L.LOCAL_NAME
  FROM EMPLOYEE E
  LEFT JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
  LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
  LEFT JOIN LOCATION L ON (D.LOCATION_ID = L.LOCAL_CODE);

 

-- VIEW 생성 권한 부여
-- <GRANT 작성 - 시스템 계정으로 전환 - 실행 - 다시 직원관리 계정전환>
GRANT CREATE VIEW TO C##EMPLOYEE;
  
SELECT
       V.*
  FROM V_RESULT_EMP V
 WHERE V.EMP_ID = '205';

 

-- 데이터 딕셔너리(Data Dictionary)
-- 자원을 효율적으로 관리하기 위해 다양한 정보를 저장하는 시스템 테이블
-- 사용자가 테이블을 생성하거나, 사용자를 변경하는 등의 작업을 할 때
-- 데이터베이스 서버에 의해 자동으로 갱신되는 테이블
-- 사용자는 데이터 딕셔너리 내용을 직접 수정하거나 삭제할 수 없음

-- 원본 테이블을 커스터마이징 해서 보여주는 원본 테이블의
-- 가상 테이블 객체(VIEW)

-- 3개의 딕셔너리 뷰로 나뉨
-- 1. DBA_XXX : 데이터베이스 관리자만 접근이 가능한 객체 등의 정보 조회
-- 2. ALL_XXX : 자신의 계정 + 권한을 부여받은 객체의 정보 조회
-- 3. USER_XXX : 자신의 계정이 소유한 객체 등에 관한 정보 조회

-- 뷰에 대한 정보를 확인하는 데이터 딕셔너리
SELECT
       UV.*
  FROM USER_VIEWS UV;

-- 뷰에 별칭 부여
CREATE OR REPLACE VIEW V_EMP
(
  사번
, 이름
, 부서
)
AS
SELECT E.EMP_ID
     , E.EMP_NAME
     , E.DEPT_CODE
  FROM EMPLOYEE E;
  
SELECT
       V.*
  FROM V_EMP V;
  
SELECT
       V.*
  FROM (SELECT E.EMP_ID
             , E.EMP_NAME
             , E.DEPT_CODE
          FROM EMPLOYEE E
       ) V
;
  
DROP VIEW V_EMP;

 

 

-- 베이스테이블의 정보가 변경 되면
-- VIEW도 같이 변경된다.
COMMIT;

UPDATE
       EMPLOYEE E
   SET E.EMP_NAME = '정중앙'
 WHERE E.EMP_ID = '205';
 
SELECT
       E.*
  FROM EMPLOYEE E
 WHERE E.EMP_ID = '205';
 
SELECT
       V.*
  FROM V_RESULT_EMP V
 WHERE V.EMP_ID = '205';

ROLLBACK;

DROP VIEW V_RESULT_EMP;

 

 

 

-- 뷰 서브쿼리 안에 연산의 결과도 포함할 수 있다.
-- 이 때는 반드시 별칭을 부여해서 생성해야 함
CREATE OR REPLACE VIEW V_EMP_JOB
(
  사번
, 이름
, 직급
, 성별
, 근무년수
)
AS
SELECT E.EMP_ID
     , E.EMP_NAME
     , J.JOB_NAME
     , DECODE(SUBSTR(E.EMP_NO, 8, 1), 1, '남', '여')
     , EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM E.HIRE_DATE)
  FROM EMPLOYEE E
  JOIN JOB J ON(E.JOB_CODE = J.JOB_CODE);

SELECT
       V.*
  FROM V_EMP_JOB V;

 

 

 

 

 

 

 

 

 

 

 

 

DMB 명령어로 조작이 불가능한 경우


  • 1. 뷰 정의에 포함되지 않은 컬럼을 조작하는 경우
  • 2. 뷰에 포함되지 않은 컬럼 중에 베이스가 되는 테이블 컬럼이 NOT NULL 제약조건이 지정된 경우
  • 3. 산술 표현식이 정의된 경우
  • 4. JOIN을 이용해 여러 테이블을 연결한 경우
  • 5. DISTINCT를 포함한 경우
  • 6. 그룹함수나 GROUP BY 절을 포함한 경우

 

 

 

 

 

 

뷰 정의에 포함되지 않는 컬럼을 INSERT/UPDATE하는 경우 에러 발생
  • 단, DELETE는 가능하다.

 

   CREATE OR REPLACE VIEW V_JOB2
     AS SELECT
        JOB_CODE
       FROM JOB;

     INSERT
        INTO V_JOB2
     VALUES(‘J8’,’인턴’);

 

 

 

 

 

 

뷰에 포함되지 않은 NOT NULL 제약조건이 있는 컬럼이 존재하면 INSERT/UPDATE시 에러 발생
  • 단, DELETE는 가능하다.

 

   CREATE OR REPLACE VIEW V_JOB3
     AS SELECT
        JOB_NAME
      FROM JOB;

     INSERT
        INTO V_JOB3
      VALUES(’인턴’);

 

 

 

 

뷰에 산술 계산식이 포함된 경우 INSERT/UPDATE시 에러 발생
  • 단, DELETE는 가능하다.
   CREATE OR REPLACE VIEW EMP_SAL
     AS SELECT
          EMP_ID
        , EMP_NAME
        , SALARY
        ,(SALARY + (SALARY * NVL(BONUS,0))) * 12 연봉
      FROM EMPLOYEE;

 

 

   INSERT
        INTO EMP_SAL
   VALUES(
        800
        , ’정진훈’
        , 3000000
        , 40000000
        );

 

   CREATE OR REPLACE VIEW V_GROUPDEPT
     AS SELECT
            DEPT_CODE
            , SUM(SALARY) 합계
            , AVG(SALARY) 평균
        FROM EMPLOYEE
        GROUP BY DEPT_CODE;

 

 

 

 

 

 

그룹함수 혹은 GROUP BY를 사용한 경우 INSERT/UPDATE시 에러 발생
  • DELETE시에도 에러 발생

 

   INSERT
        INTO V_GROUPDEPT
   VALUES(
        ‘D10’
        , 6000000
        , 4000000
        );

 

   DELETE
        FROM V_GROUPDEPT
        WHERE DEPT_CODE = ‘D1’;

 

 

 

   CREATE OR REPLACE VIEW V_DT_EMP
     AS SELECT
        DISTINCT JOB_CODE
      FROM EMPLOYEE;

 

 

 

DISTINCT를 사용한 경우 DML 사용 불가
  • DELETE시에도 에러 발생

 

  INSERT
    INTO V_DT_EMP
    VALUES (
    ‘J9’
    );

 

 

  DELETE
    FROM V_DT_EMP
    WHERE JOB_CODE = ‘J1’;

 

 

 

   CREATE OR REPLACE VIEW V_JOINEMP
     AS SELECT
            EMP_ID
            , EMP_NAME
            , DEPT_TITLE
        FROM EMPLOYEE
        JOIN DEPARTMENT
       	  ON (DEPT_CODE = DEPT_ID);

 

 

 

뷰 정의시 JOIN을 사용한 경우 INSERT/UPDATE시 에러 발생
  • 단, DELETE는 가능하다.

 

       INSERT
          INTO V_JOINEMP
       VALUES (
            888
            , ‘조세오’
            , ‘인사관리부‘
            );

 

 

 

 

  • 뷰 정의 시 사용한 쿼리 문장이 TEXT컬럼에 저장되어 있다.
  • 뷰가 재생 될 때는 TEXT에 기록된 SELECT에 문장이 다시 실행되면서 결과를 보여주는 구조이다.
      SELECT
        *
      FROM USER_VIEWS;

 

 

 

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기

 

-- VIEW를 통한 DML 구문 수행
CREATE OR REPLACE VIEW V_JOB
AS
SELECT J.JOB_CODE
     , J.JOB_NAME
  FROM JOB J;

SELECT
       V.*
  FROM V_JOB V;
  
INSERT
  INTO V_JOB
(
  JOB_CODE
, JOB_NAME
)
VALUES
(
  'J8'
, '인턴'
);

SELECT
       V.*
  FROM V_JOB V;

SELECT
       J.*
  FROM JOB J;
  
UPDATE
       V_JOB V
   SET V.JOB_NAME = '알바'
 WHERE V.JOB_CODE = 'J8';

SELECT
       V.*
  FROM V_JOB V;

SELECT
       J.*
  FROM JOB J;

DELETE
  FROM V_JOB V
 WHERE V.JOB_CODE = 'J8';


SELECT
       V.*
  FROM V_JOB V;

SELECT
       J.*
  FROM JOB J;

 

-- DML 명령어로 조작이 불가능한 경우
-- 1. 뷰 정의에 포함되지 않은 컬럼을 조작하는 경우
-- 2. 뷰에 포함되지 않은 컬럼 중에,
--    베이스가 되는 테이블 컬럼이 NOT NULL 제약조건이 지정 된 경우
-- 3. 산술표현식으로 정의된 경우
-- 4. JOIN을 이용해 여러 테이블을 연결한 경우
-- 5. DISTINCT 포함한 경우
-- 6. 그룹함수나 GROUP BY 절을 포함한 경우

 

-- 뷰 정의에 포함되지 않은 컬럼을 조작하는 경우
CREATE OR REPLACE VIEW V_JOB2
AS
SELECT J.JOB_CODE
  FROM JOB J;

SELECT
       V.*
  FROM V_JOB2 V;
-- JOB_NAME 부적합한 식별자 오류
INSERT
  INTO V_JOB2
(
  JOB_CODE
, JOB_NAME
)
VALUES
(
  'J8'
, '인턴'
);
-- JOB_NAME 부적합한 식별자 오류
UPDATE
       V_JOB2 V
   SET V.JOB_NAME = '인턴'
 WHERE V.JOB_CODE = 'J7';
-- 뷰 정의에 사용 된 컬럼만 사용하므로 삽입 가능
INSERT
  INTO V_JOB2
(
  JOB_CODE
)
VALUES
(
  'J8'
);

SELECT
       J.*
  FROM JOB J;
-- 뷰 정의에 사용 된 컬럼만 사용하여 DELETE 가능
DELETE
  FROM V_JOB2
 WHERE JOB_CODE = 'J8';

 

 

-- 뷰에 포함되지 않은 컬럼 중에 
-- 베이스가 되는 테이블 컬럼이 NOT NULL 제약조건이 지정된 경우
CREATE OR REPLACE VIEW V_JOB3
AS
SELECT J.JOB_NAME
  FROM JOB J;
 
SELECT 
       V.*
  FROM V_JOB3 V;
-- JOB_CODE 뷰 정의에 없어 부적합한 식별자 오류
INSERT
  INTO V_JOB3
(
  JOB_CODE
, JOB_NAME
)
VALUES
(
  'J8'
, '인턴'
);
-- JOB_CODE에는 NULL이 삽입 될 수 없어 오류
INSERT
  INTO V_JOB3
(
  JOB_NAME
)
VALUES
(
  '인턴'
);  
-- 뷰에 정의 된 컬럼만을 사용한 UPDATE 수행 가능
UPDATE
       V_JOB3 V
   SET V.JOB_NAME = '인턴'
 WHERE V.JOB_NAME = '사원';

 

-- 산술표현식으로 정의된 경우
CREATE OR REPLACE VIEW EMP_SAL
AS
SELECT E.EMP_ID
     , E.EMP_NAME
     , E.SALARY
     , (E.SALARY + (E.SALARY * NVL(E.BONUS, 0))) * 12 연봉
  FROM EMPLOYEE E;
 
SELECT
       ES.*
  FROM EMP_SAL ES;
-- 산술표현식으로 연산 된 가상 컬럼 연봉에는 INSERT 불가
INSERT
  INTO EMP_SAL
(
  EMP_ID
, EMP_NAME
, SALARY
, 연봉
)
VALUES
(
  '800'
, '정진훈'
, 3000000
, 4000000
);
-- 산술표현식으로 연산 된 가상 컬럼 연봉은 UPDATE 불가  
UPDATE
       EMP_SAL ES
   SET ES.연봉 = 80000000
 WHERE ES.EMP_ID = '200';

-- DELETE의 조건으로는 사용 가능
DELETE
  FROM EMP_SAL ES
 WHERE ES.연봉 = 124800000;
 
ROLLBACK;

 

-- JOIN을 이용해 여러 테이블을 연결한 경우
CREATE OR REPLACE VIEW V_JOINEMP
AS
SELECT E.EMP_ID 
     , E.EMP_NAME
     , D.DEPT_TITLE
  FROM EMPLOYEE E
  LEFT JOIN DEPARTMENT D ON(E.DEPT_CODE = D.DEPT_ID);
  
SELECT
       V.*
  FROM V_JOINEMP V;
-- 조인 뷰에 의하여 하나 이상의 기본 테이블을 수정할 수 없습니다.
INSERT
  INTO V_JOINEMP
(
  EMP_ID
, EMP_NAME
, DEPT_TITLE
)
VALUES
(
  888
, '조세오'
, '인사관리부'
);
-- 키-보존된것이 아닌 테이블로 대응한 열을 수정할 수 없습니다
UPDATE
       V_JOINEMP V
   SET V.DEPT_TITLE = '인사관리부';
  
DELETE
  FROM V_JOINEMP V
 WHERE V.EMP_ID = '219';
  
SELECT
       V.*
  FROM V_JOINEMP V
 WHERE V.EMP_ID = '219';
 
ROLLBACK;

 

 

-- DISTINCT를 포함한 경우
CREATE OR REPLACE VIEW V_DT_EMP
AS
SELECT DISTINCT E.JOB_CODE
  FROM EMPLOYEE E;
-- 뷰에 대한 데이터 조작이 부적합합니다
INSERT
  INTO V_DT_EMP
(
  JOB_CODE
)
VALUES
(
  'J9'
);

-- 뷰에 대한 데이터 조작이 부적합합니다
UPDATE
       V_DT_EMP V
   SET V.JOB_CODE = 'J9'
 WHERE V.JOB_CODE = 'J7';
-- 뷰에 대한 데이터 조작이 부적합합니다
DELETE
  FROM V_DT_EMP V
 WHERE V.JOB_CODE = 'J7';

 

 

-- 그룹 함수나 GROUP BY 절을 포함한 경우
CREATE OR REPLACE VIEW V_GROUPDEPT
AS
SELECT E.DEPT_CODE
     , SUM(E.SALARY) 합계
     , AVG(E.SALARY) 평균
  FROM EMPLOYEE E
 GROUP BY E.DEPT_CODE;

SELECT
       V.*
  FROM V_GROUPDEPT V;
-- 가상 열은 사용할 수 없습니다
INSERT
  INTO V_GROUPDEPT
(
  DEPT_CODE
, 합계
, 평균
)
VALUES
(
  'D0'
, 60000000
, 4000000
);
-- 뷰에 대한 데이터 조작이 부적합합니다
UPDATE
       V_GROUPDEPT V
   SET V.DEPT_CODE = 'D10'
 WHERE V.DEPT_CODE = 'D1';
-- 뷰에 대한 데이터 조작이 부적합합니다
DELETE
  FROM V_GROUPDEPT V
 WHERE V.DEPT_CODE = 'D1';

 

 

 

 

 

 

 

 

 

 

 

 

VIEW 옵션


  • 1. OR REPLACE 옵션
    - 생성한 뷰가 존재하면, 뷰를 갱신한다.
  • 2. FORCE/NOFORCE 옵션
    - FORCE옵션은 기본 테이블이 존재하지 않더라도 뷰를 생성한다.
    - 기본값은 NOFORCE로 지정되어 있다.
  • 3. FORCE/NOFORCE 옵션
    - FORCE옵션은 기본 테이블이 존재하지 않더라도 뷰를 생성한다.
    - 기본값은 NOFORCE로 지정되어 있다.
  • 4. WITH READ ONLY 옵션
    - 뷰에 대해 조회만 가능하고, 삽입, 수정, 삭제 등을 하지 못하게 한다.

 

 

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기

 

-- VIEW 옵션
-- OR REPLACE : 기존에 동일한 뷰 이름이 존재하는 경우 덮어쓰고
--              존재하지 않으면 새로 생성하는 옵션
-- FORCE 옵션 : 서브쿼리에 사용 된 테이블이 존재하지 않아도 뷰 생성
CREATE OR REPLACE FORCE VIEW V_EMP
AS
SELECT TCODE
     , TNAME
     , TCONTENTS
  FROM TT;
 
SELECT
       V.*
  FROM V_EMP V;

 

-- NOFORCE 옵션 : 서브쿼리에 테이블이 존재해야만 뷰 생성함(기본값)
CREATE OR REPLACE /*NOFORCE*/ VIEW V_EMP2
AS
SELECT TCODE
     , TNAME
     , TCONTENTS
  FROM TT;

 

-- WITH CHECK OPTION : 조건절에 사용 된 컬럼의 값을 수정하지 못하게 한다.
CREATE OR REPLACE VIEW V_EMP3
AS
SELECT E.* 
  FROM EMPLOYEE E
 WHERE MANAGER_ID  = '200'
  WITH CHECK OPTION;
-- 뷰의 WITH CHECK OPTION의 조건에 위배 됩니다
UPDATE
       V_EMP3
   SET MANAGER_ID = '900'
 WHERE MANAGER_ID = '200';

 

 

-- WITH READ ONLY : DML 수행이 불가능하게 한다.
CREATE OR REPLACE VIEW V_DEPT
AS
SELECT D.*
  FROM DEPARTMENT D
  WITH READ ONLY;
-- 읽기 전용 뷰에서는 DML 작업을 수행할 수 없습니다.
DELETE
  FROM V_DEPT;

 

 

 

 

 

 

 

 

 

 

 

 

INLINE VIEW


  • 일반적으로 FROM 절에 사용된 서브쿼리의 결과 화면에 별칭을 붙인 것을 말한다.
  • FROM절에 서브쿼리를 직접 사용해도 되고, 따로 뷰를 생성 후 FROM절에서 생성한 뷰를
    사용해도 된다.

 

 

 

 

 

 

 

 

 

 

 

ORACLE OBJECT(SEQUENCE)

 

 

 

 

 

 

SEQUENCE


  • 순차적으로 정수 값을 자동으로 생성하는 객체로, 자동 번호 발생기의 역할을 한다.

 

표현식
CREATE SEQUENCE 시퀀스명
   [START WITH 숫자 ] - 처음 발생시킬 시작 값 지정, 기본값 1
   [INCREMENT BY 숫자 ] - 다음 값에 대한 증가치, 기본값 1
   [MAXVALUE 숫자 | NOMAXVALUE ] - 발생시킬 최대값 지정, 10의 27승 -1까지 가능
   [MINVALUE 숫자 | NOMINVALUE ] – 발생시킬 최소값 지정, -10의 26승
   [CYCLE | NOCYCLE ] – 시퀀스가 최대값까지 증가를 완료하면 CYCLE은 START WITH 설정값으로
   						돌아가고, NOCYCLE은 에러발생
   [CACHE | NOCACHE] – CACHE는 메모리상에서 시퀀스값을 관리, 기본값 20

 

 

  CREATE SEQUENCE SEQ_EMPID
    START WITH 300
    INCREMENT BY 5
    MAXVALUE 310
    NOCYCLE
    NOCACHE;
    
    
  SELECT
    SEQ_EMP_ID.CURRVAL
    FROM DUAL;

 

 

 

 

 

 

 

NEXTVAL / CURRVAR 사용 가능 여부


사용가능 사용불가
서브쿼리가 아닌 SELECT문 VIEW의 SELECT절
INSERT문의 SELECT절 DISTINCT 키워드가 있는 SELECT문
INSERT문의 VALUE절 GROUP BY, HAVING, ORDER BY 절이 있는 SELECT문
UPDATE문의 SET절 SELECT, DELETE, UPDATE의 서브쿼리
  CREATE TABLE, ALTER TABLE 명령의 DEFAULT값

 

 

 

  • 시퀀스 수정 시 CREAT에 사용한 옵션을 변경할 수 있다.
  • 단, START WITH값 변경은 불가하기 때문에 변경하려면 삭제 후 다시 생성해야 한다.
  ALTER SEQUENCE SEQ_EMPID
    INCREMENT BY 10
    MAXVALUE 400
    NOCYCLE
    NOCACHE;

 

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기

 

-- 시퀀스(SEQUENCE)
-- 자동 번호 발생기 역할을 하는 객체
-- 순차적으로 정수값을 자동으로 생성해줌
/*
  CREATE SEQUENCE 시퀀스이름
  [INCREMENT BY 숫자] -- 다음 값에 대한 증가치, 생략하면 자동 1 기본
  [START WITH 숫자] -- 처음 발생시킬 값 지정, 생략하면 자동 1 기본
  [MAXVALUE 숫자 | NOMAXVALUE] -- 발생시킬 최대값 지정(10의 27승)
  [MINVALUE 숫자 | NOMINVALUE] -- 최소값 지정(-10의 26승)
  [CYCLE | NOCYCLE] -- 값 순환 여부
  [CACHE 바이트크기 | NOCACHE] -- 캐쉬메모리 기본값은 20바이트, 최소는 2바이트
*/

 

CREATE SEQUENCE SEQ_EMPID
START WITH 300
INCREMENT BY 5
MAXVALUE 310
NOCYCLE
NOCACHE;


-- 시퀀스.CURRVAL : 해당 시퀀스가 가지고 있는 CURRENT VALUE (현재 값)
-- 시퀀스.NEXTVAL : 해당 시퀀스가 가질 다음 값 리턴
-- NEXTVAL를 1회 수행해야 CURRVAL를 알아올 수 있다
SELECT SEQ_EMPID.CURRVAL FROM DUAL;

 

SELECT SEQ_EMPID.NEXTVAL FROM DUAL; --300
SELECT SEQ_EMPID.CURRVAL FROM DUAL; --300
SELECT SEQ_EMPID.NEXTVAL FROM DUAL; --305
SELECT SEQ_EMPID.CURRVAL FROM DUAL; --305
SELECT SEQ_EMPID.NEXTVAL FROM DUAL; --310
SELECT SEQ_EMPID.NEXTVAL FROM DUAL; --MAXVALUE를 넘어서면 에러 발생

 

 

-- 시퀀스 변경
ALTER SEQUENCE SEQ_EMPID
INCREMENT BY 10
MAXVALUE 400
NOCYCLE
NOCACHE;

-- START WITH 값은 변경이 불가능하므로
-- START WITH 값을 변경하려면 DROP으로 삭제 후 다시 생성해야 함

 

-- START WITH 값은 변경이 불가능하므로
-- START WITH 값을 변경하려면 DROP으로 삭제 후 다시 생성해야 함

-- SELECT문에서 사용 가능
-- INSERT문에서 SELECT 구문 사용 가능
-- INSERT문에서 VALUES 절에서 사용 가능
-- UPDATE문에서 SET절에서 사용 가능

-- 단, 서브쿼리의 SELECT문에서 사용 불가
-- VIEW의 SELECT절에서 사용 불가
-- DISTINCT 키워드가 있는 SELECT문에서 사용 불가
-- GROUP BY, HAVING절이 있는 SELECT문에서 사용 불가
-- ORDER BY절에서 사용 불가
-- CREATE TABLE, ALTER TABLE의 DEFAULT값으로 사용 불가

 

CREATE SEQUENCE SEQ_EID
START WITH 300
INCREMENT BY 1
MAXVALUE 10000
NOCYCLE
NOCACHE;

INSERT
  INTO EMPLOYEE A
(
  A.EMP_ID, A.EMP_NAME, A.EMP_NO, A.EMAIL, A.PHONE
, A.DEPT_CODE, A.JOB_CODE, A.SAL_LEVEL, A.SALARY, A.BONUS
, A.MANAGER_ID, A.HIRE_DATE, A.ENT_DATE, A.ENT_YN
)  
VALUES
(
  SEQ_EID.NEXTVAL, '홍길동', '666666-6666666', 'hong_gd@greedy.com', '01012341234'
, 'D2', 'J7', 'S1', 5000000, 0.1
, 200, SYSDATE, NULL, DEFAULT
);

 

SELECT
       E.*
  FROM EMPLOYEE E;
  
ROLLBACK;

 

-- 시퀀스 삭제
DROP SEQUENCE SEQ_EMPID;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

INDEX


  • SQL명령문의 처리 속도를 향상시키기 위해서 컬럼에 대해서 생성하는 오라클 객체이다.
  • 내부구조는 B*트리 형식으로 구성되어 있다.

 

 

INDEX의 장점과 단점


장점 
  • 검색속도가 빨라지고 시스템에 걸리는 부하를 줄여서 시스템 전체 성능을 향상시킬 수 있다.

 

단점 
  • 인덱스를 위한 추가 저장 공간이 필요하고, 인덱스를 생성하는데 시간이 걸린다.
  • 따라서 데이터의 변경 작업(INSERT/UPDATE/DELETE)이 자주 일어날 경우에는 오히려 성능이 저하된다.

 

 

 

 

INDEX 표현식

 

 

    SELECT
    	*
    FROM USER_IND_COLUMNS;

 

 

 

 

 

INDEX 구조


 

  SELECT
   	  ROWID
    , EMP_ID
    , EMP_NAME
  FROM EMPLOYEE;

 

 

 

 

INDEX 종류


1. 고유 인덱스(UNIQUE INDEX)
  • - 중복값이 포함될 수 없음
  • - PRIMARY KEY 제약조건을 생성하면 자동으로 생성됨

 

2. 비고유 인덱스(NONUNIQUE INDEX)
  • - 빈번하게 사용되는 일반 컬럼을 대상으로 생성함
  • - 주로 성능 향상을 위한 목적으로 생성함

 

3. 단일 인덱스(SINGLE INDEX)
  • - 한 개의 컬럼으로 구성한 인덱스

 

4. 결합 인덱스(COMPOSITE INDEX)
  • - 두 개 이상의 컬럼으로 구성한 인덱스

 

5. 함수 기반 인덱스(FUNCTION-BASED INDEX)
  • - SELECT 절이나 WHERE 절에 산술계산식이나 함수식이 사용된 경우
  • - 계산식은 인덱스의 적용을 받지 않는다.

 

 

 

 

 

 

 

1. 고유 인덱스 UNIQUE INDEX


  • 중복값이 포함될 수 없음
  • PRIMARY KEY 제약조건을 생성하면 자동으로 생성됨

 

 

CREATE UNIQUE INDEX IDX_EMPNO
ON EMPLOYEE(EMP_NO);

 

SELECT
*
FROM USER_IND_COLUMNS
WHERE TABLE_NAME = ‘EMPLOYEE’;

 

 

  • UNIQUE INDEX는 중복 값이 있는 컬럼에 생성 시 에러가 발생한다.
CREATE UNIQUE INDEX IDX_DEPTCODE
ON EMPLOYEE(DEPT_CODE);

 

 

 

 

 

 

 

 

2. 비고유 인덱스(NONUNIQUE INDEX)


  • 빈번하게 사용되는 일반 컬럼을 대상으로 생성함
  • 주로 성능 향상을 위한 목적으로 생성함
  • NONUNIQUE INDEX는 중복 값이 있는 컬럼에도 생성 가능하다.

 

CREATE INDEX IDX_DEPTCODE
ON EMPLOYEE(DEPT_CODE);

 

 

SELECT
*
FROM USER_IND_COLUMNS
WHERE TABLE_NAME = ‘EMPLOYEE’;

 

 

 

3. 결합 인덱스(COMPOSITE INDEX)


  • COMPOSITE INDEX는 두 개 이상의 컬럼을 하나의 인덱스로 생성할 수 있다.
  • COLUMN_POSITION의 순서에 의해 성능이 차이날 수 있다.

 

CREATE INDEX IDX_DEPT
ON DEPARTMENT(DEPT_ID,DEPT_TITLE);

 

SELECT
*
FROM USER_IND_COLUMNS
WHERE TABLE_NAME = ‘DEPARTMENT’;

 

 

 

4.함수 기반 인덱스(FUNCTION-BASED INDEX)


   CREATE TABLE EMP_SAL
     AS SELECT
        EMP_ID
        , EMP_NAME
        , SALARY
        , BONUS
        , (SALARY +(SALARY + NVL(BONUS,0))) * 12 연봉
      FROM EMPLOYEE;

 

CREATE INDEX IDX_SALCALC
   ON EMP_SAL ((SALARY + (SALARY * NVL(BONUS,0))) * 12);

 

    SELECT
    	*
    FROM USER_IND_COLUMNS
    WHERE TABLE_NAME = 'EMP_SAL';

 

 

 

 

INDEX와 DML


  • DML작업(특히 DELETE)명령을 수행한 경우, 해당 인덱스 엔트리가 논리적으로만 제거되고 실제 엔트리는 그냥 남아있게 된다. 
  • 제거된 인덱스가 필요없는 공간을 차지하고 있기 때문에 인덱스를 재생성 할 필요가 있다.

 

표현식

 

 

 

 

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기

 

-- 인덱스(INDEX)
-- : SQL명령문의 검색 처리 속도를 향상시키기 위해
--   컬럼에 대해서 생성하는 오라클 객체

-- 하드디스크의 어느 위치인지에 대한 정보를 가진 주소록
-- DATA - ROWID로 구성
-- ROWID 구조 : 오브젝트 번호, 상대파일 번호, 블록 번호, 데이터 번호
SELECT
       ROWID
     , E.EMP_ID
     , E.EMP_NAME
  FROM EMPLOYEE E;
  
-- 인덱스의 내부 구조는 이진트리 형식으로 구성되어 있고
-- 인덱스를 생성하기 위해서는 시간이 필요함
-- 또한 인덱스를 위한 추가 저장 공간이 필요하기 때문에 반드시 좋은 것은 아니다.
-- => 인덱스가 생성 된 컬럼에서 DML 작업이 빈번한 경우 처리 속도가 느려진다.
-- 따라서 일반적으로 테이블 전체 로우의 15% 이하의 데이터를 조회할 때 인덱스를 생성한다.

-- 장점
-- 검색 속도가 빨라짐
-- 시스템에 걸리는 부하를 줄여서 시스템 전체의 성능을 향상시킴

-- 단점
-- 인덱스를 위한 추가 저장 공간이 필요함
-- 인덱스를 생성하는데 시간이 걸림
-- 데이터의 변경작업(INSERT/UPDATE/DELETE)이 자주 일어나는 경우
-- REBUILD 작업을 주기적으로 해주어야 하고, REBUILD를 자주 해주지 않으면
-- 성능이 오히려 저하된다.

-- 인덱스를 관리하는 데이터 딕셔너리
-- PK, UNIQUE 제약 조건이 있으면 자동으로 INDEX 객체가 생성 된다.
SELECT
       UIC.*
  FROM USER_IND_COLUMNS UIC;
  
-- 인덱스 종류
-- 1. 고유 인덱스(UNIQUE INDEX)
-- 2. 비고유 인덱스(NONUNIQUE INDEX)
-- 3. 단일 인덱스(SINGLE INDEX)
-- 4. 결합 인덱스(CONPOSITE INDEX)
-- 5. 함수기반 인덱스(FUNCTION BASED INDEX)

 

-- UNIQUE INDEX
-- UNIQUE INDEX로 생성 된 컬럼에는 중복 값이 포함될 수 없음
-- 오라클 PRIMARY KEY, UNIQUE 제약조건을 생성하면
-- 자동으로 해당 컬럼에 UNIQUE INDEX가 생성 됨
-- 해당 컬럼으로 ACCESS 하는 경우 성능 향상의 효과가 있음

-- 인덱스 힌트
-- 일반적으로는 옵티마이저가 적절한 인덱스를 타거나 풀 스캐닝을 해서 비용이 적게 드는 효율적인 방식으로 검색함
-- 하지만 우리가 원하는 테이블에 있는 인덱스를 사용할 수 있도록 해주는 구문(힌트)를 통해 선택 가능
-- SELECT절 첫 줄에 힌트 주석(/*+ 내용 */)을 작성하여 적절한 인덱스를 부여할 수 있다.

SELECT /*+ INDEX(E 엔터티1_PK)*/
       E.*
  FROM EMPLOYEE E;

 

-- 인덱스가 내림차순으로 생성 되어서 인덱스 영역에서 역방향으로 스캔하라는 뜻
-- 예전에 넣었던 데이터부터 순서대로 나오도록 정렬
SELECT /*+ INDEX_DESC(E 엔터티1_PK)*/
       E.*
  FROM EMPLOYEE E;

 

-- UNIQUE 제약 조건에 의해 이미 인덱스가 존재하여 생성할 수 없음
CREATE UNIQUE INDEX IDX_EMPNO
ON EMPLOYEE(EMP_NO);

 

SELECT 
       UIC.*
  FROM USER_IND_COLUMNS UIC;

-- PK나 UNIQUE 제약 조건으로 인해 생성 된 인덱스는 DROP 할 수 없음
DROP INDEX SYS_C007415;

-- 중복 값이 있는 컬럼은 UNIQUE 인덱스 생성하지 못함
CREATE UNIQUE INDEX IDX_DEPTCODE
ON EMPLOYEE(DEPT_CODE);

-- NONUNIQUE INDEX
-- WHERE절에서 빈번하게 사용 되는 일반 컬럼을 대상으로 생성
-- 주로 성능 향상을 위한 목적으로 생성함
CREATE INDEX IDX_DEPTCODE
ON EMPLOYEE(DEPT_CODE);

 

-- 결합 인덱스(COMPOSITE INDEX)
-- 결합 인덱스는 중복 값이 낮은 값이 먼저 오는 것이 검색 속도를 향상시킨다.
CREATE INDEX IDX_DEPT
ON DEPARTMENT(DEPT_ID, DEPT_TITLE);

SELECT /*+ INDEX_DESC(D IDX_DEPT)*/
       D.DEPT_ID
  FROM DEPARTMENT D
 WHERE D.DEPT_TITLE > '0'
   AND D.DEPT_ID > '0';
   
   
   -- 0보다 크다는 것은 '모두'라는 뜻

 

-- 함수 기반 인덱스
-- SELECT절이나 WHERE절에서 산술 계산식이나 함수가 사용된 경우
-- 계산에 포함 된 컬럼은 인덱스의 적용을 받지 않는다.
-- 계산식으로 검색하는 경우가 많다면, 수식이나 함수식으로 이루어진 컬럼을
-- 인덱스로 만들 수도 있다.
CREATE INDEX IDX_EMP_SALCALC
ON EMPLOYEE((SALARY + (SALARY * NVL(BONUS, 0))) * 12);

SELECT /*+ INDEX_DESC(E IDX_EMP_SALCALC)*/
       E.EMP_ID
     , E.EMP_NAME
     , ((E.SALARY + (E.SALARY * NVL(E.BONUS, 0))) * 12) 연봉
  FROM EMPLOYEE E
 WHERE ((E.SALARY + (E.SALARY * NVL(E.BONUS, 0))) * 12) > 10000000;

 

 

 

 

 

 

 

 

 

 

 

'Programming > ORACLE' 카테고리의 다른 글

동의어(SYNONTM) & 권한과 롤  (0) 2022.01.27
DML & TCL  (0) 2022.01.25
DDL(Data Definition Language)  (0) 2022.01.24
서브 쿼리 ( Subquery)  (0) 2022.01.21
JOIN  (0) 2022.01.19

 

 

 

 

 

DML이란?


  • 데이터 조작언어이다.
  • 테이블에 값을 삽입(INSERT), 수정(UPDATE), 삭제(DELETE)를 한다.

 

 

 

 

 

 

 

 

INSERT


  •  새로운 행을 테이블에 추가하는 구문이다. 테이블의 행 개수가 증가한다.

 

 

 

 

 

INSERT하고자 하는 컬럼이 모든 컬럼인 경우 컬럼명 생략이 가능하다. 
  • 단, 컬럼의 순서를 지켜서 VALUES에 값을 기입해야한다.

 

 

 

 

 

 

SELECT
*
FROM EMPLOYEE
WHERE EMP_NAME = ‘장채현’;

 

 

 

 

INSERT시에 VALUES 대신 서브쿼리를 이용할 수 있다.

 

 

 

SELECT
*
FROM EMP_01;

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기
더보기
-- DML (Data Manupulation Language)
-- INSERT, UPDATE, DELETE, SELECT
-- : 데이터 조작 언어, 테이블에 값을 삽입하거나, 수정하거나
--   삭제하거나, 조회하는 언어

-- INSERT : 새로운 행을 추가하는 구문이다.
--          테이블의 행 갯수가 증가한다. 
-- [표현식]
-- 테이블의 일부 컬럼에 INSERT할 때 
-- INSERT INTO 테이블명(컬럼명, 컬럼명, ..) VALUES (데이터, 데이터, ...);

-- 테이블의 모든 컬럼에 INSERT할 때
-- INSERT INTO 테이블명 VALUES (데이터, 데이터, ..);

-- 하지만 모든 컬럼에 INSERT 할 때에도 컬럼명을 기술하는 것이 의미파악에 더 좋다.

 

INSERT
  INTO EMPLOYEE E
  (
  E.EMP_ID, E.EMP_NAME, E.EMP_NO, E.EMAIL, E.PHONE
, E.DEPT_CODE, E.JOB_CODE, E.SAL_LEVEL, E.SALARY, E.BONUS
, E.MANAGER_ID, E.HIRE_DATE, E.ENT_DATE, E.ENT_YN
)
VALUES
(
'900', '장채현', '901123-2080503', 'jang123@greedy.com', '011055569512'
, 'd1', 'j7', 's3', 4300000, 0.2
, '200', SYSDATE, NULL, DEFAULT
);

COMMIT;

 

SELECT
        E.*
  FROM EMPLOYEE E
  WHERE EMP_NAME = '장채현';

 

 

 

-- INSERT 대신에 서브쿼리를 이용할 수 있다. 
CREATE TABLE EMP_01 (   
    EMP_ID NUMBER,
    EMP_NAME VARCHAR2(30),
    DEPT_TITLE VARCHAR2(20)
);

SELECT
        E.*
  FROM EMP_01 E;


INSERT
  INTO EMP_01 A
  (
  A.EMP_ID
, A.EMP_NAME
, A.DEPT_TITLE
)
(
SELECT
     E.EMP_ID
   , EMP_NAME
   , DEPT_TITLE
   FROM EMPLOYEE E
   LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
);


SELECT
        E.*
  FROM EMP_01 E;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

INSERT ALL


  • INSERT시 사용하는 서브쿼리가 사용하는 테이블이 같은 경우, 두 개 이상의 테이블에 INSERT ALL을 이용하여 한번에 삽입을 할 수 있다.
  • 단, 각 서브쿼리의 조건절이 같아야 한다.

 

 CREATE TABLE EMP_DEPT_D1
    AS SELECT
    EMP_ID
    , EMP_NAME
    , DEPT_CODE
    , HIRE_DATE
    FROM EMPLOYEE
    WHERE 1=0;

  SELECT
    *
    FROM EMP_DEPT_D1;

 

 

 

 CREATE TABLE EMP_MANAGER
    AS SELECT
    EMP_ID
    , EMP_NAME
    , DEPT_CODE
    , MANAGER_ID
    FROM EMPLOYEE
    WHERE 1=0;

 SELECT
    *
    FROM EMP_MANAGER;

 

 

 

문제
EMP_DEPT_D1 테이블에 EMPLOYEE테이블의 부서 코드가 D2인 직원을 조회한 후 사번, 이름, 소속부서,입사일을 삽입하고, EMP_MANAGER 테이블에 EMPLOYEE 테이블의 부서 코드가 D2인 직원의사번, 이름, 관리자사번을 조회해 삽입하세요.
 INSERT ALL
    INTO EMP_DEPT_D1
 VALUES(EMP_ID,EMP_NAME,DEPT_CODE,HIRE_DATE)
    INTO EMP_MANAGER
 VALUES(EMP_ID,EMP_NAME,DEPT_CODE,HIRE_DATE,MANAGER_ID)
    SELECT
    EMP_ID
    , EMP_NAME
    , DEPT_CODE
    , HIRE_DATE
    , MANAGER_ID
 FROM EMPLOYEE
 WHERE DEPT_CODE = ‘D1’;

 

 

  • EMPLOYEE 테이블의 구조를 복사하여 사번, 이름, 입사일, 급여를 기록할 수 있는 테이블
    EMP_OLD와 EMP_NEW를 생성하세요.
 CREATE TABLE EMP_OLD
    AS SELECT
    EMP_ID
    , EMP_NAME
    , HIRE_DATE
    , SALARY
    FROM EMPLOYEE
    WHERE 1 = 0;

 

 

 CREATE TABLE EMP_NEW
    AS SELECT
    EMP_ID
    , EMP_NAME
    , HIRE_DATE
    , SALARY
    FROM EMPLOYEE
    WHERE 1 = 0;

 

 

 

EMPLOYEE 테이블의 입사일 기준으로 2000년 1월1일 이전에 입사한 사원의 사번, 이름, 입사일, 급여를 조회해서 EMP_OLD 테이블에 삽입하고, 그전에 입사한 사원의 정보는 EMP_NEW 테이블에 삽입하세요.
 INSERT ALL
    WHEN HIRE_DATE < ‘2000/01/01’ THEN
    INTO EMP_OLD
 VALUES (EMP_ID, EMP_NAME, HIRE_DATE, SALARY)
    WHEN HIRE_DATE >= ‘2000/01/01’ THEN
    INTO EMP_NEW
 VALUES (EMP_ID,EMP_NAME,HIRE_DATE,SALARY)
    SELECT
    EMP_ID
    , EMP_NAME
    , HIRE_DATE
    , SALARY
    FROM EMPLOYEE;

 

 

 

SELECT
*
FROM EMP_OLD;

 

SELECT
*
FROM EMP_OLD;

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기
더보기

 

-- INSERT ALL : INSERT 시에 사용 하는 서브쿼리가 같은 경우
--              두개 이상의 테이블에 INSERT ALL을 이용하여
--              한번에 데이터를 삽입할 수 있다.
--              단, 각 서브쿼리의 조건절이 같아야 한다. 

-- 테이블을 생성하자
CREATE TABLE EMP_DEPT_D1
AS
SELECT E.EMP_ID
     , E.EMP_NAME
     , E.DEPT_CODE
     , E.HIRE_DATE
FROM EMPLOYEE E
WHERE 1 = 0;

-- 생성된 테이블 조회하기 
SELECT 
        ED.*
  FROM EMP_DEPT_D1 ED; 

-- 테이블을 하나 더 만든다. 
CREATE TABLE EMP_MANAGER
AS
SELECT E.EMP_ID
     , E.EMP_NAME
     , E.MANAGER_ID
FROM EMPLOYEE E
WHERE 1 = 0;

-- 테이블을 조회하기
SELECT
        EM.*
  FROM EMP_MANAGER EM;

 

-- EMP_DEPT_D1 테이블에 EMPLOYEE 테이블에 있는 부서 코드가 D1인 직원을
-- 조회해서 사번, 이름, 소속부서, 입사일을 삽입하고,
-- EMP_MANAGER 테이블에 EMPLOYEE 테이블에 있는 부서 코드가 D1인 직원을
-- 조회해서 사번, 이름, 관리자 사번을 삽입하세요
INSERT
  INTO EMP_DEPT_D1 A
(
  A.EMP_ID
, A.EMP_NAME
, A.DEPT_CODE
, A.HIRE_DATE
)
(
  SELECT E.EMP_ID
       , E.EMP_NAME
       , E.DEPT_CODE
       , E.HIRE_DATE
    FROM EMPLOYEE E
   WHERE E.DEPT_CODE = 'D1'
);

INSERT
  INTO EMP_MANAGER A
(
  A.EMP_ID
, A.EMP_NAME
, A.MANAGER_ID
)
(
  SELECT E.EMP_ID
       , E.EMP_NAME
       , E.MANAGER_ID
    FROM EMPLOYEE E
   WHERE E.DEPT_CODE = 'D1'
);

SELECT
       ED.*
  FROM EMP_DEPT_D1 ED;
  
SELECT
       EM.*
  FROM EMP_MANAGER EM;

DELETE
  FROM EMP_DEPT_D1;

DELETE
  FROM EMP_MANAGER;
  
INSERT ALL
  INTO EMP_DEPT_D1
VALUES
(
  EMP_ID
, EMP_NAME
, DEPT_CODE
, HIRE_DATE
)
  INTO EMP_MANAGER
VALUES
(
  EMP_ID
, EMP_NAME
, MANAGER_ID
)
SELECT E.EMP_ID
     , E.EMP_NAME
     , E.DEPT_CODE
     , E.HIRE_dATE
     , E.MANAGER_ID
  FROM EMPLOYEE E
 WHERE E.DEPT_CODE = 'D1';

SELECT
       ED.*
  FROM EMP_DEPT_D1 ED;
  
SELECT
       EM.*
  FROM EMP_MANAGER EM;

 

 

 

-- EMPLOYEE 테이블에서 입사일 기준으로 2000년 1월 1일 이전에 입사한
-- 사원의 사번, 이름, 입사일, 급여를 조회하여
-- EMP_OLD 테이블에 삽입하고
-- 그 이후에 입사한 사원은 EMP_NEW 테이블에 삽입하세요
-- 문제 의도 : INSERT ALL을 사용하되 입사일 기준으로 나누어라

CREATE TABLE EMP_OLD
AS
SELECT E.EMP_ID
     , E.EMP_NAME
     , E.HIRE_DATE
     , E.SALARY
  FROM EMPLOYEE E
 WHERE 1 = 0;

CREATE TABLE EMP_NEW
AS
SELECT E.EMP_ID
     , E.EMP_NAME
     , E.HIRE_DATE
     , E.SALARY
  FROM EMPLOYEE E
 WHERE 1 = 0;


-- INSERT ALL
INSERT ALL
WHEN HIRE_DATE < '2000/01/01'
THEN
INTO EMP_OLD
VALUES
(
    EMP_ID
  , EMP_NAME
  , HIRE_DATE
  , SALARY
  )
  WHEN HIRE_DATE >= '2000/01/01'
  THEN 
  INTO EMP_NEW
VALUES
(
EMP_ID
, EMP_NAME
, HIRE_DATE
, SALARY
)
SELECT E.EMP_ID
  , E.EMP_NAME
  , E.HIRE_DATE
  , E.SALARY
FROM EMPLOYEE E;

-- 24개 행 이(가) 삽입되었습니다.



-- 잘 들어갔는지 조회
SELECT 
        EO.*
  FROM EMP_OLD EO;


SELECT 
        EN.*
  FROM EMP_NEW EN;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

MERGE


  • 구조가 같은 두 개의 테이블을 하나의 테이블로 합치는 기능을 한다.
  • 두 테이블에서 지정하는 조건의 값이 존재하면 UPDATE되고, 조건의 값이 없으면 INSERT 되도록 한다.

 

 CREATE TABLE EMP_M01
    AS SELECT
    *
    	FROM EMPLOYEE;



 CREATE TABLE EMP_M02
    AS SELECT
    *
   	 	FROM EMPLOYEE
    	WHERE JOB_CODE = ‘J4’;

 

 INSERT
    INTO EMP_M02
 VALUES
    (
    999
    , ‘곽두원’
    , ‘561115-1234567’
    , ‘kwack_dw@greedy.com’
    , ‘01011112222’
    , ‘D9’
    , ‘J1’
    , ‘S1’
    , 9000000
    , 0.5
    , NULL
    , SYSDATE
    , DEFAULT
    , DEFAULT
    );


 UPDATE
    EMP_M02
 SET
    SALARY = 0;
MERGE INTO EMP_M01 USING EMP_M02 ON(EMP_M01.EMP_ID = EMP_M02.EMP_ID)
WHEN MATCHED THEN
UPDATE SET
EMP_M01.EMP_NAME = EMP_M02.EMP_NAME,
EMP_M01.EMP_NO = EMP_M02.EMP_NO,
EMP_M01.EMAIL = EMP_M02.EMAIL,
EMP_M01.PHONE = EMP_M02.PHONE,
EMP_M01.DEPT_CODE = EMP_M02.DEPT_CODE,
EMP_M01.JOB_CODE = EMP_M02.JOB_CODE,
EMP_M01.SAL_LEVEL = EMP_M02.SAL_LEVEL,
EMP_M01.SALARY = EMP_M02.SALARY,
EMP_M01.BONUS = EMP_M02.BONUS,
EMP_M01.MANAGER_ID = EMP_M02.MANAGER_ID,
EMP_M01.HIRE_DATE = EMP_M02.HIRE_DATE,
EMP_M01.ENT_DATE = EMP_M02.ENT_DATE,
EMP_M01.ENT_YN = EMP_M02.ENT_YN
WHEN NOT MATCHED THEN
INSERT VALUES(EMP_M02.EMP_ID, EMP_M02.EMP_NAME, EMP_M02.EMP_NO,
EMP_M02.EMAIL, EMP_M02.PHONE, EMP_M02.DEPT_CODE, EMP_M02.JOB_CODE,
EMP_M02.SAL_LEVEL, EMP_M02.SALARY, EMP_M02.BONUS, EMP_M02.MANAGER_ID,
EMP_M02.HIRE_DATE, EMP_M02.ENT_DATE, EMP_M02.ENT_YN);

 

 

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기
더보기

 

-- MERGE : 
-- 구조가 같은 두 개의 테이블을 하나로 합치는 기능을한다.
-- 테이블에서 지정하는 조건의 값이 존재하면 UPDATE
-- 조건의 값이 없으면 INSERT 됨


CREATE TABLE EMP_M01
AS
SELECT E.*
  FROM EMPLOYEE E;
-- Table EMP_M01이(가) 생성되었습니다.
  
  
  
CREATE TABLE EMP_M02
AS
SELECT E.*
  FROM EMPLOYEE E
 WHERE E.JOB_CODE = 'J4';
-- Table EMP_M02이(가) 생성되었습니다.


INSERT
  INTO EMP_M02 A
(
  A.EMP_ID, A.EMP_NAME, A.EMP_NO, A.EMAIL, A.PHONE
, A.DEPT_CODE, A.JOB_CODE, A.SAL_LEVEL, A.SALARY, A.BONUS
, A.MANAGER_ID, A.HIRE_DATE, A.ENT_DATE, A.ENT_YN
)
VALUES
(
  999, '임수연', '000101-4567890', 'limsoo@greedy.com', '01011112222'
, 'D9', 'J4', 'S1', 9000000, 0.5
, NULL, SYSDATE, NULL, DEFAULT
);
-- 1 행 이(가) 삽입되었습니다.


SELECT
       EM1.*
  FROM EMP_M01 EM1;
 
SELECT
       EM2.*
  FROM EMP_M02 EM2; 
 
UPDATE
       EMP_M02
   SET SALARY = 0;
   
-- 5개 행 이(가) 업데이트되었습니다.  
   
   

MERGE
 INTO EMP_M01 M1
USING EMP_M02 M2
   ON (M1.EMP_ID = M2.EMP_ID)
 WHEN MATCHED THEN
UPDATE
   SET M1.EMP_NAME = M2.EMP_NAME
     , M1.EMP_NO = M2.EMP_NO
     , M1.EMAIL = M2.EMAIL
     , M1.PHONE = M2.PHONE
     , M1.DEPT_CODE = M2.DEPT_CODE
     , M1.JOB_CODE = M2.JOB_CODE
     , M1.SAL_LEVEL = M2.SAL_LEVEL
     , M1.SALARY = M2.SALARY
     , M1.BONUS = M2.BONUS
     , M1.MANAGER_ID = M2.MANAGER_ID
     , M1.HIRE_DATE = M2.HIRE_DATE
     , M1.ENT_DATE = M2.ENT_DATE
     , M1.ENT_YN = M2.ENT_YN
 WHEN NOT MATCHED THEN
INSERT
(
  M1.EMP_ID, M1.EMP_NAME, M1.EMP_NO, M1.EMAIL, M1.PHONE
, M1.DEPT_CODE, M1.JOB_CODE, M1.SAL_LEVEL, M1.SALARY, M1.BONUS
, M1.MANAGER_ID, M1.HIRE_DATE, M1.ENT_DATE, M1.ENT_YN
)
VALUES
(
  M2.EMP_ID, M2.EMP_NAME, M2.EMP_NO, M2.EMAIL, M2.PHONE
, M2.DEPT_CODE, M2.JOB_CODE, M2.SAL_LEVEL, M2.SALARY, M2.BONUS
, M2.MANAGER_ID, M2.HIRE_DATE, M2.ENT_DATE, M2.ENT_YN
);

--5개 행 이(가) 병합되었습니다.



SELECT
       EM.*
  FROM EMP_M01 EM;

 

 

 

 

 

 

 

 

 

 

UPDATE


  • 테이블에 기록된 컬럼의 값을 수정하는 구문이다. 테이블의 전체 행 개수에는 변화가 없다.

 

WHERE 조건을 설정하지 않으면 모든 행의 컬럼 값이 변경된다.
 CREATE TABLE DEPT_COPY
    AS SELECT
   		 *
   		 FROM DEPARTMENT;

 

 

    UPDATE
   		 DEPT_COPY
		 SET DEPT_TITLE = ‘전략기획팀’
    WHERE DEPT_ID = 'D9';

 

 

 

  • UPDATE시에도 서브쿼리를 이용할 수 있다.
평상시 유재식 사원을 부러워하던 방명수 사원의 급여와 보너스율을 유재식 사원과 동일하게 변경해 주기로 했다. 이를 반영하는 UPDATE문을 작성하세요.

 

 CREATE TABLE EMP_SALARY
    AS SELECT
    EMP_ID
    , EMP_NAME
    , DEPT_CODE
    , SALARY
    , BONUS
  FROM EMPLOYEE;

 

 UPDATE
    EMP_SALARY
 SET SALARY = (SELECT
        SALARY
        FROM EMP_SALARY
        WHERE EMP_NAME= '유재식’),
    BONUS = (SELECT
        BONUS
        FROM EMP_SALARY
        WHERE EMP_NAME=‘유재식’)
 WHERE EMP_NAME = '방명수';

 

 

SELECT
    *
    FROM EMP_SALARY
    WHERE EMP_NAME IN (‘유재식’,’방명수’);

 

[ UPDATE 적용 전 ]

 

 

[ UPDATE 적용 후 ]

 

 

 

각각 쿼리문 작성한 것을 다중행 다중열 서브쿼리로 변경하세요.

 

 UPDATE
        EMP_SALARY
        SET (SALARY, BONUS) = (SELECT
        SALARY
        , BONUS
        FROM EMP_SALARY
        WHERE EMP_NAME - '유재식’)
        WHERE EMP_NAME = '방명수';


 SELECT
        *
        FROM EMP_SALARY
        WHERE EMP_NAME IN (‘유재식’,’방명수’);

 

 

 

EMP_SALARY 테이블에서 아시아 지역에 근무하는 직원의 보너스 포인트를 0.3으로 변경하세요.

 

 UPDATE
    EMP_SALARY
    SET BONUS = 0.3
    WHERE EMP_ID IN (SELECT
            EMP_ID
            FROM EMPLOYEE
            JOIN DEPARTMENT
            ON (DEPT_ID = DEPT_CODE)
            JOIN LOCATION
            ON (LOCATION_ID = LOCAL_CODE)
            WHERE LOCAL_NAME LIKE 'ASIA%’);

 

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기
더보기

 

-- UPDATE : 테이블에 기록 된 컬럼의 값을 수정하는 구문이다. 
--          테이블의 전체 행 갯수는 변화가 없다. 

-- 테스트할 테이블을 만들기
CREATE TABLE DEPT_COPY
AS
SELECT D.*
  FROM DEPARTMENT D;
-- DEPT_COPY이(가) 생성되었습니다.




-- UPDATE의 문법 : 
-- UPDATE 테이블명 SET 컬럼명 = 바꿀값, 컬럼명 = 바꿀값, ...
-- WHERE 절이 없으면 모든 행이 대상이 된다. 
-- [WHERE 컬럼명 비교연산자 비교값];

SELECT
        DC.*
  FROM DEPT_COPY DC;

UPDATE
        DEPT_COPY DC
   SET DC.DEPT_TITLE = '전략기획팀'
 WHERE DC.DEPT_ID = 'D9';

SELECT
        DC.*
  FROM DEPT_COPY DC;

 

-- UPDATE에서도 서브쿼리를 사용할 수 있다. 
-- UPDATE 테이블명
-- SET 컬럼명 = (서브쿼리)

CREATE TABLE EMP_SALARY
AS
SELECT E.EMP_ID
  , E.EMP_NAME
  , E.DEPT_CODE
  , E.SALARY
  , E.BONUS
  FROM EMPLOYEE E;
  
  
SELECT
        ES.*
FROM EMP_SALARY ES
WHERE ES.EMP_NAME IN ('유재식', '방명수');

 

-- 평상시 유재식 사원을 부러워 하던 방명수 사원의 
-- 급여와 보너스율을 유재식 사원과 동일하게 변경해 주기로 했다.
-- 이를 반영하는 UPDATE 문을 작성해보세요. 

UPDATE 
        EMP_SALARY ES
        SET ES.SALARY = (SELECT E1.SALARY
                           FROM EMPLOYEE E1
                          WHERE E1.EMP_NAME = '유재식'
                          )
        , ES.BONUS = (SELECT E2.BONUS
                           FROM EMPLOYEE E2
                          WHERE E2.EMP_NAME = '유재식'
                          )
  WHERE ES.EMP_NAME = '방명수';
  
SELECT
        ES.*
FROM EMP_SALARY ES
WHERE ES.EMP_NAME IN ('유재식', '방명수');

 

-- 다중열 서브쿼리를 이용한 UPDATE문
-- 방명수 사원의 급여인상 소식을 전해들은 다른 직원들이 
-- 단체로 파업을 진행했다. 
-- 노옹철, 전형돈, 정중하, 하동운 사원의 급여와 보너스를
-- 유재식 사원의 급여와 보너스와 같게 변경하는 UPDATE문 작성 

UPDATE
        EMP_SALARY ES
    SET (ES.SALARY, ES.BONUS) = (SELECT E1.SALARY, E1.BONUS
                                   FROM EMPLOYEE E1
                                  WHERE E1.EMP_NAME = '유재식'
                                  )
 WHERE ES.EMP_NAME IN ('노옹철', '전형돈', '정중하', '하동운') ;                                

SELECT
        ES.*
  FROM EMP_SALARY ES
 WHERE ES.EMP_NAME IN ('유재식', '방명수', '노옹철', '전형돈', '정중하', '하동운');

 

 

 

-- 다중행 서브쿼리를 이용한 UPDATE
-- EMP_SALARY 테이블에서 아시아 근무 지역에 근무하는 직원의
-- 보너스를 0.5로 변경하세요.


UPDATE 
       EMP_SALARY ES
   SET ES.BONUS = 0.5
 WHERE ES.EMP_ID IN (SELECT E1.EMP_ID
                       FROM EMPLOYEE E1
                       JOIN DEPARTMENT D1 ON(E1.DEPT_CODE = D1.DEPT_ID)
                       JOIN LOCATION L1 ON (D1.LOCATION_ID = L1.LOCAL_CODE)
                      WHERE L1.LOCAL_NAME LIKE 'ASIA%'
                    );

SELECT
       ES.*
  FROM EMP_SALARY ES
 WHERE ES.EMP_ID IN (SELECT E1.EMP_ID
                       FROM EMPLOYEE E1
                       JOIN DEPARTMENT D1 ON(E1.DEPT_CODE = D1.DEPT_ID)
                       JOIN LOCATION L1 ON (D1.LOCATION_ID = L1.LOCAL_CODE)
                      WHERE L1.LOCAL_NAME LIKE 'ASIA%'
                    );

 

-- UPDATE 시 변경 값은 해당 컬럼에 대한 제약 조건에 위배되지 않아야 함
UPDATE
        EMPLOYEE E
-- FOREIGN KEY 제약 조건에 위배됨 (부모키가 없습니다.)
   SET E.DEPT_CODE = '65'
 WHERE E.DEPT_CODE = 'D6';

UPDATE
    EMPLOYEE E
    -- NOT NULL 제약 조건에 위배됨
  SET E.EMP_NAME = NULL
  WHERE E.EMP_ID = '200';

UPDATE
        EMPLOYEE E
   -- UNIQUE 제약 조건에 위배됨      
   SET E.EMP_NO = '800808-2123341'
   WHERE E.EMP_ID = '200';

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DELETE


  • 테이블의 행을 삭제하는 구문이다. 테이블의 행 개수가 줄어든다.
  • WHERE 조건을 설정하지 않으면 모든 행이 삭제된다.
DELETE
FROM EMPLOYEE
WHERE EMP_NAME = '장채현';

 

 

  • FOREIGN KEY 제약조건이 설정되어 있는 경우 참조되고 있는 값에 대해서는 삭제 할 수 없다.
DELETE
FROM DEPARTMENT
WHERE DEPT_ID = 'D1';

 

  • FOREIGN KEY 제약조건이 설정되어 있는 경우 참조되고 있지 않은 값에 대해서는 삭제할 수 있다.
DELETE
FROM DEPARTMENT
WHERE DEPT_ID = ‘D3';

 

  • 비활성화 된 제약조건을 다시 활성화 시킬 수 있다.
ALTER TABLE EMPLOYEE
ENABLE CONSTRAINT FK_DEPT_ID CASCADE;

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기
더보기

 

-- DELETE : 테이블의 행을 삭제하는 구문이다.
--          테이블의 행의 갯수가 줄어든다.
-- DELETE FROM 테이블명 WHERE 조건설정
-- 만약 WHERE 조건을 설정하지 않으면 모든 행이 다 삭제된다. 
COMMIT;
--커밋 완료.

DELETE
    FROM EMPLOYEE E;
-- 24개 행 이(가) 삭제되었습니다.

SELECT
        E.*
    FROM EMPLOYEE E;
    
-- COMMIT 시점을 만들었기에 되돌리기 가능
ROLLBACK;
-- 롤백 완료.


DELETE
  FROM EMPLOYEE E
 WHERE E.EMP_NAME = '장채현';
-- 1 행 이(가) 삭제되었습니다.



SELECT
     E.*
  FROM EMPLOYEE E;
    
    
    
ROLLBACK;
-- 롤백 완료.




-- FOEIGN KEY 제약조건이 설정 되어 있는 경우
-- 참조되고 있는 값에 대해서는 삭제할 수 없다.

DELETE 
  FROM DEPARTMENT D
 WHERE D.DEPT_ID = 'D1';





-- FOEIGN KEY 제약조건이 설정되어있어도
-- 참조되고 있지 않는 값에 대해서는 삭제 가능
DELETE 
  FROM DEPARTMENT D
 WHERE D.DEPT_ID = 'D3';
-- 1 행 이(가) 삭제되었습니다.



-- 삭제 된 결과 조회하기 
SELECT
        D.*
  FROM DEPARTMENT D;



ROLLBACK;
-- 롤백 완료.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TRUNCATE


  • 테이블의 전체 행을 삭제 시 사용한다. DELETE문 보다 수행 속도가 빠르지만, ROLLBACK을 통해 복구를 할 수 없다.
  • 또한, DELETE와 마찬가지로 FOREIGN KEY 제약조건일 때는 적용 불가능하기 때문에 제약조건을 비활성화 해야 삭제를 할 수 있다.

 

TRUNCATE TABLE EMP_SALARY;

 

SELECT
*
FROM EMP_SALARY;


ROLLBACK;

** 모든 컬럼이 삭제 되기는 하지만, 테이블의 구조는 남아 있다.

** ROLLBACK 수행 후에도 컬럼이 복구되지 않는다.

 

ROLLBACK;



 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기
더보기

 

--TRUNCATE : 테이블의 전체행을 삭제할 시 사용한다.
--           DELETE 보다 수행속도가 더 빠르다.
--           ROLLBACK을 통해 복수할 수 없다. 
-- DDL의 종류에 해당한다. 

SELECT
        ES.*
  FROM EMP_SALARY ES;
    
COMMIT;


DELETE
  FROM EMP_SALARY ES;
  
SELECT
  ES.*
FROM EMP_SALARY ES;

-- 롤백완료
ROLLBACK;

-- 복구된결과 조회
SELECT
  ES.*
FROM EMP_SALARY ES;


-- 테이블 지우기 
TRUNCATE TABLE EMP_SALARY;
-- Table EMP_SALARY이(가) 잘렸습니다.


-- 잘린 테이블 확인 
SELECT
  ES.*
FROM EMP_SALARY ES;


-- 롤백완료
ROLLBACK;


-- 롤백을 해도 행들이 돌아오지않음을 확인 
SELECT
  ES.*
FROM EMP_SALARY ES;

 

 

 

 

 

 

 

 

 

TCL (Transaction Control Language)


  • 트랜잭션이란 한꺼번에 수행되어야 할 최소의 작업 단위를 말한다.
  • COMMIT과 ROLLBACK이 있다.
  1. COMMIT :  트랜잭션 작업이 정상 완료되면 변경 내용을 영구히 저장
  2. ROLLBACK : 트랜잭션 작업을 취소하고 최근 COMMIT한 시점으로 이동 
  3. SAVEPOINT 세이브포인트명 : 현재 트랜잭션 작업 시점에 이름을 정해줌, 하나의 트랜잭션 안에 구역을 나눔
  4. ROLLBACK TO 세이브 포인트명 : 트랜잭션 작업을 취소하고 SAVEPOINT 지점으로 이동

 

 

 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기
더보기

 

 

-- TCL (Transaction Control Language)
-- 트랜잭션 제어 언어 
-- commit과 rollback이 있다.

-- 트랜잭션이란?
-- 한꺼번에 수행되어야 할 최소의 작업 단위를 말한다.
-- 논리적 작업 단위 (Logical Unit of Work : LUW)
-- 하나의 트랜잭션으로 이루어진 작업은 반드시 한꺼번에 완료 (COMMIT)
-- 되어야 하며, 그렇지 않은 경우에는 한번에 취소 (ROLLBACK) 되어야 함

-- COMMIT :  트랜잭션 작업이 정상 완료되면 변경 내용을 영구히 저장
-- ROLLBACK : 트랜잭션 작업을 취소하고 최근 COMMIT한 시점으로 이동 
-- SAVEPOINT 세이브포인트명 : 현재 트랜잭션 작업 시점에 이름을 정해줌
--                          하나의 트랜잭션 안에 구역을 나눔
-- ROLLBACK TO 세이브 포인트명 : 트랜잭션 작업을 취소하고 SAVEPOINT 지점으로 이동


CREATE TABLE TBL_USER(
  USENO NUMBER UNIQUE,
  ID VARCHAR2(20) PRIMARY KEY,
  PASSWORD CHAR(20) NOT NULL
  );

-- Table TBL_USER이(가) 생성되었습니다.

INSERT
   INTO TBL_USER A
   (
   A.USENO
   , A.ID
   , A.PASSWORD
   )
   VALUES
   (
   1
   , 'test1'
   , 'pass1'
   );
   
-- 1 행 이(가) 삽입되었습니다.

INSERT
   INTO TBL_USER A
   (
   A.USENO
   , A.ID
   , A.PASSWORD
   )
   VALUES
   (
   2
   , 'test2'
   , 'pass2'
   );

-- 1 행 이(가) 삽입되었습니다.

INSERT
   INTO TBL_USER A
   (
   A.USENO
   , A.ID
   , A.PASSWORD
   )
   VALUES
   (
   3
   , 'test3'
   , 'pass3'
   );

-- 1 행 이(가) 삽입되었습니다.

COMMIT;

SELECT
        UT.*
  FROM TBL_USER UT;

 

 

INSERT
   INTO TBL_USER A
   (
   A.USENO
   , A.ID
   , A.PASSWORD
   )
   VALUES
   (
   4
   , 'test4'
   , 'pass4'
   );
-- 1 행 이(가) 삽입되었습니다.

SELECT
        UT.*
  FROM TBL_USER UT;

 

ROLLBACK;
-- 롤백 완료.

SELECT
        UT.*
  FROM TBL_USER UT;

 

SELECT
       UT.*
  FROM TBL_USER UT;

INSERT
  INTO TBL_USER A
(
  A.USERNO
, A.ID
, A.PASSWORD
)
VALUES
(
  4
, 'test4'
, 'pass4'
);

SAVEPOINT SP1;

INSERT
  INTO TBL_USER A
(
  A.USERNO
, A.ID
, A.PASSWORD
)
VALUES
(
  5
, 'test5'
, 'pass5'
);

SELECT
       UT.*
  FROM TBL_USER UT;

ROLLBACK TO SP1;

SELECT
       UT.*
  FROM TBL_USER UT;
  
ROLLBACK;

SELECT
       UT.*
  FROM TBL_USER UT;

 

 

 

 

 

 

 

 

 

 

DDL (DATA DEFINITION LANGUAGE)


 

▼ 코드 예시 보기 ▼ (더보기 클릭)
더보기
더보기

 

-- DDL (DATA DEFINITION LANGUAGE)
-- ALTER : 객체를 수정하는 구문
-- 테이블 객체 수정 : ALTER TABLE 테이블명 수정할 내용
-- 컬럼 추가/삭제/변경, 제약조건 추가/삭제/변경
-- 테이블명 변경, 제약조건 이름 변경

-- 컬럼추가
-- 테이블 조회

SELECT
        DC.*
  FROM DEPT_COPY DC;
  
  
-- 내용 변경
ALTER TABLE DEPT_COPY
ADD (LNAME VARCHAR2(20));
-- Table DEPT_COPY이(가) 변경되었습니다.

-- 변경된 테이블 조회
SELECT
        DC.*
  FROM DEPT_COPY DC;

 

 

-- 컬럼 삭제
ALTER TABLE DEPT_COPY
DROP COLUMN LNAME;
-- Table DEPT_COPY이(가) 변경되었습니다.  
  
  
-- 삭제된 테이블 조회 
  SELECT
        DC.*
  FROM DEPT_COPY DC;

 

-- 컬럼 생성시 DEFAULT 값 지정 
-- 20 바이트 컬럼 추가 ADD
ALTER TABLE DEPT_COPY
ADD (CNAME VARCHAR2(20) DEFAULT '한국');
-- Table DEPT_COPY이(가) 변경되었습니다.
  
  
-- 추가되어 변경된 테이블 조회
SELECT
        DC.*
FROM DEPT_COPY DC;

 

-- 컬럼에 제약조건 추가
CREATE TABLE DEPT_COPY2
AS
SELECT D.*
  FROM DEPARTMENT D;
-- Table DEPT_COPY2이(가) 생성되었습니다.
  
  
-- 테이블 조회
SELECT
        DC.*
  FROM DEPT_COPY2 DC;

 

-- 테이블 변경
ALTER TABLE DEPT_COPY2
ADD CONSTRAINT PK_DEPT_ID2 PRIMARY KEY(DEPT_ID);
-- Table DEPT_COPY2이(가) 변경되었습니다.
  
  
ALTER TABLE DEPT_COPY2
ADD CONSTRAINT UN_DEPT_TITLE2 UNIQUE(DEPT_TITLE);
-- Table DEPT_COPY2이(가) 변경되었습니다.
  
ALTER TABLE DEPT_COPY2
MODIFY DEPT_TITLE CONSTRAINT NN_LID NOT NULL;
-- Table DEPT_COPY2이(가) 변경되었습니다.

 

 

  
-- 컬럼 자료형 수정
ALTER TABLE DEPT_COPY2
MODIFY DEPT_ID CHAR(3)
MODIFY DEPT_TITLE VARCHAR(30)
MODIFY LOCATION_ID VARCHAR(2);


-- 컬럼의 크기를 줄이는 경우에는 
-- 변경하려는 크기를 초과하는 값이 없을 때만 변경할 수 있다. 
-- 수정
ALTER TABLE DEPT_COPY2
MODIFY DEPT_TITLE VARCHAR2(10);

-- 오류 보고 -
-- ORA-01441: 일부 값이 너무 커서 열 길이를 줄일 수 없음
  
  
-- DEFAULT 값 변경
ALTER TABLE DEPT_COPY
MODIFY CNAME DEFAULT '미국';

INSERT
  INTO DEPT_COPY
VALUES
  (
'D0'
, '생산부'
, 'L2'
, DEFAULT
  );
  
-- 1 행 이(가) 삽입되었습니다.  
  
  
-- 테이블 조회
SELECT
        DC.*
FROM DEPT_COPY DC;
  
  
  
  
-- 컬럼 삭제
ALTER TABLE DEPT_COPY2
DROP COLUMN DEPT_TITLE;
-- Table DEPT_COPY2이(가) 변경되었습니다.
  
  
  
-- 사라진 테이블 조회 
SELECT
        DC.*
FROM DEPT_COPY2 DC;
  
  
-- 추가 삭제
ALTER TABLE DEPT_COPY2
DROP COLUMN LOCATION_ID;
-- Table DEPT_COPY2이(가) 변경되었습니다.
  
-- 삭제된 테이블 재조회
SELECT
        DC.*
FROM DEPT_COPY2 DC;
  

-- DEPT_ID 삭제를 시도해보자
ALTER TABLE DEPT_COPY2
DROP COLUMN DEPT_ID;
-- 오류 보고 -
-- ORA-12983: 테이블에 모든 열들을 삭제할 수 없습니다

-- 테이블에 최소 한 개 이상의 컬럼이 남아있어야 하므로
-- 모든 열을 삭제할 수 없음 
  
  
  
  
-- 참조관련 테스트 
-- 테이블 생성
CREATE TABLE TB1 ( 
  PK NUMBER PRIMARY KEY,
  FK NUMBER REFERENCES TB1,
COL1 NUMBER,
CHECK (PK > 0 AND COL1 >0 )
);
-- Table TB1이(가) 생성되었습니다.


-- 컬럼 삭제 시 참조하고 있는 컬럼이 있다면 삭제를 못한다. 
ALTER TABLE TB1
DROP COLUMN PK;
  
  
-- 제약조건도 함께 삭제한다면 삭제할 수 있다. 
ALTER TABLE TB1
DROP COLUMN PK CASCADE CONSTRAINTS;
--Table TB1이(가) 변경되었습니다.
  
SELECT
        T.*
  FROM TB1 T; 
  
  -- 컬럼 삭제 :  DROP COLUMN 삭제할 컬럼명 또는 DROP (삭제할 컬럼명)
  -- 데이터가 기록 되어 있어도 삭제 됨
  -- 삭제된 컬럼은 복구가 불가능
  -- 테이블에는 최소 한개 이상의 컬럼이 존재해야 함 : 모든 컬럼 삭제 불가능
  
  SELECT
        DC.*
    FROM DEPT_COPY DC;
    
ALTER TABLE DEP_COPY
DROP (CNAME);

-- 테이블 조회 
SELECT
     DC.*
 FROM DEPT_COPY DC;
 
 
 ALTER TABLE DEPT_COPY
 DROP (DEPT_TITLE, LOCATION_ID);
  -- Table DEPT_COPY이(가) 변경되었습니다.
  
  
  
  SELECT
       DC.*
  FROM DEPT_COPY DC;
  
  
  
ROLLBACK;



SELECT
       DC.*
  FROM DEPT_COPY DC;




-- 제약조건 삭제
CREATE TABLE CONST_EMP(
  ENAME VARCHAR2(20) NOT NULL,
  ENO VARCHAR2(15) NOT NULL,
  MARRIAGE CHAR(1) DEFAULT 'N',
  EID CHAR(3),
  EMAIL VARCHAR2(30),
  JID CHAR(2),
  MID CHAR(3),
  DID CHAR(2),
  -- 테이블 레벨로 제약조건 설정
  CONSTRAINT CK_MARRIAGE CHECK(MARRIAGE IN('Y', 'N')),
  CONSTRAINT PK_EID PRIMARY KEY(EID),
  CONSTRAINT UN_ENO UNIQUE(ENO),
  CONSTRAINT UN_EAMIL UNIQUE(EMAIL),
  CONSTRAINT FK_JID FOREIGN KEY(JID) REFERENCES JOB(JOB_CODE) ON DELETE SET NULL,
  CONSTRAINT FK_MID FOREIGN KEY(MID) REFERENCES CONST_EMP ON DELETE SET NULL,
  CONSTRAINT FK_DID FOREIGN KEY(DID) REFERENCES DEPARTMENT ON DELETE CASCADE
);

-- 제약조건 1개 삭제시
ALTER TABLE CONST_EMP
DROP CONSTRAINT CK_MARRIAGE;

-- 제약조건 여러개 삭제시
ALTER TABLE CONST_EMP
DROP CONSTRAINT FK_DID
DROP CONSTRAINT FK_JID
DROP CONSTRAINT FK_MID;

-- NOT NULL 제약 조건 삭제 시 MODIFY 사용
ALTER TABLE CONST_EMP
MODIFY (ENAME NULL, ENO NULL);

-- 컬럼 이름 변경
CREATE TABLE DEPT_COPY3
AS SELECT * FROM DEPARTMENT;

ALTER TABLE DEPT_COPY3
RENAME COLUMN DEPT_ID TO DEPT_CODE;

SELECT
       DC.*
  FROM DEPT_COPY3 DC;
  
-- 제약조건 이름 변경
ALTER TABLE DEPT_COPY3
ADD CONSTRAINT PK_DEPT_CODE3 PRIMARY KEY(DEPT_CODE);

ALTER TABLE DEPT_COPY3
RENAME CONSTRAINT PK_DEPT_CODE3 TO PK_DCODE;

-- 테이블 이름 변경
ALTER TABLE DEPT_COPY3
RENAME TO DEPT_TEST;

SELECT
       DC.*
  FROM DEPT_COPY3 DC;

SELECT
       DT.*
  FROM DEPT_TEST DT;
  
-- 테이블 삭제
DROP TABLE DEPT_TEST CASCADE CONSTRAINTS;

 

 

 

 

 

 

 

 

 

 

 

'Programming > ORACLE' 카테고리의 다른 글

동의어(SYNONTM) & 권한과 롤  (0) 2022.01.27
VIEW & SEQUENCE & INDEX  (0) 2022.01.27
DDL(Data Definition Language)  (0) 2022.01.24
서브 쿼리 ( Subquery)  (0) 2022.01.21
JOIN  (0) 2022.01.19

 

 

 

 

DDL(Data Definition Language)

 

 

 

 

DDL이란?


  • 데이터 정의 언어이다. 객체(Object)를 만들고(CREATE),수정하고(ALTER),삭제(DROP)하는 구문을 말한다.

 

 

 

 

 

오라클 객체의 종류


  • 테이블(TABLE),뷰(VIEW),시퀀스(SEQUENCE),인덱스(INDEX),패키지(PACKAGE),프로시져(PROCEDUAL),함수(FUNCTION),트리거(TRIGGER),동의어(SYNONYM), 사용자(USER)가 있다.

 

 

 

 

 

 

CREATE – 테이블만들기


 

CREATE TABLE MEMBER(
	MEMBER_ID VARCHAR2(20),
	MEMBER_PWD VARCHAR2(20),
	MEMBER_NAME VARCHAR2(20)
);

 

 

-- DDL (CREAT TABLE) 및 제약 조건


-- DDL (DATA DEFINITION LANGUAGE) 데이터 정의 언어
-- 객체(OBJECT)를 만들고(CREAT), 수정(ALTER)하고, 삭제(DROP)하는 구문

-- 테이블 만들기
-- [표현식] : 
-- CREATE TABLE 테이블명 (컬럼명 자료형(크기), 컬럼명 자료형(크기), ...);

CREATE TABLE MEMBER(
    MEMBER_ID VARCHAR2(20),
    MEMBER_PWD VARCHAR2(20),
    MEMBER_NAME VARCHAR2(20)
    );

SELECT
        M.*
    FROM MEMBER M;

-- 컬럼에 주석 달기
-- [표현식]
-- COMMENTS ON CLOUME 테이블명.컬럼명 IS '주석내용';
COMMENT ON COLUMN MEMBER.MEMBER_ID IS '회원아이디';
COMMENT ON COLUMN MEMBER.MEMBER_PWD IS '비밀번호';
COMMENT ON COLUMN MEMBER.MEMBER_NAME IS '회원이름';


-- 데이터 딕셔너리
-- 현재 이 계정이 가지고 있는 테이블들에 대하여 조회 한다는 뜻
SELECT
        UT.*
    FROM USER_TABLES UT;

-- 컬럼들에 대하여 조회
SELECT
        UTC.*
    FROM USER_TAB_COLUMNS UTC
    WHERE UTC.TABLE_NAME = 'MEMBER';

 

 

 

 

 

 

오라클의 데이터형


 

데이터형 설명
CHAR(크기) 고정길이 문자 데이터
VARCHAR2(크기) 가변길이 문자 데이터(최대 2,000 Byte)
NUMBER 숫자 데이터(최대 40자리)
NUMBER(길이) 숫자 데이터로, 길이 지정 가능하다 (최대 38자리)
DATE 날짜 데이터(BC 4712년 1월 1일 ~ AD 4712년 12월 31일)
LONG 가변 길이 문자형 데이터(최대 2GB)
LOB 2GB까지의 가변길이 바이너리 데이터 저장 가능(이미지, 실행파일 등을 저장할 수 있음)
ROWID DB에 저장되지 않는 행을 식별할 수 있는 고유 값
BFILE 대용량의 바이너리 데이터 저장 가능(최대 4GB)
TIMESTAMP DATE형의 확장된 형태이다.
INTERVAL YEAR TO MONTH 년과 월을 이용하여 기간을 저장한다.
INTERVAL DAY TO SECONT 일, 시, 분, 초를 이용하여 기간을 저장한다.

 

 

 

 

 

 

 

 

컬럼 주석


[ 표현식 ]
     COMMENT ON CULUMN 테이블명.컬럼명 IS ‘주석내용’ ;

 

COMMENT ON COLUMN MEMBER.MEMBER_ID IS ‘회원아이디’;
COMMENT ON COLUMN MEMBER.MEMBER_PWD IS ‘비밀번호’;
COMMENT ON COLUMN MEMBER.MEMBER_NAME IS ‘회원이름’;

 

 

 

 

 

 

 

제약 조건(CONSTRAINTS)


  • 테이블 작성 시 각 컬럼에 대한 기록에 대해 제약조건을 설정할 수 있다.
  • 데이터 무결성 보장이 주 목적이다.
  • 입력 데이터에 문제가 없는지 검사와 데이터의 수정/삭제 가능 여부 검사 등에 사용한다.

 

제약 조건 설명
NOT NULL 데이터에 NULL을 허용하지 않는다.
UNIQUE 중복된 값을 허용하지 않는다.
PRIMARY KEY NULL을 허용하지 않고, 중복 값을 허용하지 않는다.
컬럼의 고유 식별자로 사용하기 위함이다.
FOREIGN KEY 참조되는 테이블의 컬럼의 값이 존재하면 허용한다.
CHECK 저장 가능한 데이터 값의 범위나 조건을 지정하여 설정한 값만 허용한다.

 

 

 

-- 제약조건
-- 테이블 작성 시 각 컬럼에 대해 값 기록에 대한 제약조건을 설정할 수 있다.
-- '이 테이블에는 어떠한 조건만 들어와야 됩니다.'
-- 데이터 무결성 보장을 목적으로 함
-- 입력/수정하는 데이터에 문제가 없는지 자동으로 검사하는 목적
-- 5가지 제약 조건 :
-- PRIMARY KEY, NOT NULL, UNIQUE, CHECK, FOREIGN KEY

SELECT
        UC.*
    FROM USER_CONSTRAINTS UC;
    
    
SELECT
        UCC.*
    FROM USER_CONS_COLUMNS UCC;

 

 

 

 

 

 

 

 

제약 조건(CONSTRAINTS) 확인


 

 

 

 

 

 

 

 

NOT NULL


  • 해당 컬럼에 반드시 값이 기록되어야 하는 경우, 특정 컬럼에 값을 저장하거나 수정할 때 NULL값을 허용하지 않도록 컬럼 레벨에서 제한한다.

 

CREATE TABLE
USER_NOCONS(
	USER_NO NUMBER,
	USER_ID VARCHAR2(20),
	USER_PWD VARCHAR2(30),
	USER_NAME VARCHAR2(30),
	GENDER VARCHAR2(10),
	PHONE VARCHAR2(30),
	EMAIL VARCHAR2(50)
);

 

 

 

 

 

 

 

 

컬럼에 아무 제약조건을 설정하지 않을 경우, NULL값이 문제 없이 삽입됨

 

 

 

 

CREATE TABLE USER_NOTNULL(
	USER_NO NUMBER NOT NULL,
	USER_ID VARCHAR2(20) NOT NULL,
	USER_PWD VARCHAR2(30) NOT NULL,
	USER_NAME VARCHAR2(30),
	GENDER VARCHAR2(10),
	PHONE VARCHAR2(30),
	EMAIL VARCHAR2(50)
);

 

 

INSERT
	INTO USER_NOCONS
	VALUES
	(
	1
	, ’user01’
	, ‘pass01’
	, ‘홍길동’
	, ‘남’
	, ‘010-1234-5678’
	, ‘hong123@greedy.com’
);

 

 

 

INSERT
	INTO USER_NOCONS
	VALUES
	(
	2
	, NULL
	, NULL
	, NULL
	, NULL
	, ‘010-1234-5678’
	, ‘hong123@greedy.com’
);

 

 

 

 

 

 

** NOT NULL 제약조건이 설정된 컬럼에 NULL값이 입력되면, 행 자체를 삽입하지 않는다.

 

 

 

-- NOT NULL : 해당 컬럼에 반드시 값이 기록되어야 하는 경우 사용
--            삽입/수정 시 NULL 값을 허용하지 않도록
--            컬럼 레벨에서 제한

CREATE TABLE USER_NOCONS (
    USER_NO NUMBER,
    USER_ID VARCHAR2(20),
    USER_PWD VARCHAR2(30),
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50)
);

-- INSERT INTO 테이블명 (컬럼명) VALUES (들어갈 값)
INSERT
    INTO USER_NOCONS
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES --실질적으로 들어가는 값
(
  1
, 'USER01'
, 'PASS01'
, '홍길동'
, '남'
, '010-1234-4567'
, 'hong@naver.com'
);

-- 1행 이 (가) 삽입 되었습니다.



INSERT
    INTO USER_NOCONS
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES --실질적으로 들어가는 값
(
  2
, NULL
, NULL
, NULL
, NULL
, '010-1234-4567'
, 'hong@naver.com'
);

SELECT
    UN.*
    FROM USER_NOCONS UN;
-- 행이 조회되는 것을 확인할 수 있다.

 

 

-- 컬럼 레벨에 NOT NULL 제약 조건 설정
CREATE TABLE USER_NOTNULL (
    USER_NO NUMBER NOT NULL,
    USER_ID VARCHAR2(20) NOT NULL,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30) NOT NULL,
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50)
);

-- Table USER_NOTNULL이(가) 생성되었습니다.


SELECT
       UC.*
     , UCC.*
  FROM USER_CONSTRAINTS UC
  JOIN USER_CONS_COLUMNS UCC ON(UC.CONSTRAINT_NAME = UCC.CONSTRAINT_NAME)
 WHERE UC.TABLE_NAME = 'USER_NOTNULL';



INSERT
  INTO USER_NOTNULL
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user01'
, 'pass01'
, NULL
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);


--오류 보고 -
--ORA-01400: NULL을 ("C##EMPLOYEE"."USER_NOTNULL"."USER_NAME") 안에 삽입할 수 없습니다

 

 

 

 

 

 

 

 

 

UNIQUE


  • 컬럼에 입력 값에 대해 중복을 제한하는 제약조건이다. 컬럼레벨과 테이블레벨에 설정 가능하다.

 

CREATE TABLE USER_NOCONS(
	USER_NO NUMBER,
	USER_ID VARCHAR2(20),
	USER_PWD VARCHAR2(30),
	USER_NAME VARCHAR2(30),
	GENDER VARCHAR2(10),
	PHONE VARCHAR2(30),
	EMAIL VARCHAR2(50)
);

 

 

INSERT
	INTO USER_NOCONS
	VALUES
	(
	1
	, ’user01’
	, ‘pass01’
	, ‘홍길동’
	, ‘남’
	, ‘010-1234-5678’
	, ‘hong123@greedy.com’
);

 

 

 

 

 

UNIQUE 제약조건이 없는 컬럼은 중복된 값도 저장을 허용한다.

 

 

 

CREATE TABLE USER_UNIQUE(
	USER_NO NUMBER,
	USER_ID VARCHAR2(20) UNIQUE, --컬럼레벨
	USER_PWD VARCHAR2(30),
	USER_NAME VARCHAR2(30),
	GENDER VARCHAR2(10),
	PHONE VARCHAR2(30),
	EMAIL VARCHAR2(50)
);

 

 

 

INSERT
	INTO USER_UNIQUE
	VALUES
	(
	1
	, ’user01’
	, ‘pass01’
	, ‘홍길동’
	, ‘남’
	, ‘010-1234-5678’
	, ‘hong123@greedy.com’
);

 

 

 

 

 

 

 

 

 

-- UNIQUE 제약 조건 : 컬럼의 입력 값에 대해 중복을 제한하는 제약조건
--                   컬럼 레벨에서 설정 가능, 테이블 레벨에서 설정 가능

SELECT
       UN.*
  FROM USER_NOCONS UN;

 

INSERT
  INTO USER_NOCONS
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user01'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);


-- 1 행 이(가) 삽입되었습니다.

 

 

-- UNIQUE 제약 조건 컬럼 레벨 설정
CREATE TABLE USER_UNIQUE(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20) UNIQUE NOT NULL,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50)
);


-- Table USER_UNIQUE이(가) 생성되었습니다.

 

INSERT
  INTO USER_UNIQUE
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user01'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);

INSERT
  INTO USER_UNIQUE
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user01'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);

SELECT
       UCC.TABLE_NAME
     , UCC.COLUMN_NAME
     , UC.CONSTRAINT_TYPE
  FROM USER_CONSTRAINTS UC
     , USER_CONS_COLUMNS UCC
 WHERE UC.CONSTRAINT_NAME = UCC.CONSTRAINT_NAME
   AND UCC.CONSTRAINT_NAME = 'SYS_C007353';

 

 

-- UNIQUE 제약 조건 테이블 레벨 설정
CREATE TABLE USER_UNIQUE2(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20) NOT NULL,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50),
    UNIQUE(USER_ID)
);

INSERT
  INTO USER_UNIQUE2
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user01'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);

SELECT
       UCC.TABLE_NAME
     , UCC.COLUMN_NAME
     , UC.CONSTRAINT_TYPE
  FROM USER_CONSTRAINTS UC
     , USER_CONS_COLUMNS UCC
 WHERE UC.CONSTRAINT_NAME = UCC.CONSTRAINT_NAME
   AND UCC.CONSTRAINT_NAME = 'SYS_C007356';

 

 

-- 두 개의 컬럼을 묶어서 하나의 UNIQUE 제약 조건 설정
CREATE TABLE USER_UNIQUE3(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20) NOT NULL,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50),
    UNIQUE(USER_NO, USER_ID)
);
  
INSERT
  INTO USER_UNIQUE3
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user01'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);
  
INSERT
  INTO USER_UNIQUE3
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  2
, 'user01'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);  
  
INSERT
  INTO USER_UNIQUE3
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user02'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);  

INSERT
  INTO USER_UNIQUE3
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user01'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);

SELECT
       UCC.TABLE_NAME
     , UCC.COLUMN_NAME
     , UCC.CONSTRAINT_NAME
     , UC.CONSTRAINT_TYPE
  FROM USER_CONSTRAINTS UC
     , USER_CONS_COLUMNS UCC
 WHERE UC.CONSTRAINT_NAME = UCC.CONSTRAINT_NAME
   AND UCC.CONSTRAINT_NAME = 'SYS_C007359';

 

 

 

중복값이 있는 경우 UNIQUE 제약조건에 의해 행이 삽입되지 않는다.
INSERT
INTO USER_UNIQUE2
VALUES
(
1
, ’user01’
, ‘pass01’
, ‘홍길동’
, ‘남’
, ‘010-1234-5678’
, ‘hong123@greedy.com’
);

 

 

 

 

UNIQUE 제약조건은 NULL값 중복 저장이 가능하다.
  • 해결방법은 테이블 생성시 컬럼레벨에 NOT NULL을 함께 지정하면 된다.

 

 

 

 

두 개의 컬럼을 묶어서 하나의 UNIQUE 제약조건을 설정한다.
CREATE TABLE USER_UNIQUE3(
USER_NO NUMBER,
USER_ID VARCHAR2(20),
USER_PWD VARCHAR2(30) NOT NULL,
USER_NAME VARCHAR2(30),
GENDER VARCHAR2(10),
PHONE VARCHAR2(30),
EMAIL VARCHAR2(50),
UNIQUE (USER_NO, USER_ID)
);

 

 

 

 

 

 

 

 

 

 

 

 

 

PRIMARY KEY


  • 테이블에서 한 행의 정보를 구분하기 위한 고유 식별자(IDENTIFIER)역할을 한다.
  • NOT NULL의 의미와 UNIQUE의 의미를 둘 다 가지고 있으며, 한 테이블 당 한 개만
    설정할 수 있다.
  • 컬럼 레벨과 테이블 레벨에서 둘 다 지정 가능하다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

--PRIMARY KEY(기본키) 제약조건
-- : 테이블에서 한 행의 정보를 찾기위해 사용할 컬럼을 의미한다.
-- 테이블에 대한 식별자 역할을 한다.
-- NOT NULL + UNIQUE 제약조건 의미
-- 한 테이블당 한개만 설정할 수 있음
-- 컬럼 레벨, 테이블 레벨 둘 다에서 설정 가능함
-- 한 개 컬럼에 설정할 수도 있고, 여러개의 컬럼을 묶어서 설정할 수 있음 


CREATE TABLE USER_PRIMARYKEY(
    USER_NO NUMBER CONSTRAINT PK_USER_NO PRIMARY KEY,
    USER_ID VARCHAR2(20) UNIQUE,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50)
);

 

INSERT 
  INTO USER_PRIMARYKEY
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL
)
VALUES
(
  1, 'user01', 'pass01'
, '홍길동', '남', '010-1234-5678'
, 'hong123@greedy.com'
);

-- 1 행 이(가) 삽입되었습니다.




-- PK값은 UNIQUE 해야 함, 오류 발생

INSERT 
  INTO USER_PRIMARYKEY
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL
)
VALUES
(
  1, 'user02', 'pass03'
, '유관순', '여', '010-7777-5678'
, 'yoo123@greedy.com'
);

-- 오류 보고 ORA-00001: 무결성 제약 조건(C##EMPLOYEE.PK_USER_NO)에 위배됩니다



-- PK는 NULL값이 존재할 수 없음, 중복된 값을 가지는 것도 안됨
INSERT 
  INTO USER_PRIMARYKEY
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL
)
VALUES
(
  NULL, 'user02', 'pass03'
, '유관순', '여', '010-7777-5678'
, 'yoo123@greedy.com'
);


-- 오류 보고 -
-- ORA-01400: NULL을 ("C##EMPLOYEE"."USER_PRIMARYKEY"."USER_NO") 안에 삽입할 수 없습니다

 

-- 제약조건 관련 테이블에서 조회하기

SELECT
        UC.TABLE_NAME
      , UCC.COLUMN_NAME
      , UC.CONSTRAINT_NAME
      , UC.CONSTRAINT_TYPE
  FROM USER_CONSTRAINTS UC
  JOIN USER_CONS_COLUMNS UCC ON (UC.CONSTRAINT_NAME = UCC.CONSTRAINT_NAME)
WHERE UC.CONSTRAINT_NAME = 'PK_USER_NO';

 

 

 

-- 테이블 레벨에서 PK 설정 (복합키로 설정)
CREATE TABLE USER_PRIMARYKEY2(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20),
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50),
    CONSTRAINT PK_USER_NO2 PRIMARY KEY(USER_NO, USER_ID)
); 

-- Table USER_PRIMARYKEY2이(가) 생성되었습니다.


 
INSERT 
  INTO USER_PRIMARYKEY2
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL
)
VALUES
(
  1, 'user01', 'pass01'
, '홍길동', '남', '010-1234-5678'
, 'hong123@greedy.com'
); 

-- 1 행 이(가) 삽입되었습니다.



INSERT 
  INTO USER_PRIMARYKEY2
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL
)
VALUES
(
  1, 'user02', 'pass01'
, '홍길동', '남', '010-1234-5678'
, 'hong123@greedy.com'
); 

-- 1 행 이(가) 삽입되었습니다.



INSERT 
  INTO USER_PRIMARYKEY2
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL
)
VALUES
(
  2, 'user01', 'pass01'
, '홍길동', '남', '010-1234-5678'
, 'hong123@greedy.com'
);

INSERT 
  INTO USER_PRIMARYKEY2
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL
)
VALUES
(
  1, 'user01', 'pass01'
, '홍길동', '남', '010-1234-5678'
, 'hong123@greedy.com'
);

-- 오류 보고 -
-- ORA-00001: 무결성 제약 조건(C##EMPLOYEE.PK_USER_NO2)에 위배됩니다

 

 

 

 

 

 

 

 

 

FOREIGN KEY


  • 참조 무결성을 유지하기 위한 제약조건이다.
    - 즉 참조된 다른 테이블이 제공하는 값만 사용할 수 있도록 제한을 거는 것이다.
    - 참조되는 컬럼과 참조된 컬럼을 통해 테이블간의 관계가 형성된다.
  • 또한, 참조되는 값은 제공되는 값 이외에 NULL을 사용 가능하며, 참조할 테이블의 참조할 컬럼명을 생략할 경우에는 PRIMARY KEY로 설정된 컬럼이 자동으로 참조할 컬럼이 된다.

 

 

 

 

1) 테이블레벨
CREATE TABLE USER_FOREIGNKEY(
    USER_NO NUMBER PRIMARY KEY,
    USER_ID VARCHAR2(20) UNIQUE,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50),
    GRADE_CODE NUMBER,
    FOREIGN KEY(GRADE_CODE) REFERENCES USER_GRADE(GRADE_CODE)
    );

 

 

 

 

2) 컬럼레벨
CREATE TABLE USER_FOREIGNKEY(
    USER_NO NUMBER PRIMARY KEY,
    USER_ID VARCHAR2(20) UNIQUE,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50),
    GRADE_CODE NUMBER CONSTRAINT FK_USER_NO REFERENCESUSER_GRADE(GRADE_CODE)
 );

 

 

 

 

 

 INSERT
    INTO USER_FOREIGNKEY
    VALUES
    (
    5
    , 'user05'
    , 'pass05'
    , '윤봉길'
    , '남'
    , '010-6666-7771'
    , 'yoon123@greedy.com'
    , 50
 );

 

 

 

 

** FOREIGN KEY 제약조건으로 USER_GRADE TABLE의 GRADE_CODE 컬럼을 참조한다.
** USER_GRADE 테이블을 USER_FOREIGNKEY 테이블에서 참조하는 관계이기 때문에 USER_GRADE 테이블의 데이터 삭제 시 참조무결성에 위배되기 때문에 삭제가 불가능하다.

 

 

-- FOREIGN KEY (외부키 / 외래키) 제약 조건
-- 참조(REFERENCE) 된 다른 테이블에서 제공하는 값만 사용할 수 있음
-- 참조 무결성을 위배하지 않게 하기 위해서 사용
-- FOREIGN KEY 제약 조건에 의해서 테이블 간의 관계가 형성 됨
-- 제공되는 값 외에는 NULL을 사용할 수 없음


-- 컬럼 레벨일 경우
-- 컬럼명 자료형(크기) [CONSTRAINT 이름] REFERENCES 참조할테이블명 [(참조할컬럼)] [삭제룰]

-- 테이블 레벨일 경우
-- [CONSTRAINT 이름] FOREIGN KEY (적용할 컬럼명) REFERENCES 참조할테이블명 [(참조할컬럼)] [삭제룰]

-- 참조할 컬럼을 생략시 프라이머리 키를 참조한다. 


-- 참조할 테이블의 참조할 컬럼명이 생략 되면
-- PRIMARY KEY 로 설정 된 컬럼이 자동 참조할 컬럼이 됨
-- 참조될 수 있는 컬럼은 PRIMARY KEY 컬럼과,
-- UNIQUE 로 지정된 컬럼만 외래키로 참조할 수 있음

 

 

CREATE TABLE USER_GRADE(
    GRADE_CODE NUMBER PRIMARY KEY,
    GRADE_NAME VARCHAR2(30) NOT NULL
);


INSERT
    INTO USER_GRADE
(
GRADE_CODE
, GRADE_NAME
)
VALUES
(
10
, '일반회원'
);

INSERT
    INTO USER_GRADE
(
GRADE_CODE
, GRADE_NAME
)
VALUES
(
20
, '우수회원'
);

INSERT
    INTO USER_GRADE
(
GRADE_CODE
, GRADE_NAME
)
VALUES
(
30
, '특별회원'
);


SELECT
        UG.*
  FROM USER_GRADE UG; 
  
  
-- 테이블이 생성되었음을 확인할 수 있다.

 

-- 위 테이블을 대상으로 참조할 테이블을 만들어 보자

CREATE TABLE USER_FOREIGNKEY(
    USER_NO NUMBER PRIMARY KEY,
    USER_ID VARCHAR2(20) UNIQUE,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50),
    GRADE_CODE NUMBER,
    CONSTRAINT FK_GRADE_CODE FOREIGN KEY(GRADE_CODE) REFERENCES USER_GRADE(GRADE_CODE)
    );

-- Table USER_FOREIGNKEY이(가) 생성되었습니다.


-- 데이터1
INSERT
    INTO USER_FOREIGNKEY
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  1, 'user01', 'pass01'
, '홍길동', '남', '010-1234-5678'
, 'hong123@greedy.com', 10
);


-- 데이터2

INSERT
    INTO USER_FOREIGNKEY
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  2, 'user02', 'pass02'
, '유관순', '여', '010-7777-5678'
, 'yoo123@greedy.com', 10
);


-- 데이터 3
INSERT
    INTO USER_FOREIGNKEY
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  3, 'user03', 'pass03'
, '이순신', '남', '010-9999-5678'
, 'lee123@greedy.com', 30
);

-- 데이터4

INSERT
    INTO USER_FOREIGNKEY
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  4, 'user04', 'pass04'
, '신사임당', '여', '010-8888-5678'
, 'shin123@greedy.com', NULL
);


-- 데이터5

INSERT
    INTO USER_FOREIGNKEY
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  5, 'user05', 'pass05'
, '윤봉길', '남', '010-1111-5678'
, 'yoon123@greedy.com', 50
);

-- 오류 보고 -
-- ORA-02291: 무결성 제약조건(C##EMPLOYEE.FK_GRADE_CODE)이 위배되었습니다- 부모 키가 없습니다
-- -- 10~30은 참조하는 값이라 문제가 없으나 50이라는 값은 참조하는 값이 아니기 때문에 오류가 발생했다.

 

-- USER_FOREIGNKEY  테이블에서
-- 회원 아이디, 이름, 성별, 연락처, 회원 등급명 조회

SELECT
        UF.USER_ID
      , UF.USER_NAME
      , UF. GENDER
      , UF.PHONE
      , UG. GRADE_NAME
      FROM USER_FOREIGNKEY UF
      LEFT JOIN USER_GRADE UG ON (UF.GRADE_CODE = UG.GRADE_CODE);

 

 

 

 

 

 

 

 

삭제 옵션


  • 부모 테이블의 데이터 삭제 시 자식 테이블의 데이터를 어떠한 방식으로 처리할 지에 대한 내용을 제약조건
    설정 시 옵션으로 지정할 수 있다.
  • 기본 삭제 옵션은 ON DELETE RESTRICTED로 지정되어 있다.

 

 

 

부모 테이블의 데이터 삭제 시 참조하고 있는 테이블의 컬럼 값이 NULL로 변경된다.
CREATE TABLE USER_FOREIGNKEY(
USER_NO NUMBER PRIMARY KEY,
USER_ID VARCHAR2(20) UNIQUE,
USER_PWD VARCHAR2(30) NOT NULL,
USER_NAME VARCHAR2(30),
GENDER VARCHAR2(10),
PHONE VARCHAR2(30),
EMAIL VARCHAR2(50),
GRADE_CODE NUMBER REFERENCES USER_GRADE(GRADE_CODE) ON DELETE SET NULL
);



DELETE
	FROM USER_GRADE
	WHERE GRADE_CODE = 10;

 

 

 

 

 

 

부모 테이블의 데이터 삭제 시 참조하고 있는 테이블의 컬럼 값이 존재하던 행 전체를 삭제한다.
CREATE TABLE USER_FOREIGNKEY(
USER_NO NUMBER PRIMARY KEY,
USER_ID VARCHAR2(20) UNIQUE,
USER_PWD VARCHAR2(30) NOT NULL,
USER_NAME VARCHAR2(30),
GENDER VARCHAR2(10),
PHONE VARCHAR2(30),
EMAIL VARCHAR2(50),
GRADE_CODE NUMBER REFERENCES USER_GRADE(GRADE_CODE) ON DELETE CASCADE
);




DELETE
	FROM USER_GRADE
	WHERE GRADE_CODE = 10;

 

 

 

-- 만약 참조하고있는 테이블의 값이 삭제되는 일이 발생하면 어떻게 될까?


-- 삭제옵션
-- : 부모 테이블의 데이터 삭제 시 자식 테이블의 데이터를
-- 어떤 식으로 처리할 것인지에 대한 내용을 설정할 수 있다. 
-- 특정행을 선택하기 위해서 WHERE 사용
DELETE
  FROM USER_GRADE
 WHERE GRADE_CODE = 10;
-- ON DELETE RESTRICT 로 기본 지정되어있음
 
-- 삭제 룰이 별도로 지정되어있지않다면 삭제할 수 없다. 
-- 오류 보고 -
-- ORA-02292: 무결성 제약조건(C##EMPLOYEE.FK_GRADE_CODE)이 위배되었습니다- 자식 레코드가 발견되었습니다
-- 제공하는 컬럼의 값은 삭제하지 못한다. 


COMMIT;
-- 커밋 완료. 이 지점을 기억하기 위함이다. 


DELETE
  FROM USER_GRADE
 WHERE GRADE_CODE = 20;
 
-- 출력 : 1 행 이(가) 삭제되었습니다.

SELECT 
        UG.*
  FROM USER_GRADE UG;

-- 20은 아무것도 참조하고 있지않으므로 자식레코드가 발견되지않는다. 곧 삭제 가능하다. 


ROLLBACK; 
-- COMMIT한 순간으로 돌아간다. 즉, 20이 삭제되기 이전으로 돌아간다.

롤백 이후 재 실행시 나타나는 결과

 

 

 

-- 두번째 삭제옵션 확인 

-- ON DELETE SET NULL : 부모키를 삭제 시 자식키를 NULL로 변경하는 옵션

CREATE TABLE USER_GRADE2(
    GRADE_CODE NUMBER PRIMARY KEY,
    GRADE_NAME VARCHAR2(30) NOT NULL
);


INSERT
    INTO USER_GRADE2
(
GRADE_CODE
, GRADE_NAME
)
VALUES
(
10
, '일반회원'
);

INSERT
    INTO USER_GRADE2
(
GRADE_CODE
, GRADE_NAME
)
VALUES
(
20
, '우수회원'
);

INSERT
    INTO USER_GRADE2
(
GRADE_CODE
, GRADE_NAME
)
VALUES
(
30
, '특별회원'
);


SELECT
        UG.*
  FROM USER_GRADE UG; 
  
  
-- 테이블이 생성되었음을 확인할 수 있다.

 


-- 위 테이블을 대상으로 참조할 테이블을 만들어 보자
-- 삭제룰을 넣어보자.

CREATE TABLE USER_FOREIGNKEY2(
    USER_NO NUMBER PRIMARY KEY,
    USER_ID VARCHAR2(20) UNIQUE,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50),
    GRADE_CODE NUMBER,
    CONSTRAINT FK_GRADE_CODE2 FOREIGN KEY(GRADE_CODE) REFERENCES USER_GRADE2(GRADE_CODE) ON DELETE SET NULL
    );

-- Table USER_FOREIGNKEY2이(가) 생성되었습니다.




-- 데이터1
INSERT
    INTO USER_FOREIGNKEY2
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  1, 'user01', 'pass01'
, '홍길동', '남', '010-1234-5678'
, 'hong123@greedy.com', 10
);


-- 데이터2

INSERT
    INTO USER_FOREIGNKEY2
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  2, 'user02', 'pass02'
, '유관순', '여', '010-7777-5678'
, 'yoo123@greedy.com', 10
);


-- 데이터 3
INSERT
    INTO USER_FOREIGNKEY2
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  3, 'user03', 'pass03'
, '이순신', '남', '010-9999-5678'
, 'lee123@greedy.com', 30
);

-- 데이터4

INSERT
    INTO USER_FOREIGNKEY2
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  4, 'user04', 'pass04'
, '신사임당', '여', '010-8888-5678'
, 'shin123@greedy.com', NULL
);


-- 데이터5

INSERT
    INTO USER_FOREIGNKEY2
    (
      USER_NO, USER_ID, USER_PWD
    , USER_NAME, GENDER, PHONE
    , EMAIL, GRADE_CODE
    )
VALUES
(
  5, 'user05', 'pass05'
, '윤봉길', '남', '010-1111-5678'
, 'yoon123@greedy.com', 50
);

-- 위의 4개는 잘 삽입되었으나 5번 데이터의 50은 오류 보고 발생 -
-- ORA-02291: 무결성 제약조건(C##EMPLOYEE.FK_GRADE_CODE)이 위배되었습니다- 부모 키가 없습니다



COMMIT;
-- 커밋 완료.

DELETE
  FROM USER_GRADE2
 WHERE GRADE_CODE = 10;
-- 1 행 이(가) 삭제되었습니다.


SELECT
        UG.*
FROM USER_GRADE2 UG;

SELECT
        UF.*
FROM USER_FOREIGNKEY2 UF;


-- 30을 제외하고 모두 NULL로 변환된 모습이다.

 

 

 

 

-- 세번째 삭제옵션

-- ON DELETE CASCADE : 부모 키 삭제 시 자식 키를 가진 행도 함께 삭제
CREATE TABLE USER_GRADE3(
    GRADE_CODE NUMBER PRIMARY KEY,
    GRADE_NAME VARCHAR2(30) NOT NULL
);

INSERT
  INTO USER_GRADE3
(
  GRADE_CODE
, GRADE_NAME
)
VALUES
(
  10
, '일반회원'
);

INSERT
  INTO USER_GRADE3
(
  GRADE_CODE
, GRADE_NAME
)
VALUES
(
  20
, '우수회원'
);

INSERT
  INTO USER_GRADE3
(
  GRADE_CODE
, GRADE_NAME
)
VALUES
(
  30
, '특별회원'
);

SELECT
       UG.*
  FROM USER_GRADE3 UG;
  
CREATE TABLE USER_FOREIGNKEY3(
    USER_NO NUMBER PRIMARY KEY,
    USER_ID VARCHAR2(20) UNIQUE,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50),
    GRADE_CODE NUMBER,
    CONSTRAINT FK_GRADE_CODE3 FOREIGN KEY(GRADE_CODE) REFERENCES USER_GRADE3 (GRADE_CODE) ON DELETE CASCADE
);

INSERT
  INTO USER_FOREIGNKEY3
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL, GRADE_CODE
)
VALUES
(
  1, 'user01', 'pass01'
, '홍길동', '남', '010-1234-5678'
, 'hong123@greedy.com', 10
);

INSERT
  INTO USER_FOREIGNKEY3
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL, GRADE_CODE
)
VALUES
(
  2, 'user02', 'pass02'
, '유관순', '여', '010-7777-5678'
, 'yoo123@greedy.com', 10
);

INSERT
  INTO USER_FOREIGNKEY3
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL, GRADE_CODE
)
VALUES
(
  3, 'user03', 'pass03'
, '이순신', '남', '010-9999-5678'
, 'lee123@greedy.com', 30
);

INSERT
  INTO USER_FOREIGNKEY3
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL, GRADE_CODE
)
VALUES
(
  4, 'user04', 'pass04'
, '신사임당', '여', '010-8888-5678'
, 'shin123@greedy.com', NULL
);

INSERT
  INTO USER_FOREIGNKEY3
(
  USER_NO, USER_ID, USER_PWD
, USER_NAME, GENDER, PHONE
, EMAIL, GRADE_CODE
)
VALUES
(
  5, 'user05', 'pass05'
, '윤봉길', '남', '010-1111-5678'
, 'yoon123@greedy.com', 50
);


-- 오류 보고 -
-- ORA-02291: 무결성 제약조건(C##EMPLOYEE.FK_GRADE_CODE3)이 위배되었습니다- 부모 키가 없습니다

 

DELETE
  FROM USER_GRADE3
 WHERE GRADE_CODE = 10;
 
SELECT
       UG.*
  FROM USER_GRADE3 UG;
  
SELECT
       UF.*
  FROM USER_FOREIGNKEY3 UF;

 

 

 

 

 

 

 

CHECK


  • 해당 컬럼에 입력되거나 수정되는 값을 체크하여, 설정된 값 이외의 값이면 에러를 발생시킨다.
  • 비교연산자를 이용하여 조건을 설정하며, 비교값은 리터럴만 사용 가능하고 변하는 값이나 함수
    사용이 불가능하다.

 

CREATE TABLE USER_CHECK(
	USER_NO NUMBER PRIMARY KEY,
	USER_ID VARCHAR2(20) UNIQUE,
	USER_PWD VARCHAR2(30) NOT NULL,
	USER_NAME VARCHAR2(30),
	GENDER VARCHAR2(10) CHECK(GENDER IN (‘남’,’여‘)),
	PHONE VARCHAR2(30),
	EMAIL VARCHAR2(50),
	GRADE_CODE NUMBER
);

 

 INSERT
    INTO USER_CHECK
    VALUES
    (
    1
    , 'user01'
    , 'pass01'
    , '홍길동'
    , '남자'
    , '010-1234-5678'
    , 'hong123@greedy.com'
 );

 

 

 

-- 제약 조건에 이름 설정
CREATE TABLE CONS_NAME(
    TEST_DATA1 VARCHAR2(20) CONSTRAINT NN_TEST_DATA1 NOT NULL,
    TEST_DATA2 VARCHAR2(20) CONSTRAINT UN_TEST_DATA2 UNIQUE,
    TEST_DATA3 VARCHAR2(30),
    CONSTRAINT UN_TEST_DATA3 UNIQUE(TEST_DATA3)
);
   
SELECT
       UC.*
  FROM USER_CONSTRAINTS UC
 WHERE TABLE_NAME = 'CONS_NAME';

 

 

-- CHECK 제약조건 : 컬럼에 기록되는 값에 조건 설정을 할 수 있음
-- CHECK(컬럼명 비교연산자 비교값)
-- 주의 : 비교값은 리터럴만 사용할 수 있음, 변하는 값이나 함수 사용 못함
CREATE TABLE USER_CHECK(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20) UNIQUE,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(30),
    GENDER VARCHAR2(10) CHECK(GENDER IN ('남', '여')),
    PHONE VARCHAR2(30),
    EMAIL VARCHAR2(50)
);

-- Table USER_CHECK이(가) 생성되었습니다.


INSERT
  INTO USER_CHECK
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  1
, 'user01'
, 'pass01'
, '홍길동'
, '남'
, '010-1234-5678'
, 'hong123@greedy.com'
);

-- 1 행 이(가) 삽입되었습니다.


INSERT
  INTO USER_CHECK
(
  USER_NO
, USER_ID
, USER_PWD
, USER_NAME
, GENDER
, PHONE
, EMAIL
)
VALUES
(
  2
, 'user02'
, 'pass02'
, '홍길동'
, '남자'
, '010-1234-5678'
, 'hong123@greedy.com'
);


-- 오류 보고 -
-- ORA-02290: 체크 제약조건(C##EMPLOYEE.SYS_C007572)이 위배되었습니다

 

-- 테이블 레벨에서 CHECK 제약조건 설정
CREATE TABLE TEST_CHECK(
    TEST_NUMBER NUMBER,
    CONSTRAINT CK_TEST_NUMBER CHECK(TEST_NUMBER > 0)
);

INSERT
  INTO TEST_CHECK
(
  TEST_NUMBER
)
VALUES
(
  10
);

INSERT
  INTO TEST_CHECK
(
  TEST_NUMBER
)
VALUES
(
  -10
);

 

 

 

 

[문제]

-- 회원가입용 테이블 생성 (USER_TEST)
-- 컬럼명 : USER_NO(회원번호)
--         USER_ID(회원 아이디) -- 중복금지, NULL값 허용안함
--         USER_PWD(회원 비밀번호) --NULL값 허용안함
--         PNO (주민등록번호) -- 중복급지, NULL값 허용안함
--         GENDER(성별) -- 남 또는 여로 입력
--         PHONE(연락처)
--         ADDRESS(주소)
--         STATUS (탈퇴여부)  -- NOT NULL, 'Y' 혹은 'N' 입력
-- 각 제약조건 이름부여
-- 5명 이상 회원 정보 INSERT
-- 각 컬럼별로 코멘트 생성



CREATE TABLE USER_TEST(
  USER_NO NUMBER,
  USER_ID VARCHAR2(20) CONSTRAINT NN_USER_ID NOT NULL,
  USER_PWD VARCHAR2(20) CONSTRAINT NN_USER_PWD NOT NULL,
  PNO VARCHAR2(20) CONSTRAINT NN_PNO NOT NULL,
  GENDER VARCHAR2(3),
  PHONE VARCHAR2(20),
  ADDRESS VARCHAR2(100),
  STATUS VARCHAR2(3) CONSTRAINT NN_STATUS NOT NULL,
  CONSTRAINT UK_USER_ID UNIQUE(USER_ID),
  CONSTRAINT UK_PNO UNIQUE (PNO),
  CONSTRAINT CK_GENDER CHECK(GENDER IN ('남', '여')),
  CONSTRAINT CK_STATUS CHECK(STATUS IN ('Y', 'N'))
);

COMMENT ON COLUMN USER_TEST.USER_NO IS '회원번호';
COMMENT ON COLUMN USER_TEST.USER_ID IS '회원아이디';
COMMENT ON COLUMN USER_TEST.USER_PWD IS '비밀번호';
COMMENT ON COLUMN USER_TEST.PNO IS '주민등록번호';
COMMENT ON COLUMN USER_TEST.GENDER IS '성별';
COMMENT ON COLUMN USER_TEST.PHONE IS '연락처';
COMMENT ON COLUMN USER_TEST.ADDRESS IS '주소';
COMMENT ON COLUMN USER_TEST.STATUS IS '탈퇴여부';

INSERT 
  INTO USER_TEST
(
  USER_NO, USER_ID, USER_PWD
, PNO, GENDER, PHONE
, ADDRESS, STATUS
)
VALUES
(
  1, 'user01', 'pass01'
, '881122-1234567', '여', '010-1234-5678'
, '서울시 강남구 역삼동', 'N'
);

INSERT 
  INTO USER_TEST
(
  USER_NO, USER_ID, USER_PWD
, PNO, GENDER, PHONE
, ADDRESS, STATUS
)
VALUES
(
  2, 'user02', 'pass02'
, '891122-1234567', '남', '010-1234-5679'
, '서울시 강남구 역삼동', 'N'
);

INSERT 
  INTO USER_TEST
(
  USER_NO, USER_ID, USER_PWD
, PNO, GENDER, PHONE
, ADDRESS, STATUS
)
VALUES
(
  3, 'user03', 'pass03'
, '901122-1234567', '여', '010-1234-5670'
, '서울시 강남구 역삼동', 'Y'
);
INSERT 
  INTO USER_TEST
(
  USER_NO, USER_ID, USER_PWD
, PNO, GENDER, PHONE
, ADDRESS, STATUS
)
VALUES
(
  4, 'user04', 'pass04'
, '911122-1234567', '남', '010-1234-5671'
, '서울시 강남구 역삼동', 'N'
);

INSERT 
  INTO USER_TEST
(
  USER_NO, USER_ID, USER_PWD
, PNO, GENDER, PHONE
, ADDRESS, STATUS
)
VALUES
(
  5, 'user05', 'pass05'
, '921122-1234567', '여', '010-1234-5672'
, '서울시 강남구 역삼동', 'N'
);

SELECT
       UT.*
  FROM USER_TEST UT;

 

 

 

 

 

 

 

 

 

 

 

++ 추가적으로 알고있으면 좋은 삭제


-- 서브쿼리를 이용한 테이블 생성 
-- 컬럼명, 데이터 타입, 값이 복사되고, 제약 조건은 NOT NULL만 복사됨

-- EMPLOYEE_COPY 복사본 
CREATE TABLE EMPLOYEE_COPY
AS
SELECT
    E.*
  FROM EMPLOYEE E;
  -- EMPLOYEE 테이블의 전체행을 조회
  
SELECT
        EC.*
  FROM EMPLOYEE_COPY EC;


-- 새로 생성
CREATE TABLE EMPLOYEE_COPY2
AS
SELECT
        E.EMP_ID
      , E.EMP_NAME
      , E.SALARY
      , D.DEPT_TITLE
      , J.JOB_NAME
  FROM EMPLOYEE E
  LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
  LEFT JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE);

-- Table EMPLOYEE_COPY2이(가) 생성되었습니다.


SELECT
        EC.*
  FROM EMPLOYEE_COPY2 EC;

 

 

 

-- 제약 조건 추가
-- ALTER TABLE 테이블명 ADD PRIMARY KEY (컬럼명);
-- ALTER TABLE 테이블명 ADD FOREIGN KEY (컬럼명) REFERENCES 테이블명 (컬럼명);
-- ALTER TABLE 테이블명 ADD UNIQUE (컬럼명);
-- ALTER TABLE 테이블명 ADD CHECK (컬럼명 비교연산자 비교값);
-- ALTER TABLE 테이블명 MODIFY 컬럼명 NOT NULL;

ALTER TABLE EMPLOYEE_COPY ADD PRIMARY KEY (EMP_ID);

 

-- 실습
-- EMPLOYEE 테이블의 DEPT_CODE에 외래키 제약조건 추가
-- 참조 테이블은 DEPARTMENT, 참조컬럼은 DEPARTMENT의 기본키
-- DEPARTMENT 테이블의 LOCATION_ID에 외래키 제약조건 추가
-- 참조 테이블은 LOCATION, 참조 컬럼은 LOCATION의 기본키
-- EMPLOYEE 테이블의 JOB_CODE에 외래키 제약조건 추가
-- 참조 테이블은 JOB 테이블, 참조 컬럼은 JOB테이블의 기본키
-- EMPLOYEE 테이블의 SAL_LEVEL에 외래키 제약조건 추가
-- 참조테이블은 SAL_GRADE테이블, 참조 컬럼은 SAL_GRADE테이블 기본키
-- EMPLOYEE테이블의 ENT_YN컬럼에 CHECK제약조건 추가('Y','N')
-- 단, 대 소문자를 구분하기 때문에 대문자로 설정
-- EMPLOYEE테이블의 SALARY 컬럼에 CHECK제약조건 추가(양수)
-- EMPLOYEE테이블의 EMP_NO컬럼에 UNIQUE 제약조건 추가



ALTER TABLE EMPLOYEE ADD FOREIGN KEY (DEPT_CODE) REFERENCES DEPARTMENT (DEPT_ID);
ALTER TABLE DEPARTMENT ADD FOREIGN KEY (LOCATION_ID) REFERENCES LOCATION (LOCAL_CODE);
ALTER TABLE EMPLOYEE ADD FOREIGN KEY (JOB_CODE) REFERENCES JOB (JOB_CODE);
ALTER TABLE EMPLOYEE ADD FOREIGN KEY (SAL_LEVEL) REFERENCES SAL_GRADE (SAL_LEVEL);
ALTER TABLE EMPLOYEE ADD CHECK (ENT_YN IN ('Y', 'N'));
ALTER TABLE EMPLOYEE ADD CHECK (SALARY > 0);
ALTER TABLE EMPLOYEE ADD UNIQUE (EMP_NO);

 

 

 

 

 

 

'Programming > ORACLE' 카테고리의 다른 글

VIEW & SEQUENCE & INDEX  (0) 2022.01.27
DML & TCL  (0) 2022.01.25
서브 쿼리 ( Subquery)  (0) 2022.01.21
JOIN  (0) 2022.01.19
GROUP BY & HAVING  (0) 2022.01.19

 

 

 

 

 

 

SUBQUERY란?


  • 하나의 SELECT 문장의 절 안에 포함된 또 하나의 SELECT 문장이다.
  • 서브쿼리는 메인쿼리가 실행되기 이전에 한번만 실행되며 
    비교연산자의 오른쪽에 기술해야 하고, 반드시 괄호로 묶어야 한다.
  • 또한 서브쿼리와 비교할 항목은 반드시 서브쿼리의 SELECT한 항목의 개수와
    자료형을 일치시켜야 한다.

 

 

 

SUBQUERY 예제
  • 전 직원의 평균 급여보다 많은 급여를 받고 있는 직원의 사번, 이름, 직급코드, 급여를 조회하세요.

 

 

-- subquery

-- 부서 코드가 노옹철 사원과 같은 소속의 직원 명단 조회

-- 사원명이 노옹철인 사람의 부서 조회 



SELECT
        DEPT_CODE
  FROM EMPLOYEE
 WHERE EMP_NAME = '노옹철';
  
-- 부서코드가 D9인 직원을 조회


SELECT
        EMP_NAME
  FROM EMPLOYEE
 WHERE DEPT_CODE = 'D9';
  
  
  
  
-- 위의 두 쿼리를 하나로 

SELECT
       EMP_NAME
  FROM EMPLOYEE
 WHERE DEPT_CODE = (SELECT
                            DEPT_CODE
                      FROM EMPLOYEE
                     WHERE EMP_NAME = '노옹철');

 

-- 전 직원의 평균급여보다 많은 급여를 받고있는 직원의 
-- 사번, 이름, 직급코드, 급여를 조회하세요.
  
SELECT
        AVG(SALARY)
  FROM EMPLOYEE;
    

-- 서브쿼리 작성
  
SELECT
        EMP_ID
      , EMP_NAME
      , JOB_CODE
      , SALARY
  FROM EMPLOYEE
  WHERE SALARY >= (SELECT
                          AVG(SALARY)
                     FROM EMPLOYEE);

 

 

 

 

 

 

 

SUBQUERY의 유형


  • 1. 단일행 서브쿼리
    - 서브쿼리의 조회 결과 값의 개수가 1개 일 때
  • 2. 다중행 서브쿼리
    - 서브쿼리의 조회 결과 값의 행이 여러 개 일 때
  • 3. 다중열 서브쿼리
    - 서브쿼리의 조회 결과 컬럼의 개수가 여러 개 일 때
  • 4. 다중행 다중열 서브쿼리
    - 서브쿼리의 조회 결과 컬럼의 개수와 행의 개수가 여러 개 일 때
  • 5. 상(호연)관 서브쿼리
    - 서브쿼리가 만든 결과값을 메인 쿼리가 비교 연산할 때, 메인 쿼리 테이블의 값이 변경되면 서브쿼리의 결과값도 바뀐다.
  • 6. 스칼라 서브쿼리
    - 상관쿼리 이면서 결과값이 한 개인 서브쿼리

 

 

-- 서브쿼리의 유형
-- 단일행 서브쿼리 : 서브쿼리의 조회 결과 값이 1개 행일때
-- 다중행 서브쿼리 : 서브쿼리의 조회 결과 값의 행이 여러개일때
-- 다중열 서브쿼리 : 서브쿼리의 조회 결과 값의 컬럼이 여러개일때
-- 다중행 다중열 서브쿼리 : 조회 결과 행 수 와 열 수가 여러개일때
  
-- 서브쿼리의 유형에 따라 서브쿼리 앞에 붙는 연산자가 다름
-- 단일행 서브쿼리 앞에는 일반 비교 연산자 사용
-- >, <, >=, <=, =, != / ^= / <> (셋다 같지않다의 뜻)

 

-- (서브쿼리)
-- 노옹철 사원의 급여보다 많이 받는 직원의 
-- 사번, 이름, 부서, 직급, 급여를 조회하세요





-- (메인쿼리)
-- 가장 적은 급여를 받는 직원의
-- 사번, 이름, 부서, 직급, 급여를 조회하세요

 

 

 

-- 서브쿼리는 SELECT, FROM, WHERE, HAVING, ORDER BY 절에서 
-- 사용할 수 있다.
-- 부서별 급여의 합계 중 가장 큰 부서의 부서명, 급여 합계를 구하세요 (HAVING 사용)

SELECT 
        D.DEPT_TITLE
      , SUM(E.SALARY)
    FROM EMPLOYEE E
    LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
    GROUP BY D.DEPT_TITLE
  HAVING SUM (E.SALARY) = (SELECT MAX (SUM(E2.SALARY))
                                FROM EMPLOYEE E2
                                GROUP BY DEPT_CODE
                                );

 

 

 

 

 

 

 

 전 직원의 급여 평균보다 급여를 많이 받는 직원의 이름, 직급, 부서, 급여 조회

 

 

 

 

 

 

 

다중행(MULTIPLE ROW) 서브쿼리


  • 부서별 최고 급여를 받는 직원의 이름, 직급 , 부서, 급여 조회

 

 

 

-- 다중행 서브쿼리
-- 다중행 서브쿼리 앞에서는 일반 비교 연산자를 사용할 수 없다. 
-- 왜냐면 비교대상은 하나이기 때문에 1:1비교해야하기 때문
-- IN / NOT IN : 여러개의 결과 값 중에서 한 개라도 일치하는 값이 있다면
--                OR 없다면의 의미
-- > ANY / < ANY : 여러개의 결과 값 중에서 한개라도 큰 / 작은 경우
--                 가장 작은 값보다 크냐? / 가장 큰 값보다 작냐?
-- > ALL < ALL : 모든 값보다 큰 / 작은 경우
--                가장 큰 값보다 크냐? / 가장 작은 값보다 작냐?
-- EXIST / NOT EXIST : 서브 쿼리에만 사용하는 연산자로 
--                      값이 존재하냐? / 존재하지 않냐? 
  
  
  
-- 부서'별' 최고급여를 받는 직원의 이름, 직급, 부서, 급여를 조회

SELECT 
        E.DEPT_CODE
      , MAX(E.SALARY)
  FROM EMPLOYEE E
  GROUP BY E.DEPT_CODE;
  
   
SELECT
        E.EMP_NAME
      , E.JOB_CODE
      , E.DEPT_CODE
      , E.SALARY
  FROM  EMPLOYEE E
 WHERE  E.SALARY IN (SELECT MAX(E2.SALARY)
                        FROM EMPLOYEE E2
                        GROUP BY E2.DEPT_CODE
                         );

 

             
                                
-- 관리자에 해당하는 직원에 대한 정보와 관리자가 아닌 직원의
-- 정보를 추출하여 조회
-- 사번, 이름, 부서명, 직급, '관리자' AS 구분 / '직원' AS 구분

SELECT
        DISTINCT E.MANAGER_ID -- ( DISTINCT :  중복 제거 )
  FROM EMPLOYEE E
 WHERE E.MANAGER_ID IS NOT NULL; -- NULL 제거 
                                
  
SELECT 
        E.EMP_ID 사번
      , E.EMP_NAME 이름
      , D.DEPT_TITLE  부서명
      , J.JOB_NAME 직급
      , '관리자' AS 구분
  FROM EMPLOYEE E
  LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
  LEFT JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
WHERE E.EMP_ID IN (SELECT
                            DISTINCT E2.MANAGER_ID
                      FROM EMPLOYEE E2
                     WHERE E2.MANAGER_ID IS NOT NULL
                    )
  
  
  UNION -- 위와 아래 결과를 합쳐준다. 
  
  SELECT 
        E.EMP_ID 사번
      , E.EMP_NAME 이름
      , D.DEPT_TITLE  부서명
      , J.JOB_NAME 직급
      , '직원' AS 구분
  FROM EMPLOYEE E
  LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
  LEFT JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
WHERE E.EMP_ID NOT IN (SELECT
                            DISTINCT E2.MANAGER_ID
                            FROM EMPLOYEE E2
                            WHERE E2.MANAGER_ID IS NOT NULL
                       );

 

-- 이와 같은 결과를 SELECT 절의 SUBQUERY를 이용해서도 만들 수 있다. 
-- SELECT 절에서도 서브쿼리를 사용할 수 있다. 

SELECT 
        E.EMP_ID
      , E.EMP_NAME
      , D.DEPT_TITLE
      , CASE
           WHEN E.EMP_ID IN (SELECT
                                     DISTINCT E2.MANAGER_ID
                               FROM EMPLOYEE E2
                            WHERE E2.MANAGER_ID IS NOT NULL
                             )
                THEN '관리자'
                ELSE '직원'
                END AS 구분
        FROM EMPLOYEE E
        LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID);

 

-- 대리 직급의 직원들 중에서 과장 직급의 최소 급여보다 많이 받는
-- 직원의 사번, 이름, 직급명, 급여를 조회하세요.

 -- 서브이자 조건
SELECT
        E.EMP_ID
      , E.EMP_NAME
      , J.JOB_NAME
      , E.SALARY
  FROM EMPLOYEE E
  JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
  WHERE J.JOB_NAME = '과장';
  
  
  -- 메인
  SELECT
        E.EMP_ID
      , E.EMP_NAME
      , J.JOB_NAME
      , E.SALARY
  FROM EMPLOYEE E
  JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
  WHERE J.JOB_NAME = '대리';
  
  
  
  -- 합쳐보자 
  
SELECT
        E.EMP_ID
      , E.EMP_NAME
      , J.JOB_NAME
      , E.SALARY
FROM EMPLOYEE E
JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
WHERE J.JOB_NAME = '대리'
AND E.SALARY > ANY (SELECT        
                          E2.SALARY
                      FROM EMPLOYEE E2
                      JOIN JOB J2 ON (E2.JOB_CODE = J2.JOB_CODE)
                      WHERE J2.JOB_NAME = '과장'
                      );

 

-- 차장 직급의 급여의 가장 큰 값보다 많이 받는 과장 직급의
-- 사번, 이름, 직급, 급여를 조회하세요
-- 단, > ALL 혹은 < ALL 연산자를 사용 

  
  SELECT
        E.EMP_ID
      , E.EMP_NAME
      , J.JOB_NAME
      , E.SALARY
FROM EMPLOYEE E
JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
WHERE J.JOB_NAME = '과장'
AND E.SALARY > ALL (SELECT        
                          E2.SALARY
                      FROM EMPLOYEE E2
                      JOIN JOB J2 ON (E2.JOB_CODE = J2.JOB_CODE)
                      WHERE J2.JOB_NAME = '차장'
                      );

 

-- 자기 직급의 평균 급여를 받고있는 직원의
-- 사번, 이름, 직급코드, 급여를 조회하세요 
-- 단, 급여와 급여 평균은 만원 단위로 계산 하세요 TRUNC(컬럼명, -5)
  
SELECT
        E.JOB_CODE
      , TRUNC(AVG(E.SALARY), -5)
  FROM EMPLOYEE E
  GROUP BY E.JOB_CODE;
  
SELECT 
        E.EMP_ID
      , E.EMP_NAME
      , E.JOB_CODE
      , E.SALARY
      FROM EMPLOYEE E
      WHERE E.SALARY IN (SELECT TRUNC (AVG(E2.SALARY), -5)
                            FROM EMPLOYEE E2
                            GROUP BY E2.JOB_CODE
                           );
  
  
-- 이 상황에서 SALARY만 비교했기에 논리적으로 오류가 발생한다.

 

  
-- 다중 행 다중열 서브쿼리를 이용한 해결 
-- 비교하려는 행과 조회되는 행의 개수가 일치해야 한다. 
  
  SELECT 
        E.EMP_ID
      , E.EMP_NAME
      , E.JOB_CODE
      , E.SALARY
      FROM EMPLOYEE E
      WHERE (E.JOB_CODE, E.SALARY) IN (SELECT 
                                                E2.JOB_CODE
                                             , TRUNC (AVG(E2.SALARY), -5)
                                         FROM EMPLOYEE E2
                                     GROUP BY E2.JOB_CODE
                                       );

 

 

 

 

 

 

 

 

다중열 서브쿼리


  • 퇴사한 여직원과 같은 부서, 같은 직급에 해당하는 사원의 이름, 직급, 부서, 입사일을 조회하세요.

 

 

 

 

 

  
-- 다중열 서브쿼리
-- 퇴사한 여직원과 같은 부서, 같은 직급에 해당하는
-- 사원의 이름, 직급, 부서, 입사일을 조회
SELECT
       E.EMP_ID
     , E.EMP_NAME
     , E.DEPT_CODE
     , E.JOB_CODE
  FROM EMPLOYEE E
 WHERE SUBSTR(E.EMP_NO, 8, 1) = 2
   AND E.ENT_YN = 'Y';
   
SELECT
       E.EMP_ID
     , E.JOB_CODE
     , E.DEPT_CODE
     , E.HIRE_DATE
  FROM EMPLOYEE E
 WHERE E.DEPT_CODE = (SELECT E2.DEPT_CODE
                        FROM EMPLOYEE E2
                       WHERE SUBSTR(E2.EMP_NO, 8, 1) = 2
                         AND E2.ENT_YN = 'Y'
                     )
   AND E.JOB_CODE = (SELECT E3.JOB_CODE
                        FROM EMPLOYEE E3
                       WHERE SUBSTR(E3.EMP_NO, 8, 1) = 2
                         AND E3.ENT_YN = 'Y'
                     )
   AND E.EMP_ID != (SELECT E4.EMP_ID
                        FROM EMPLOYEE E4
                       WHERE SUBSTR(E4.EMP_NO, 8, 1) = 2
                         AND E4.ENT_YN = 'Y'
                     );
-- 다중열 서브쿼리로 변경
SELECT
       E.EMP_ID
     , E.JOB_CODE
     , E.DEPT_CODE
     , E.HIRE_DATE
  FROM EMPLOYEE E
 WHERE (E.DEPT_CODE, E.JOB_CODE) = (SELECT E2.DEPT_CODE
                                         , E2.JOB_CODE
                                      FROM EMPLOYEE E2
                                     WHERE SUBSTR(E2.EMP_NO, 8, 1) = 2
                                       AND E2.ENT_YN = 'Y'
                                    )
   AND E.EMP_ID != (SELECT E3.EMP_ID
                      FROM EMPLOYEE E3
                     WHERE SUBSTR(E3.EMP_NO, 8, 1) = 2
                       AND E3.ENT_YN = 'Y'
                   );

 

 

 

 

 

다중행 다중열 서브쿼리


  • 직급별 최소 급여를 받는 직원의 사번, 이름, 직급, 급여 조회

 

 

 

-- FROM 절에서 서브쿼리를 사용할 수 있다 => 테이블 대신 사용
-- 인라인 뷰(INLINE VIEW)라고도 함
-- : 서브쿼리가 만든 결과 집합(RESULT SET)에 대한 출력 화면

SELECT
       E.EMP_NAME
     , J.JOB_NAME
     , E.SALARY
  FROM (SELECT E2.JOB_CODE
             , TRUNC(AVG(E2.SALARY), -5) AS JOBAVG
          FROM EMPLOYEE E2
         GROUP BY E2.JOB_CODE
       ) V
   JOIN EMPLOYEE E ON (V.JOBAVG = E.SALARY AND E.JOB_CODE = V.JOB_CODE)    
   JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
  ORDER BY J.JOB_NAME;   
  
SELECT
       V.EMP_NAME
     , V.부서명
     , V.직급이름
  FROM (SELECT EMP_NAME
             , DEPT_TITLE AS 부서명
             , JOB_NAME AS 직급이름
          FROM EMPLOYEE E
          LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
          JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
        ) V
 WHERE V.부서명 = '인사관리부';

 

 

  SELECT
        V.EMP_NAME
        , V.부서명
        , V.직급이름   -- 별칭을 줬으면 별칭으로 검색해야함
  FROM (SELECT EMP_NAME
            , DEPT_TITLE AS 부서명
            , JOB_NAME AS 직급이름
          FROM EMPLOYEE E
          LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
          JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
          ) V
    WHERE V.부서명 = '인사관리부';

 

 

  -- 인라인뷰를 활용한 TOP-N 분석
  -- ORDER BY 한 결과에 ROWNUM을 붙임
  -- ROWNUM 은 행 번호를 의미함
  -- WHERE 절에서 붙여지므로 ORDER BY 한 다음에
  -- ROWNUM이 붙게 하려면 서브쿼리 (인라인뷰) 를 사용해야함
  
SELECT
         ROWNUM 
       , E.EMP_NAME
       , E.SALARY
  FROM   EMPLOYEE E
 ORDER BY E.SALARY DESC;
 
SELECT
        ROWNUM
      , V.EMP_NAME
      , V.SALARY
  FROM (SELECT E.*
          FROM EMPLOYEE E
        ORDER BY E.SALARY DESC
        ) V
  WHERE ROWNUM <= 5;

 

-- 6위부터 10위까지 조회 
-- 결과가 나오지 않음. 왜일까? 
-- ROWNUM은 WHERE절에 붙는다. 
-- WHERE ROWNUM BETWEEN 6 AND 10; 범위를 1 AND 10으로 바꿔주면 동작된다.
-- 오류가 난 행은 지속해서 1번으로 리셋이 되기 때문 

SELECT
         V2.RNUM
        ,V2.EMP_NAME
        ,V2.SALARY

        FROM(SELECT
                ROWNUM RNUM
              , V.EMP_NAME
              , V.SALARY
               FROM (SELECT E.*
                       FROM EMPLOYEE E
                      ORDER BY E.SALARY DESC
                    ) V
            ) V2
  WHERE RNUM BETWEEN 6 AND 10;

 

 

-- STOPKEY 활용
  
  SELECT
         V2.RNUM
        ,V2.EMP_NAME
        ,V2.SALARY

        FROM(SELECT
                ROWNUM RNUM
              , V.EMP_NAME
              , V.SALARY
               FROM (SELECT E.*
                       FROM EMPLOYEE E
                      ORDER BY E.SALARY DESC
                    ) V
                WHERE ROWNUM < 11
            ) V2
  WHERE RNUM BETWEEN 6 AND 10;
  
  
  
  
  -- 급여 평균 3위 안에 드는 부서의 
  -- 부서 코드와 부서명, 평균 급여를 조회하세요 
  
  SELECT
           V.DEPT_CODE
          , V.DEPT_TITLE
          , V.평균급여
  
 FROM (SELECT
            E.DEPT_CODE
          , D.DEPT_TITLE
          , AVG(E.SALARY) 평균급여
    FROM EMPLOYEE E
    JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
    GROUP BY E.DEPT_CODE, D.DEPT_TITLE
    ORDER BY AVG(E.SALARY) DESC
    ) V
 WHERE ROWNUM <= 3;

 

 

-- 직원 정보에서 급여를 가장 많이 받는 순으로 이름, 급여, 순위 조회
-- RANK() 함수는 동일한 순위 이후의 등수를 동일한 인원수 만큼 건너뛰고
-- 다음 순위를 계산하는 방식
-- DENSE_RANK() 함수는 중복되는 순위 이후의 등수를 이후 등수로 처리 

SELECT
          E.EMP_NAME
        , E.SALARY
        , RANK() OVER(ORDER BY E.SALARY DESC) 순위
  FROM EMPLOYEE E;

SELECT
          E.EMP_NAME
        , E.SALARY
        , DENSE_RANK() OVER(ORDER BY E.SALARY DESC) 순위
  FROM EMPLOYEE E;

 

 

--  TOP 5 조회

SELECT
        V.*
  FROM ( SELECT
          E.EMP_NAME
        , E.SALARY
        , RANK() OVER(ORDER BY E.SALARY DESC) 순위
  FROM EMPLOYEE E
  ) V
WHERE V.순위 <= 5;

 

  -- 직원 테이블에서 보너스를 포함한 연봉이 높은 5명의
  -- 사번, 이름, 부서명, 직급명, 입사일을 조회하세요.

SELECT
          V.EMP_ID
        , V.EMP_NAME
        , V.DEPT_TITLE
        , V.JOB_NAME
        , V.HIRE_DATE
  FROM ( SELECT
                E.EMP_ID
              , E.EMP_NAME
              , D.DEPT_TITLE
              , J.JOB_NAME
              , E.HIRE_DATE
              , E.SALARY
              , (E.SALARY + (E.SALARY * NVL(E.BONUS,0))) * 12
          , RANK() OVER(ORDER BY((E.SALARY * NVL(E.BONUS,0))) * 12 DESC) 순위
      FROM EMPLOYEE E
      JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
      JOIN JOB J ON (E.JOB_CODE = J.JOB_CODE)
      ) V
  WHERE V.순위 < 6;

 

 

 

 

 

 

 

  
  -- WITH 이름 AS (쿼리문)
  -- 서브쿼리에 이름을 붙여주고 사용시 이름을 사용하게 됨
  -- 인라인 뷰로 사용 될 서브쿼리에서 이용됨
  -- 같은 서브쿼리가 여러번 사용될 경우 중복 작성을 줄일 수 있으며
  -- 실행속도도 빨리진다는 장점이 있다. 
  
  WITH
        TOPN_SAL
    AS (SELECT E.EMP_ID
              , E.EMP_NAME
              , E.SALARY
            FROM EMPLOYEE E
            ORDER BY E.SALARY DESC    
    )
SELECT ROWNUM
    , T.EMP_NAME
    , T.SALARY
FROM TOPN_SAL T;

 

 

 

-- 부서별 급여 합계가 전체 급여의 총 합의 20%보다 많은 
-- 부서의 부서명과, 부서별 급여 합계 조회
  
SELECT
        D.DEPT_TITLE
      , SUM(E.SALARY)
  FROM EMPLOYEE E
  JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
  GROUP BY D.DEPT_TITLE
  HAVING SUM(E.SALARY) > (SELECT SUM(E2.SALARY) * 0.2
                            FROM EMPLOYEE E2
                          );

 

 

 --  WITH 사용
  WITH
        TOTAL_SAL
    AS (SELECT D.DEPT_TITLE
                , SUM(E.SALARY) SSAL
          FROM EMPLOYEE E
          LEFT JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
          GROUP BY D.DEPT_TITLE
          )
  SELECT DEPT_TITLE
       , SSAL
    FROM TOTAL_SAL
   WHERE SSAL > (SELECT SUM (E2.SALARY) * 0.2
                    FROM EMPLOYEE E2
                 );

 

 

  
  -- WITH로 서브쿼리 여러개 사용
  WITH 
        SUM_SAL
    AS (SELECT SUM(E.SALARY)
          FROM EMPLOYEE E
        )
    , AVG_SAL
    AS (SELECT AVG(E2.SALARY)
        FROM EMPLOYEE E2
    )
SELECT 
        S.*
    FROM SUM_SAL S
    UNION   
SELECT
        A.*
    FROM AVG_SAL A;

 

 

 

 

 

 

 

 

 

상(호연)관 서브쿼리


  • 관리자가 있는 사원들 중 관리자의 사번이 EMPLOYEE테이블에 존재하는 직원의 사번, 이름, 소속부서,
    관리자사번을 조회하세요.

 

 

  -- 상[호연]관 서브쿼리
  -- 일반적으로 서브쿼리가 만든 결과 값을 메인 쿼리가 비교연산
  -- 메인쿼리가 사용하는 테이블의 값을 서브쿼리가 이용해서 결과를 만듦
  -- 메인쿼리의 테이블 값이 변경되면, 서브쿼리의 결과값도 바뀌게 됨
  
  -- 관리자 사번이 EMPLOYEE 테이블에 존재하는 직원에 대한 조회
  
SELECT
         E.EMP_ID
       , E.EMP_NAME
       , E.DEPT_CODE
       , E.MANAGER_ID
  FROM EMPLOYEE E
  WHERE EXISTS (SELECT E2.EMP_ID
                  FROM EMPLOYEE E2
                WHERE E.MANAGER_ID = E2.EMP_ID
                );

 

 

 

 

 

스칼라 서브쿼리 - SELECT절


  • 모든 사원의 사번, 이름, 관리자사번, 관리자명을 조회하세요.

-- SELECT 절에서 스칼라 서브쿼리 이용
-- SELECT 절에서 서브쿼리는 무조건 결과값이 하나여야 한다.
-- 모든 사원의 사번, 이름, 관리자 사번, 관리자명을 조회하세요.

SELECT 
        E.EMP_ID
      , E.EMP_NAME
      , E.MANAGER_ID
      , NVL((SELECT E2.EMP_NAME
              FROM EMPLOYEE E2
              WHERE E.MANAGER_ID = E2.EMP_ID
              ), '없음')
    FROM EMPLOYEE E
    ORDER BY 1;

 

 

 

스칼라 서브쿼리 - WHERE절


  • 자신이 속한 직급의 평균 급여보다 많이 받는 직원의 이름, 직급, 급여를 조회하세요.

 

 

 

-- 스칼라 서브쿼리
-- 단일행 서브쿼리 + 상관 쿼리

-- 동일 직급의 급여 평균보다 급여를 많이 받고있는 직원의 
-- 사번, 직급 코드, 급여를 조회하세요. 

-- WHERE절에서 스칼라 서브쿼리 이용

SELECT 
         E.EMP_NAME
        , E.JOB_CODE
        , E.SALARY
    FROM EMPLOYEE E
    WHERE E.SALARY > (SELECT TRUNC (AVG(E2.SALARY), -5)
                      FROM EMPLOYEE E2
                      WHERE E.JOB_CODE = E2.JOB_CODE
                      );

 

 

 

 

 

 

스칼라 서브쿼리 – ORDER BY절


  • 모든 직원의 사번, 이름, 소속부서를 조회 후 부서명 내림차순으로 정렬하세요.

 

  -- ORDER BY 절에서 스칼라 서브쿼리 이용
  -- 모든 직원의 사번, 이름, 소속부서 조회
  -- 단, 부서명 내림차순 정렬
  SELECT
        E.EMP_ID
        , E.EMP_NAME
        , E.DEPT_CODE
    FROM EMPLOYEE E
    ORDER BY (SELECT D.DEPT_TITLE
                FROM DEPARTMENT D
                WHERE E.DEPT_CODE = D.DEPT_ID
                ) DESC NULLS LAST;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Programming > ORACLE' 카테고리의 다른 글

DML & TCL  (0) 2022.01.25
DDL(Data Definition Language)  (0) 2022.01.24
JOIN  (0) 2022.01.19
GROUP BY & HAVING  (0) 2022.01.19
함수(Function)  (0) 2022.01.18

 

 

 

 

 

JOIN이란?


  • 한 개 이상의 테이블에서 테이터를 조회하기 위해 사용
  • 수행 결과는 하나의 Result Set으로 나오게 된다.

 

 

 

 

1. 오라클 전용 구문


  • FROM절에 ','로 구분하여 합치게 될 TABLE명을 기술하고,
  • WHERE절에 합치기에 사용할 컬럼명을 명시한다.
  • 오라클에서만 사용가능하며 다른 DBMS에서는 통용되지 않는다.

 

 

 

-- JOIN
-- : 두 개 이상의 테이블을 하나로 합쳐서 결과를 조회한다.

-- 오라클 전용 구문
-- FROM 절에 ','로 구분하여 합치게 될 테이블 명을 기술하고
-- WHERE절엔 합치기에 사용할 컬럼명을 명시한다.




-- 1. 연결에 사용할 두 컬럼명이 다른 경우

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , DEPT_TITLE
  FROM  EMPLOYEE
      , DEPARTMENT          
 WHERE  DEPT_CODE = DEPT_ID;   -- 연결기준을 주어야한다.
 
 
 

-- 2. 연결에 사용할 두 컬럼명이 같은 경우
/* EMPLOYEE, JOB 에서 사용하는 컬럼명이 JOB_CODE로 같다.\
    해결방법 : 어떤 테이블의 코드인지 분명히 명시 해 주어야 한다. */
    
SELECT
        EMP_ID
      , EMP_NAME
      , EMPLOYEE.JOB_CODE
      , JOB.JOB_NAME
  FROM  EMPLOYEE
      , JOB         
 WHERE  EMPLOYEE.JOB_CODE = JOB.JOB_CODE;   -- 연결기준을 주어야한다.
 
 
 
 
 
 -- 테이블명에 별칭 사용
 
 SELECT
        E.EMP_ID
      , E.EMP_NAME
      , E.JOB_CODE
      , J.JOB_NAME
  FROM  EMPLOYEE E
      , JOB J     
 WHERE  E.JOB_CODE = J.JOB_CODE;

 

 

 

 

 

2. ANSI 표준 구문


  • 연결에 사용하려는 컬럼명이 같은 경우 USING()을 사용하고, 다른 경우 ON()을 사용한다.
  • 다른 DBMS에서도 통용된다.
  • 두 가지 버전을 다 활용 및 차이점을 숙지 할 줄 알아야 한다.

 

 

 

  • 연결에 사용하려는 컬럼명이 같은 경우 USING( )을 사용하고, 다른 경우 ON( )을 사용한다.

 

-- ANSI 표준 구문
-- 1. 연결에 사용할 컬럼명이 같은 경우 : USING(컬럼명)을 사용함

SELECT
        EMP_ID
      , EMP_NAME
      , JOB_CODE
      , JOB_NAME
  FROM  EMPLOYEE
  JOIN JOB USING(JOB_CODE); -- JOB코드를 이용해서 JOIN한다




-- 2. 연결에 사용할 컬럼명이 다른 경우 : ON()을 사용함

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , DEPT_TITLE
  FROM  EMPLOYEE
  JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID); -- ON( )코드를 이용해서 JOIN한다

 

-- 컬럼명이 같은 경우에도 ON()을 사용할 수 있다.

SELECT
        E.EMP_ID
      , E.EMP_NAME
      , E.JOB_CODE
      , J.JOB_NAME
  FROM  EMPLOYEE E
  JOIN JOB J ON(E.JOB_CODE = J.JOB_CODE);

 

-- 부서 테이블과 지역 테이블을 조인하여 테이블에 있는 모든 데이터를 조회하세요.

-- ANSI 표준

SELECT
        *
  FROM DEPARTMENT
  JOIN LOCATION ON (LOCATION_ID = LOCAL_CODE);



-- 오라클 전용

SELECT
        *
  FROM  DEPARTMENT D
      , LOCATION L         
 WHERE  D.LOCATION_ID = L.LOCAL_CODE;

 

 

 

 

 

 

 

 

INNER JOIN 과 OUTER JOIN


  • 두 개 이상의 테이블을 조인할 때, 일치하는 값이 없는 행은 조인에서 제외된다.
  • 이것을 INNER JOIN이라고 하며, 명시적으로 사용하지 않을 시에는 기본적으로 INNER JOIN 이다.
  • 하지만 일치하지 않은 값 또한 JOIN에 포함시킬 수도 있다.
  • 이것을 OUTER JOIN이라고 하며, 반드시 OUTER JOIN임을 명시해야 한다.

 

-- 조인은 기본이 EQUAL JOIN 이다. (EQU JOIN이라고도 함)
-- 연결되는 컬럼의 값이 일치하는 행들만 조인된다.
-- 일치하는 값이 없는 행은 조인에서 제외되는 것을 INNER JOIN이라고 한다.
-- JOIN의 기본은 INNER JOIN & EQU JOIN 이다.

 

 

 

 

 

 

INNER JOIN


-- [기본] : INNER JOIN 

SELECT
        EMP_NAME
      , DEPT_TITLE
  FROM EMPLOYEE
  JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);

 

 

 

 

 

OUTER JOIN의 종류


 

 

1. LEFT OUTER JOIN
  • 합치기에 사용한 두 테이블 중에 왼쪽 편에 기술된 테이블의 컬럼 수를 기준으로 JOIN을 할 때 사용한다.

 

2. RIGHT OUTER JOIN
  • 합치기에 사용한 두 테이블 중에 오른쪽 편에 기술된 테이블의 컬럼 수를 기준으로 JOIN을 할 때 사용한다.

 

3. FULL OUTER JOIN
  • 합치기에 사용한 두 테이블이 가진 모든 행을 결과에 포함 시킬 경우 사용한다.

 

-- OUTER JOIN : 두 테이블의 지정하는 컬럼 값이 일치하지 않는 행도
--              조인에 포함 시킨다. 세가지 종류가 있다.
--   기본은 INNER조인이기 때문에 반드시 OUTER JOIN은 따로 명시해야 한다.


-- 1. LEFT OUTER JOIN
-- : 합치기에 사용한 두 테이블 중 왼편에 기술 된 테이블의 행의 수를 기준으로 JOIN

-- 2. RIGHT OUTER JOIN
-- : 합치기에 사용한 두 테이블 중 오른편에 기술 된 테이블의 행의 수를 기준으로 JOIN

-- 3. FULL OUTER JOIN
-- : 합치기에 사용한 두 테이블이 가진 모든행을 결과에 포함하여 JOIN

 

 

 

 

 

 

OUTER JOIN


 

-- 1. LEFT OUTER JOIN
-- 1-1. ANSI 표준
/* EMPLOYEE 기준의 값을 얻을 수 있다. */

SELECT
        EMP_NAME
      , DEPT_TITLE
  FROM  EMPLOYEE
  /* OUTER을 생략한 상태로 동작해도 무방하다. */
--  LEFT JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);
  LEFT OUTER JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);
  
  
  
  
-- 2. RIGHT OUTER JOIN
-- 2-1. ANSI 표준
/* DEPARTMENT 기준의 값을 얻을 수 있다. */

SELECT
        EMP_NAME
      , DEPT_TITLE
  FROM  EMPLOYEE
  /* OUTER을 생략한 상태로 동작해도 무방하다. */
--  LEFT JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);
  RIGHT OUTER JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);
  
  
  
  
-- 3. FULL OUTER JOIN
-- 3-1. ANSI 표준
/* LEFT RIGHT 모든 행이 전부 포함된다. */

SELECT
        EMP_NAME
      , DEPT_TITLE
  FROM  EMPLOYEE
  /* OUTER을 생략한 상태로 동작해도 무방하다. */
--  FULL JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);
  FULL OUTER JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID);

 

 

 

 

 

 

오라클 전용 OUTER JOIN


 

-- 1. LEFT OUTER JOIN
-- 1-2. 오라클 전용 OUTER 구문
/* EMPLOYEE 기준의 값을 얻을 수 있다. */
SELECT
        EMP_NAME
      , DEPT_TITLE
  FROM  EMPLOYEE
      , DEPARTMENT
 WHERE  DEPT_CODE = DEPT_ID(+);
 
 
-- 2. RIGHT OUTER JOIN
-- 2-2. 오라클 전용 OUTER 구문
/* DEPARTMENT 기준의 값을 얻을 수 있다. */
/* EMPLOYEE 기준이었던 (+)을 반대쪽으로 옮겨준다. */

SELECT
        EMP_NAME
      , DEPT_TITLE
  FROM  EMPLOYEE
      , DEPARTMENT
 WHERE  DEPT_CODE(+) = DEPT_ID;
 
 
 
-- 3. FULL OUTER JOIN
-- 3-2. 오라클 전용 OUTER 구문
/* 오라클 전용 구문으로는 FULL OUTER JOIN을 하지 못한다. */
SELECT
        EMP_NAME
      , DEPT_TITLE
  FROM  EMPLOYEE
      , DEPARTMENT
      
/* 오라클 전용에서는 불가능하다. */    
-- WHERE  DEPT_CODE(+) = DEPT_ID(+);

 

 

 

 

 

 

CROSS JOIN


  • 카테이션곱(Cartensian projuct)라고도 한다.
  • 조인되는 테이블의 각 행들이 모두 매핑된 데이터가 검색되는 조인 방법
  • 검색되는 데이터 수는 행의 컬럼수 x 행의 컬럼수로 나오게 된다.

-- CROSS JOIN : 카네이션 곱이라고도 한다.
--              조인 되는 테이블의 각 행들이 모두 매핑된 
--              데이터가 검색되는방법이다.

SELECT
       EMP_NAME
     , DEPT_TITLE
  FROM EMPLOYEE
 CROSS JOIN DEPARTMENT;

 

 

 

 

 

 

NON_EQU JOIN


  • 지정한 컬럼 값이 일치하는 경우가 아닌, 값의 범위에 포함되는 행들을 연결하는 방식이다.

 

-- NON EQUAL JOIN(NON EQU JOIN)
-- : 지정한 컬럼의 값이 일치하는 경우가 아닌, 값의 범위에 포함되는 행등을
--   연결하는 방식
-- ANSI 표준



SELECT
        EMP_NAME
      , SALARY
      , E.SAL_LEVEL "EMPLOYEE의 SAL_LEVEL"
      , S.SAL_LEVEL "SAL_GRADE의 SAL_LEVEL"
  FROM EMPLOYEE E
  JOIN SAL_GRADE S ON(SALARY BETWEEN MIN_SAL AND MAX_SAL);



-- 오라클 전용

SELECT
        EMP_NAME
      , SALARY
      , E.SAL_LEVEL "EMPLOYEE의 SAL_LEVEL"
      , S.SAL_LEVEL "SAL_GRADE의 SAL_LEVEL"
  FROM  EMPLOYEE E
      , SAL_GRADE S
 WHERE  SALARY BETWEEN MIN_SAL AND MAX_SAL;

 

 

 

 

 

SELF JOIN


  • 조인은 두 개 이상의 서로 다른 테이블을 연결하기도 하지만, 같은 테이블을 조인하는 경우도 있다.
    이러한 경우 자기 자신과 조인을 맺는 것이라 하여 SELF JOIN이라 한다.

-- SELF JOIN : 같은 테이블을 조인하는 경우이다.
--             즉, 자기 자신과 조인을 맺는 것이다. 


-- 오라클 전용

SELECT
        E1.EMP_ID
      , E1.EMP_NAME 사원이름
      , E1.DEPT_CODE
      , E1.MANAGER_ID 관리자이름
      , E2.EMP_NAME
  FROM  EMPLOYEE E1
      , EMPLOYEE E2
 WHERE E1.MANAGER_ID = E2.EMP_ID;

 

-- ANSI 표준

SELECT
        E1.EMP_ID
      , E1.EMP_NAME 사원이름
      , E1.DEPT_CODE
      , E1.MANAGER_ID 관리자이름
      , E2.EMP_NAME
  FROM  EMPLOYEE E1
  JOIN EMPLOYEE E2 ON(E1.MANAGER_ID = E2.EMP_ID);

 

 

 

 

 

다중 JOIN


 

 

 

 

-- 다중 JOIN : N개의 테이블을 조회할 때 사용
-- ANSI 표준 
/* 조인 순서가 중요하다. 다중조인, 안시표준일때는 순서를 고려해야한다. */

SELECT 
        EMP_ID      --EMPLOYEE
      , EMP_NAME    --EMPLOYEE
      , DEPT_CODE   --EMPLOYEE
      , DEPT_TITLE   -- DEPARTMENT
      , LOCAL_NAME  --LOCATION
  FROM EMPLOYEE
  JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
  JOIN LOCATION ON(LOCATION_ID = LOCAL_CODE);


-- 오라클 전용
-- 조인 순서는 상관없다. 

SELECT 
         EMP_ID
       , EMP_NAME
       , DEPT_CODE
       , DEPT_TITLE
       , LOCAL_NAME
FROM EMPLOYEE
    , DEPARTMENT
    , LOCATION
WHERE DEPT_CODE = DEPT_ID
AND LOCATION_ID = LOCAL_CODE;

/* 조건이 3개일경우 연결구문은 2개 작성, 즉 조건과 연결을 잘 고려해야한다. */

 

 

-- 직급이 대리이면서 아시아 지역에 근무하는 직원 조회
-- 사번, 이름, 직급명, 부서명, 근무지역명, 급여 조회
-- (조회세에는 모든 컬럼에 테이블 별칭을 사용하는 것이 좋다.)

-- ANSI 표준

SELECT 
        E.EMP_ID     사번
      , E.EMP_NAME  이름
      , J.JOB_NAME 직급명
      , D.DEPT_TITLE  부서명
      , L.LOCAL_NAME  근무지역명
      , E.SALARY 급여조회
      
  FROM EMPLOYEE E
  JOIN JOB J ON(E.JOB_CODE = J.JOB_CODE)
  JOIN DEPARTMENT D ON (E.DEPT_CODE = D.DEPT_ID)
  JOIN LOCATION L ON(D.LOCATION_ID = L.LOCAL_CODE)
  
  WHERE J.JOB_NAME = '대리'
  AND L.LOCAL_NAME LIKE 'ASIA%';
  
  
  -- 오라클 전용
SELECT 
       E.EMP_ID
     , E.EMP_NAME
     , J.JOB_NAME
     , D.DEPT_TITLE
     , L.LOCAL_NAME
     , E.SALARY
  FROM EMPLOYEE E
     , JOB J
     , DEPARTMENT D
     , LOCATION L
 WHERE E.JOB_CODE = J.JOB_CODE
   AND E.DEPT_CODE = D.DEPT_ID
   AND D.LOCATION_ID = L.LOCAL_CODE
   AND J.JOB_NAME = '대리'
   AND L.LOCAL_NAME LIKE 'ASIA%';

 

 

 

 

 

 

 

'Programming > ORACLE' 카테고리의 다른 글

DDL(Data Definition Language)  (0) 2022.01.24
서브 쿼리 ( Subquery)  (0) 2022.01.21
GROUP BY & HAVING  (0) 2022.01.19
함수(Function)  (0) 2022.01.18
DML(SELECT)  (0) 2022.01.17

 

ORDER BY절


  • SELECT한 컬럼에 대해 정렬을 할 때 작성하는 구문이다.
  • SELECT 구문의 가장 마지막에 작성하며, 실행순서도 가장 마지막에 수행된다.

 

표현식
ORDER BY 컬럼명 | 별칭 | 컬럼순번 정렬방식 [NULLS FIRST | LAST ]

 

 

  • 그룹함수는 단 한 개의 결과값만 산출하기 때문에, 그룹함수를 이용하여 여러 개의 결과값을 산출하기 위해서는 그룹함수가 적용될 그룹의 기준을 GROUP BY절에 기술하여 사용해야한다.

 

 

 

 

  • EMPLOYEE 테이블에서 부서코드, 그룹별 급여의 합계, 그룹별 급여의 평균(정수처리),
    인원수를 조회하고, 부서코드 순으로 정렬하세요.

 

  • EMPLOYEE 테이블에서 부서코드, 보너스를 지급받는 사원 수를 조회하고 부서코드 순으로 정렬하세요.

 

 

 

 

 

-- GROUP BY와 HAVING
-- 숫자대로 실행 순서 눈여겨보기 

/* 
    5 : SELECT 컬럼명 AS 별칭, 계산식, 함수식
    1 : FROM 참조할 테이블명
    2 : WHERE 컬럼명 | 함수식 비교연산자 비교값
    3 : GROUP BY 그룹을 묶을 컬럼명
    4 : HAVING 그룹함수식 비교연산자 비교값
    6 : ORDER BY 컬럼명 | 별칭 | 컬럼순번 정렬방식 [NULLS FIRST | LAST]

    * 진행순서를 생각해봐야 내가 원하는 데이터를 가져오는 부분이 있다. 

*/


-- DEPT_CODE 기준으로 그루핑
/* GROUP BY 절 없이는 오류가 발생하는 것을 확인할 수 있다. */
SELECT
        COUNT(*)
      , DEPT_CODE
  FROM EMPLOYEE
  GROUP BY DEPT_CODE;

 

 

-- DEPT_CODE, JOB_CODE 기준으로 그루핑
/* 부서와 직급 두 가지 기준 */
 
SELECT
        DEPT_CODE
      , JOB_CODE
      , SUM(SALARY)
      , COUNT(*)
  FROM EMPLOYEE
 GROUP BY DEPT_CODE
        , JOB_CODE
 ORDER BY 1;

 

 

-- 직원 테이블에서 부서 코드별 그룹을 지정하여
-- 부서코드, 그룹별 급여의 합계, 그룹별 급여의 평균(정수처리), 인원수
-- 조회하고 부서코드 순으로 오름차순 정렬하세요. 

SELECT
        DEPT_CODE
      , SUM(SALARY) 합계
      , FLOOR(AVG(SALARY)) 평균
      , COUNT(*) 인원수
  FROM EMPLOYEE
 GROUP BY DEPT_CODE
 ORDER BY DEPT_CODE;

 

 

-- 직원 테이블에서 직급코드, 보너스를 받는 사원 수를 조회하여
-- 직급 코드 순으로 오름차순 정렬하세요. 

SELECT
        JOB_CODE
      , COUNT(BONUS)
  FROM EMPLOYEE
 GROUP BY JOB_CODE
 ORDER BY JOB_CODE;


-- ( 조건 추가 + )
-- 직원 테이블에서 직급코드, 보너스를 받는 사원 수를 조회하여
-- 직급 코드 순으로 오름차순 정렬하세요. 
-- 단, 보너스를 받는 사람이 없는 직급 코드의 경우 RESULT SET 에서 제외한다.

SELECT
        JOB_CODE
      , COUNT(BONUS)
  FROM EMPLOYEE
 WHERE BONUS IS NOT NULL    -- 보너스가 NULL이 아니어야한다는 조건 추가
 GROUP BY JOB_CODE
 ORDER BY JOB_CODE;

 

 

  • EMPLOYEE 테이블에서 EMP_NO의 8번째 자리가 1이면 '남’, 2이면 ‘여’로 결과를 조회하고,
    성별별 급여의 평균(정수처리), 급여의 합계, 인원수를 조회한 뒤, 인원수로 내림차순 정렬하세요.
-- 직원 테이블에서 주민번호 8번째 자리를 조회하여
-- 1이면 남, 2면 여로 결과 조회하고
-- 성별별 급여 평균 (정수처리), 급여합계, 인원수를 조회한 뒤
-- 인원수로 내림차순 정렬하세요. 

SELECT
        DECODE(SUBSTR(EMP_NO, 8, 1), 1, '남', 2, '여') AS 성별
      , FLOOR(AVG(SALARY)) 평균
      , SUM(SALARY) 합계
      , COUNT(*) 인원수
FROM EMPLOYEE

/* 성별기준정렬 단, 오더바이와는 달리 그룹바이에서는 별칭을 쓸 수 없다. 
실행 순서상 아직 실행 되지 않았기 때문이다. 오버바이는 가장 마지막에 실행되므로
별칭 만으로 사용할 수 있다.*/
/* GROUP BY 절에서는 SELECT 절의 별칭 사용 불가 */
GROUP BY DECODE(SUBSTR(EMP_NO, 8, 1), 1, '남', 2, '여')
ORDER BY 인원수 DESC;



-- GROUP BY 절 : 같은 값들이 여러 개 기록 된 컬럼을 가지고
--                 같은 값들을 하나의 그룹으로 묶음
-- GROUP BY 컬럼명 | 함수식, ... (여러개를 나열 해 그루핑 할 수 있다.)
-- 그룹으로 묶은 값에 대해서 SELECT 절에서 그룹 함수를 사용한다.

 

 

 

 

 

HAVING 절


  • 그룹함수로 값을 구해올 그룹에 대해 조건을 설정할 때는 HAVING절에 기술한다.
    (WHERE절은 SELECT에 대한 조건이다.)

 

  • 급여 3000000원 이상인 직원의 그룹별 평균

 

 

  • 급여 평균이 3000000원 이상인 그룹에 대한 평균

 

-- HAVING 절 :  그룹함수로 구해올 그룹에 대해 조건을 설정할 때 사용
-- HAVING 컬럼명 | 함수식 비교연산자 비교값

-- HAVING 과 WHERE의 차이를 알아보자

/* 300만원 이상의 월급을 받는 사원들을 대상으로 
   부서별 그룹 월급 평균 계산*/

SELECT
        DEPT_CODE
      , FLOOR(AVG(SALARY)) 평균
  FROM EMPLOYEE
 WHERE SALARY > 3000000
 GROUP BY DEPT_CODE
 ORDER BY 1;



/* 모든 직원을 대상으로 부서별 월급 평균을 구한 뒤
   평균이 300만원 이상인 부서 조회*/

SELECT
        DEPT_CODE
      , FLOOR(AVG(SALARY)) 평균
  FROM EMPLOYEE
 GROUP BY DEPT_CODE
 HAVING FLOOR(AVG(SALARY)) > 3000000
 ORDER BY 1;

 

-- 급여 합계가 가장 많은 부서의 부서코드와 급여 합계를 구하세요.
/* HAVING은 GROUP BY와 함께간다. DEPT_CODE 내에서 '가장 많은'
   이라는 조건을 찾기위해 HAVING 절에서 서브쿼리를 이용한다. */

SELECT
        DEPT_CODE
      , SUM (SALARY)
  FROM EMPLOYEE
 GROUP BY DEPT_CODE
 HAVING SUM(SALARY) = (SELECT MAX(SUM(SALARY))
                         FROM EMPLOYEE
                        GROUP BY DEPT_CODE
                       );

 

 

 

 

 

 

 

 

 

 

ROLLUP과 CUBE


  • 그룹별 산출한 결과값의 집계를 계산하는 함수이다.

 

 

  • 인자로 전달받은 그룹 중에 가장 먼저 지정한 그룹별 합계와 총 합계를 구한다.

 

 

-- 집계 함수
-- ROLLUP 함수 : 그룹별로 중간 집계 처리를 하는 함수
-- GROUP BY 절에서만 사용하는 함수
-- 그룹별로 묶여진 값에 대한 중간 집계와 총 집계를 구할 때 사용한다.
-- 그룹별로 계산 된 결과값들에 대한 총 집계가 자동으로 추가 됨

/* ROLLUP 과 CUBE 의 차이점
   : 전달하는 값이 여러개가 될 때 차이점을 확인할 수 있다. */

SELECT
        JOB_CODE
      , SUM(SALARY)
  FROM EMPLOYEE
  GROUP BY ROLLUP(JOB_CODE)
  ORDER BY 1;

/* 8행 NULL : 1~7에 묶여진 그룹에 대한 총 집계값 */




-- CUBE : 그룹별 산출 결과를 집계하는 함수

SELECT
        JOB_CODE
      , SUM(SALARY)
  FROM EMPLOYEE
  GROUP BY CUBE(JOB_CODE)
  ORDER BY 1;

 

 

 

 

CUBE


  • 그룹으로 지정된 모든 그룹에 대한 합계와 총 합계를 구한다.

 

 

 

 

 

 

 

 

ROLLUP과 CUBE


 

 

/* ROLLUP 과 CUBE 의 차이 확인 */

-- ROLLUP : 인자로 전달한 그룹 중에서 가장 먼저 지정한 그룹별
--          합계와 총 합계를 구하는 함수

SELECT
        DEPT_CODE
      , JOB_CODE
      , SUM(SALARY)
  FROM EMPLOYEE
  GROUP BY ROLLUP(DEPT_CODE, JOB_CODE)
  ORDER BY 1;
  
  
  
-- CUBE : 인자로 전달한 그룹 중에서 가장 먼저 지정한 그룹별
-- 그룹으로 지정 된 모든 그룹에 대한 집계와 총 합계를 구하는 함수
-- 즉, 전달받은 모든 인자에 대해 중간집계가 들어간다.
  
SELECT
        DEPT_CODE
      , JOB_CODE
      , SUM(SALARY)
FROM EMPLOYEE
GROUP BY CUBE(DEPT_CODE, JOB_CODE)
ORDER BY 1;

/* 8행 NULL : 1~7에 묶여진 그룹에 대한 총 집계값 */

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

GROUPING


  • ROLLUP이나 CUBE에 의한 집계 산출물이 인자이다.
  • 전달받은 컬럼 집합의 산출물이면 0을 반환하고, 아니면 1을 반환하는 함수이다.

 

SELECT
	DEPT_CODE
	  , JOB_CODE
	  , SUM(SALARY)
	  , CASE
	WHEN GROUPING(DEPT_CODE) = 0 AND GROUPING(JOB_CODE) = 1 THEN ‘부서별합계’
	WHEN GROUPING(DEPT_CODE) = 1 AND GROUPING(JOB_CODE) = 0 THEN ‘직급별합계’
	WHEN GROUPING(DEPT_CODE) = 1 AND GROUPING(JOB_CODE) = 1 THEN ‘총합계’
	ELSE ‘그룹별합계’
	END AS 구분
 FROM EMPLOYEE
GROUP BY CUBE(DEPT_CODE, JOB_CODE)
ORDER BY 1;

 

 

-- GOUPING 함수 : ROLLUP 이나 CUBE 에 의한 산출물이
-- 인자로 전달받은 컬럼 집합의 산출물이면 0을 반환하고,
-- 아니면 1을 반환하는 함수 

SELECT
        DEPT_CODE
      , JOB_CODE
      , SUM(SALARY)
      , GROUPING(DEPT_CODE) "부서별그룹묶인상태"
      , GROUPING(JOB_CODE) "직급별그룹묶인상태"
FROM EMPLOYEE
GROUP BY CUBE(DEPT_CODE, JOB_CODE)
ORDER BY 1;

/* 위와같이 작성하면 무엇이 그룹별이고 무엇이 부서별인지 알아보기 힘들다.
   아래와같이 선택함수를 이용해 고쳐주면 좀 더 쉽게 알아볼 수 있다.*/




SELECT
        DEPT_CODE
      , JOB_CODE
      , SUM(SALARY)
      , CASE
            WHEN GROUPING(DEPT_CODE) = 0 AND GROUPING(JOB_CODE) = 1 THEN '부서별합계'
            WHEN GROUPING(DEPT_CODE) = 1 AND GROUPING(JOB_CODE) = 0 THEN '직급별합계'
            WHEN GROUPING(DEPT_CODE) = 0 AND GROUPING(JOB_CODE) = 0 THEN '그룹별합계'
            ELSE '총합계'
            END 구분
FROM EMPLOYEE
GROUP BY CUBE(DEPT_CODE, JOB_CODE)
ORDER BY 1;

 

 

 

 

 

 

 

SET OPERATION


  • 여러 개의 SELECT 결과물을 하나의 쿼리로 만드는 연산자이다.

 

 

UNION : 합쳐지되 중복영역은 한번만 나옴
UNION ALL : 중복이 제거되지않고 중복 영역이 두번 나옴
INTERSECT : 공통되는 영역만 조회
MINUS : 첫번째 실험 결과에서 두번째 실험결과는 제외

 

 

 

 

 

 

 

 

 

UNION


  • UNION과 UNION ALL은 여러 개의 쿼리 결과를 하나로 합치는 연산자이다.
  • 그 중 UNION은 중복된 영역을 제외하여 하나로 합치는 연산자이다.

 

 

-- SET OPERATION(집합연산)
-- UNION : 여러개의 쿼리 결과를 하나로 합치는 연산자이다.
--          중복 된 영역을 제외하여 하나로 합친다.

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , SALARY
FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'

UNION       -- 중간에 집합 연산자를 넣는다.

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000;


-- 14개 행에서 중복값 2개가 없어져 12개가 산출되는 것을 확인할 수 있다.

 

 

 

 

 

 

 

UNION ALL


  • UNION ALL은 UNION과 같은 여러 쿼리 결과물에 대한 합집합을 의미한다.
  • UNION과의 차이점은 중복된 영역을 모두 포함시키는 연산자라는 점이다.

 

 

-- UNION ALL : 여러개의 쿼리를 하나로 합치는 연산자
--             UNION과 차이점은 중복 영역을 모두 포함시킨다.

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , SALARY
FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'

UNION ALL       -- 중간에 집합 연산자를 넣는다.

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000;

-- 14개 행에서 중복값 2개를 포함, 두번 조회 되어 14개가 산출되는 것을 확인할 수 있다.

 

 

 

 

 

 

INTERSECT


  • 여러 개의 SELECT 결과에서 공통된 부분만 결과로 추출한다.
  • 즉, 수행결과에 대한 교집합이라고 볼 수 있다.

 

 

-- INTERSECT : 여러개의 SELECT 한 결과에서 공통 부분만 결과로 추출
-- 수학에서의 교집합과 비슷하다. 

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , SALARY
FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'

INTERSECT       -- 중간에 집합 연산자를 넣는다.

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000;

 

 

 

 

 

 

MINUS


  • 선행 SELECT 결과에서 다음 SELECT 결과와 겹치는 부분을 제외한 나머지 부분만 추출한다.
    즉, 두 쿼리 결과물의 차집합이라고 볼 수 있다.

 

 

-- MINUS : 선행 SELECT 결과에서 다음 SELECT 결과와 겹치는 부분을
-- 제외한 나머지 부분만 추출, 수학에서의 차집합과 비슷하다.

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , SALARY
FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'

MINUS      -- 중간에 집합 연산자를 넣는다.

SELECT
        EMP_ID
      , EMP_NAME
      , DEPT_CODE
      , SALARY
FROM EMPLOYEE
WHERE SALARY > 3000000;

/* 앞의결과 - 뒤의 결과 
   앞의결과에서 뒤의 결과가 빠진 것을 알 수 있다. */

 

 

 

 

GROUPING SETS


  • 그룹별로 처리된 여러 개의 SELECT문을 하나로 합친 결과를 원할 때 사용한다.
  • SET OPERATOR(집합연산자) 사용한 결과와 동일한 결과를 얻을 수 있다.

 

 

-- GOUPING SETS : 그룹별로 처리 된 여러개의 SELECT 문을 하나로
--                 합칠때 사용한다. SET OPERATION과 결과 동일함


SELECT
        DEPT_CODE
      , JOB_CODE
      , MANAGER_ID  -- 본인을 관리하는 사원의 사번
      , FLOOR(AVG(SALARY))
    FROM EMPLOYEE
    /* 다양한 그루핑 조합을 넣는다. */
    GROUP BY GROUPING SETS ((DEPT_CODE, JOB_CODE, MANAGER_ID)
                            , (DEPT_CODE, MANAGER_ID)
                            , (JOB_CODE, MANAGER_ID)
                            );

 

 

 

 

 

 

'Programming > ORACLE' 카테고리의 다른 글

서브 쿼리 ( Subquery)  (0) 2022.01.21
JOIN  (0) 2022.01.19
함수(Function)  (0) 2022.01.18
DML(SELECT)  (0) 2022.01.17
데이터 베이스 개요  (0) 2022.01.17

 

 

 

 

 

 

함수(Function)

 

 

 

 

 

 

함수란?


  • 하나의 큰 프로그램에서 반복적으로 사용되는 부분들을 분리하여 작성해 놓은 작은 서브 프로그램이다.
  • 호출하며 값을 전달하면 수행 결과를 리턴하는 방식으로 사용된다.

 

 

 

 

 

 

함수의 유형


 

-- 그룹함수와 단일행함수
-- 함수(Function) : 컬럼값을 읽어서 계산하 결과를 리턴함
-- 단일행 함수 : 컬럼에 기록된 N개의 값을 읽어서 N개의 결과를 리턴
-- 그룹함수 :

-- SELECT 절에서 단일행 함수와 그룹 함수를 함께 사용 못한다.
--: 결과 행의 갯수가 다르기 때문에

-- 그룹함수 : SUN, AVG, MAX, MIN, COUNT
-- SUM(숫자가 기록된 컬럼명) : 합계를 구하여 리턴
SELECT
        SUM(SALARY)
  FROM  EMPLOYEE;

 

 

 

--MIN(컬럼명) : 컬럼에서 가장 작은 값 리턴
-- 취급하는 자료형은 ANY TYPE
SELECT
        MIN(EMAIL)
      , MIN (HIRE_DATE)
      , MIN (SALARY)
 FROM  EMPLOYEE;

 

 

-- MAX(컬럼명) : 컬럼에서 가장 큰 값 리턴
-- 취급하는 자료형은 ANY TYPE
SELECT
       MAX(EMAIL)
     , MAX(HIRE_DATE)
     , MAX(SALARY)
  FROM EMPLOYEE;

 

 

 

 

 

 

 

 

 

 

문자 처리 함수


구분 입력 값 타입 리턴 값 타입 설명
LENGTH CHARACTER NUMBER 문자열의 길이를 반환한다.
LENGTHB 문자열의 바이트 크기를 반환한다.
INSTR 특정 문자의 위치를 반환한다.
INSTRB 특정 문자의 위치 바이트 크기를 반환한다.
LPAD CHARACTER 문자열을 지정된 숫자만큼의 크기로 설정하고, 지정한 문자를 왼쪽부터 채워서 생성된 문자열을 리턴 한다.
RPAD 문자열을 지정된 숫자만큼의 크기로 설정하고, 지정한 문자를 오른쪽부터 채워서 생성된
문자열을 리턴 한다.
RTRIM 왼쪽부터 지정한 문자를 잘라내고 남은 문자를 리턴한다.
LTRIM 오른쪽부터 지정한 문자를 잘라내고 남은 문자를 리턴한다.
TRIM 왼쪽/오른쪽/양쪽부터 지정한 문자를 잘라내고 남은 문자를 리턴한다.
SUBSTR 지정한 위치에서 지정한 길이만큼 문자를 잘라내어 리턴한다.
SUBSTRB 지정한 위치에서 지정한 바이트만큼 문자를 잘라내어 리턴한다.
LOWER 전달받은 문자/문자열을 소문자로 변환하여 리턴한다.
UPPER 전달받은 문자/문자열을 대문자로 변환하여 리턴한다.
INITCAP 전달받은 문자/문자열의 첫 글자를 대문자로, 나머지 글자는 소문자로 변환하여 리턴한다.
CONCAT 인자로 전달받은 두 개의 문자/문자열을 합쳐서 리턴한다.
REPLACE 전달받은 문자열중에 지정한 문자를 인자로 전달받은 문자로 변환하여 리턴한다.

 

 

 

 

 

 

 

문자 처리 함수 - LENGTH


  • 주어진 컬럼 값/문자열의 길이(문자 개수)를 반환하는 함수
작성법 리턴 값 타입 파라미터
LENGTH (CHAR | STRING) NUMBER CHARACTER 타입의 컬럼 또는 임의의 문자열

 

 

 

문자 처리 함수 - LENGTHB


  • 주어진 컬럼 값 / 문자열의 길이(BYTE)를 반환하는 함수
작성법 리턴 값 타입 파라미터
LENGTHB(CHAR | STRING) NUMBER CHARACTER 타입의 컬럼 또는 임의의 문자열

 

 

-- 단일행 함수
 -- 문자 관련 함수
 -- : LENGTH, LENGTHB, SUBSTR, UPPER, LOWER, INSTR...
 
 SELECT
        LENGTH('오라클')
      , LENGTHB('오라클')
   FROM DUAL;
   /* LENGTH는 정확히 세단어로 세지만
   LENGTHB는 바이트 단위로 센다. 한글자당 3BYTE이기 때문
   그렇다면 한글값이 아닌것은 어떨까?
   */
   
   
   SELECT
        LENGTH(EMAIL)
      , LENGTHB(EMAIL)
   FROM EMPLOYEE;

 

 

 

 

 

 

 

 

문자 처리 함수 - INSTR


  • 찾는 문자(열)이 지정한 위치부터 지정한 회수만큼 나타난 시작 위치를 반환
작성법 리턴 값 타입
INSTR(STRING, STR, [POSITION,[OCCURRENCE]] NUMBER

 

파라미터 설명
STRING 문자 타입 컬럼 또는 문자열
STR 찾으려는 문자(열)
POSITION 찾을 위치 시작 값(기본값 1)
POSITION > 0 : STRING의 시작부터 끝 방향으로 찾음
POSITION < 0 : STRING의 끝부터 시작 방향으로 찾음
OCCURRENCE SUBSTRING이 반복될 때의 지정하는 빈도(기본값 1), 음수 사용 불가

 

 

  • ex: EMAIL 컬럼의 문자열 중 “@”의 위치를 구하시오

 

 

--INSTR ('문자열' | 컬럼명, '문자', 찾을 위치의 시작값, [빈도])
 
SELECT
        EMAIL
      , INSTR(EMAIL, '@', -1) 위치
FROM EMPLOYEE;
 
 /* 이메일속 @ 문자를 뒤에서부터 위치를 찾으려는 함수  */

 

 

 

 

SELECT INSTR ('AABAACAABBAA', 'B') FROM DUAL;
/* 아무값을 전달하지않으면 가장 처음부터 찾는다. */
SELECT INSTR ('AABAACAABBAA', 'B', 1) FROM DUAL;
/* 1이라는 값을 넣으면 처음부터 시작해서 찾는다. */
SELECT INSTR ('AABAACAABBAA', 'B', -1) FROM DUAL;
/* -1은 뒤에서부터 가장 첫번째 B를 찾았다는 의미 */
SELECT INSTR ('AABAACAABBAA', 'B', -1, 2) FROM DUAL;
/* 시작은 뒤에서 하되 두번째 (빈도) B를 찾으라는 의미 */

 

 

 

 

 

 

 

 

 

-- COUNT(* | 컬럼명) : 행의 갯수를 헤아려서 리턴
-- COUNT ([DISTINCT] 컬럼명) : 중복을 제거한 행 갯수 리턴
-- COUNT(*) : NULL을 포함한 전체 행 갯수 리턴, * = 모든을 의미
-- COUNT(컬럼명) : NULL을 제외한 실제값이 기록된 행 갯수 리턴
SELECT
        COUNT(*)
      , COUNT (DEPT_CODE)
      , COUNT (DISTINCT DEPT_CODE)
 FROM  EMPLOYEE;

 

 

 

 

 

 

문자 처리 함수 – LPAD/ RPAD


  • 주어진 컬럼 문자열에 임의의 문자열을 왼쪽/ 오른쪽에 덧붙여 길이 N의 문자열을 반환하는 함수
작성법 리턴 값 타입
LPAD (STRING, N, [STR])
RPAD (STRING, N, [STR])
CHARACTER

 

파라미터 설명
STRING 문자 타입 컬럼 또는 문자열
N 반환할 문자(열)의 길이(바이트)
원래 STRING 길이보다 작다면 N만큼 잘라서 표시한다.
STR 덧붙이려는 문자(열), 생략 시 공백문자임

 

 

 

 

 

--LPAD / RPAD : 주어진 컬럼 문자열에 임의의 문자열을 덧붙여
--              길이 N의 문자열을 반환하는 함수
  
/* 오른쪽 정렬, 빈자리는 #으로 채움 */
SELECT
        LPAD (EMAIL, 20, '#')
FROM EMPLOYEE;



/* 왼쪽정렬, 빈자리는 #으로 채움 */
SELECT
        RPAD (EMAIL, 20, '#')
FROM EMPLOYEE;
  
  
  
/* 자리수가 모자라면 그냥 잘려져서 나온다. */  
SELECT
        RPAD (EMAIL, 10)
FROM EMPLOYEE;

 

 

 

 

 

 

 

문자 처리 함수 – LTRIM / RTRIM


  • 주어진 컬럼이나 문자열의 왼쪽 혹은 오른쪽에서 지정한 STR에 포함된모든 문자를 제거한 나머지를 반환한다.

 

작성법 리턴 값 타입
LTRIM(STRING, STR)
RTRIM(STRING, STR)
CHARACTER

 

파라미터 설명
STRING 문자 타입 컬럼 또는 문자열
STR 제거하려는 문자(열), 생략하면 공백문자

 

 

 

 

문자 처리 함수 - LTRIM

 

 

 

문자 처리 함수 - RTRIM

 

 

 

문자 처리 함수 - TRIM


  • 주어진 컬럼이나 문자열의 앞/뒤/양쪽에 있는 지정한 문자를 제거한 나머지를 반환한다.
작성법 리턴 값 타입
TRIM ( STRING )
TRIM ( CHAR FROM STRING)
TRIM ( LEADING | TRAILING | BOTH [CHAR] FROM STRING)
CHARACTER

 

파라미터 설명
STRING 문자 타입 컬럼 또는 문자열
CHAR 제거하려는 문자, 생략하면 공백문자
LEADING TRIM할 CHAR의 위치를 지정한다.
앞(LEADING) / 뒤(TRAILING) / 양쪽(BOTH) 지정 가능 (기본값 양쪽)

 

 

문자 처리 함수 - TRIM

 

 

 

-- LTRIM / RTRIM : 주어진 컬럼이나 문자열 왼쪽/오른쪽에서
--                 지정한 문자 혹은 문자열을 제거한 나머지를 반환하는 함수이다.
  
  /* 오른쪽의 문자, 숫자는 제거하지 못한다. */
SELECT LTRIM ('   GREEDY') FROM DUAL;
SELECT LTRIM ('   GREEDY', ' ') FROM DUAL;
SELECT LTRIM ('000123456', '123') FROM DUAL;
SELECT LTRIM ('123123GREEDY123', '123') FROM DUAL;
SELECT LTRIM ('ACABACGREEDY', 'ABC') FROM DUAL;
/* ABC는 덩어리가 아니고 보이는 즉시 다 제거해준다. */
SELECT LTRIM ('2782GREEDY', '0123456789') FROM DUAL;
/* 문자열중 숫자만 골라서 지우고 싶을떄 다음과같이 작성한다. */



/* 왼쪽의 문자, 숫자는 제거하지 못한다. 오른쪽만 가능 */
SELECT RTRIM ('GREEDY   ') FROM DUAL;
SELECT RTRIM ('GREEDY   ', ' ') FROM DUAL;
SELECT RTRIM ('123456000', '0') FROM DUAL;
SELECT RTRIM ('GREEDY123123', '123') FROM DUAL;
SELECT RTRIM ('123123GREEDY123', '123') FROM DUAL;
SELECT RTRIM ('GREEDYACABAC', 'ABC') FROM DUAL;
SELECT RTRIM ('GREEDY5782', '0123456789') FROM DUAL;
  
  
  
-- TRIM : 주어진 컬럼이나 문자열의 앞/뒤에 지정한 문자를 제거
SELECT TRIM ('   GREEDY   ') FROM DUAL;
SELECT TRIM ('Z' FROM 'ZZZGREEDYZZZ') FROM DUAL;
/* LTRIM 처럼 기능 */
SELECT TRIM (LEADING 'Z' FROM 'ZZZGREEDYZZZ') FROM DUAL;
/* RTRIM 처럼 기능 */
SELECT TRIM (TRAILING 'Z' FROM 'ZZZGREEDYZZZ') FROM DUAL;
SELECT TRIM (BOTH 'Z' FROM 'ZZZGREEDYZZZ') FROM DUAL;

 

 

 

문자 처리 함수 – SUBSTR


  • 컬럼이나 문자열에서 지정한 위치부터 지정한 개수의 문자열을 잘라내어 리턴하는 함수이다.

 

작성법 리턴 값 타입
SUBSTR ( STRING, POSITION, [LENGTH] ) CHARACTER

 

파라미터 설명
STRING 문자 타입 컬럼 또는 문자열
POSITION 문자열을 잘라낼 위치이다.
양수이면 시작방향에서 지정한 수 만큼 위치를 의미하고,
음수이면 끝 방향에서 지정한 수 만큼의 의치를 의미한다.
LENGTH 반환할 문자의 개수를 의미한다.
(생략시 문자열의 끝까지를 의미하고, 음수이면 NULL을 리턴함)

 

 

문자 처리 함수 – SUBSTR

 

-- SUBSTR : 컬럼이나 문자열에서 지정한 위치로부터 지정한 갯수의 문자열을
--          잘라서 리턴하는 함수이다.

/* 5번째 위치에서 2개의 길이 리턴 */
SELECT SUBSTR('SHOWMETHEMONEY', 5, 2) FROM DUAL;
/* 7번째 위치에서 해당 문자열 끝까지 리턴 */
SELECT SUBSTR('SHOWMETHEMONEY', 7) FROM DUAL;
/* THE, 뒤에서 부터 센 8번째 자리에서 3가지 길이를 리턴 */
SELECT SUBSTR('SHOWMETHEMONEY', -8, 3) FROM DUAL;
/* 두번째 위치에서 공백포함한 5가지 길이 리턴 */
SELECT SUBSTR('쇼우 미 더 머니', 2, 5) FROM DUAL;

 

-- EMPLOYEE 테이블에서 직원들의 주민번호를 조회하여
-- 사원명, 생년, 생월, 생일을 각각 분리하여 조회
-- 단, 컬럼의 별칭은 사원명, 생년, 생월, 생일로 한다.
SELECT
        EMP_NAME 사원명
      , SUBSTR(EMP_NO, 1, 2) 생년
      , SUBSTR(EMP_NO, 3, 2) 생월
      , SUBSTR(EMP_NO, 5, 2) 생일
  FROM EMPLOYEE;

 

 

-- 날짜 데이터에서 사용할 수 있다.
-- 직원들의 입사일에도 입사년도, 입사월, 입사 날짜를 분리하여 조회
SELECT
        HIRE_DATE
      , SUBSTR(HIRE_DATE, 1, 2) 입사년도
      , SUBSTR(HIRE_DATE, 4, 2) 입사월
      , SUBSTR(HIRE_DATE, 7, 2) 입사날짜
  FROM EMPLOYEE;

 

-- WHERE절에서도 함수 사용이 가능하다.
-- EMP_NO를 통해 성별을 판단하여 여직원들의 모든 컬럼 정보를 조회하세요

SELECT
        *
FROM EMPLOYEE
WHERE SUBSTR(EMP_NO, 8, 1) = '2';

 

-- WHERE절에서는 단일행 함수만 사용 가능하다.
/* 그룹함수는 허가되지않습니다라는 오류 발생 */
SELECT
        *
FROM EMPLOYEE
WHERE SALARY > AVG(SALARY);

 

 

-- 함수 중첩 사용 가능 : 함수 안에서 함수를 사용할 수 있음
-- EMPLOYEE 테이블에서 사원명, 주민번호 조회
-- 단, 주민번호는 생년월일만 보이게 하고, '-' 다음의 값은 '*'로 바꿔서 출력
SELECT
        EMP_NAME
      , RPAD(SUBSTR(EMP_NO, 1, 7), 14, '*')
      FROM EMPLOYEE;

 

--EMPLOYEE  테이블에서 사원명, 이메일과
-- @ 이후를 제외한 아이디 조회

/* @ 앞까지만 추출하기 위해서 갯수를 @-1 처리함 */
SELECT
        EMP_NAME
      , EMAIL
      , SUBSTR(EMAIL, 1,INSTR(EMAIL,'@')-1)
      FROM EMPLOYEE;

   

 

-- SUBSTRB : 바이트 단위로 추출하는 함수
SELECT
        SUBSTR('ORACLE', 3, 2)
      , SUBSTRB('ORACLE', 3, 2)
    FROM DUAL;

SELECT
        SUBSTR('오라클', 3, 2)
      , SUBSTRB('오라클', 4, 6)
    FROM DUAL;

 

 

 

문자 처리 함수 – LOWER/UPPER/INITCAP


  • 컬럼의 문자 혹은 문자열을 소문자/대문자/첫 글자만 대문자로 변환하여 리턴하는 함수이다.
작성법 리턴 값 타입
LOWER(STRING) / UPPER(STRING) / INITCAP(STRING) CHARACTER

 

파라미터 설명
STRING 문자 타입 컬럼 또는 문자열

 

 

 

-- LOWER / UPPER / INITCAP : 대소문자 변경해주는 함수
-- LOWER(문자열 | 컬럼) : 소문자로 변경해주는 함수
SELECT
        LOWER('WELCOM TO MY WORLD')
    FROM DUAL;




-- UPPER(문자열 | 컬럼) : 대문자로 변경해주는 함수
SELECT
       UPPER('welcome to my world')
    FROM DUAL;




-- INITCAP(문자열 | 컬럼) : 앞 글자만 대문자로 변경해주는 함수
SELECT
       INITCAP('welcome to my world')
    FROM DUAL;

 

 

 

 

 

문자 처리 함수 – CONCAT


  • 컬럼의 문자 혹은 문자열을 두 개 전달 받아 하나로 합친 후 리턴하는 함수이다.

 

작성법 리턴 값 타입
CONCAT(STRING, STRING) CHARACTER

 

파라미터 설명
STRING 문자 타입 컬럼 또는 문자열

 

 

 

-- CONCAT : 문자열 혹은 컬럼 두 개를 입력 받아
--          하나로 합친후 리턴
SELECT
        CONCAT('가나다라', 'ABCD')
    FROM DUAL;
    
    

SELECT
        '가나다라' || 'ABCD'
    FROM DUAL;

 

 

 

 

 

 

문자 처리 함수 – REPLACE


  • 첫 번째 인자의 문자열에서 두번째 인자의 문자열을 세 번째 인자의 문자열로
    대체 후 리턴하는 함수이다.

 

 

작성법 리턴 값 타입
REPLACE(STRING, STR1, STR2) CHARACTER

 

파라미터 설명
STRING 문자 타입 컬럼 또는 문자열
STR1 변경하려고 하는 문자 혹은 문자열
STR2 변경하려고 하는 문자 혹은 문자열

 

 

 

-- REPLACE : 컬럼 혹은 문자열을 입력 받아 변경하고자 하는 문자열을
--          새롭게 전달하려는 값으로 문자열을 바꾼 후 리턴
    
SELECT
        REPLACE('서울시 강남구 역삼동', '역삼동', '삼성동')
FROM DUAL;

 

 

 

 

 

 

 

숫자 처리 함수


 

 

 

 

숫자 처리 함수 - ABS


  •  인자로 전달받은 숫자의 절대값을 구하는 함수이다.

 

작성법 리턴 값 타입
ABS(NUMBER) NUMBER

 

파라미터 설명
NUMBER 숫자 혹은 숫자 데이터 컬럼

 

-- 숫자 처리 함수 : ABS, MOD, ROUND, FLOOR, TRUNC, CEIL

-- ABS(숫자|숫자로 된 컬럼명) :  절대값을 구하는 함수
SELECT
        ABS(-10)
      , ABS(10)
    FROM DUAL;

 

 

 

 

 

 

 

 

숫자 처리 함수 - MOD


  • 인자로 전달받은 숫자를 나누어 나머지를 구하는 함수이다.

 

작성법 리턴 값 타입
MOD (NUMBER, DIVISION) NUMBER

 

파라미터 설명
NUMBER 숫자 혹은 숫자 데이터 컬럼
DIVISION 나눌 수 혹은 나눌 숫자 데이터 컬럼

 

-- MOD(숫자 | 숫자로 된 컬럼명, 숫자 | 숫자로 된 컬럼명)
-- : 두 수를 나누어서 나머지를 구하는 함수
--  처음 인자는 나누어지는 수, 두 번째 인자는 나눌 수
    
SELECT
      MOD (10, 5)
    , MOD (10, 3)
FROM DUAL;

 

 

 

 

 

 

 

숫자 처리 함수 - ROUND


  • 인자로 전달 받은 숫자 혹은 컬럼에서 지정한 위치로부터 반올림하여 값을 리턴하는 함수이다.

 

작성법 리턴 값 타입
ROUND(NUMBER)
ROUND(NUMBER, POSITION)
NUMBER

 

파라미터 설명
NUMBER 숫자 혹은 숫자 데이터 컬럼
POSITION 반올림 할 위치 자리

 

-- ROUND (숫자 | 숫자로 된 컬럼명, [위치])
-- : 반올림해서 리턴하는 함수
SELECT ROUND (123.456) FROM DUAL;
/* 그냥 정수인 123 리턴 */
SELECT ROUND (123.456, 0) FROM DUAL;
SELECT ROUND (123.456, 1) FROM DUAL;    
SELECT ROUND (123.456, 2) FROM DUAL;     
SELECT ROUND (123.456, -2) FROM DUAL;

 

 

 

 

 

 

숫자 처리 함수 - FLOOR


  • 인자로 전달받은 숫자 혹은 컬럼의 소수점 자리의 수를 버리는 함수이다.

 

작성법 리턴 값 타입
FLOOR(NUMBER) NUMBER

 

파라미터 설명
NUMBER 숫자 혹은 숫자 데이터 컬럼

 

 

-- FLOOR (숫자 | 숫자로 된 컬럼명) : 내림처리 하는 함수
/* 위치지정은 없고 그냥 내림으로 버려서 정수를 반환 */
SELECT FLOOR (123.456) FROM DUAL;
SELECT FLOOR (123.678) FROM DUAL;

 

 

 

 

 

 

 

숫자 처리 함수 - TRUNC


  • 인자로 전달받은 숫자 혹은 컬럼의 지정한 위치부터 소수점 자리의 수를 버리는 함수이다.

 

작성법 리턴 값 타입
TRUNC(NUMBER, POSITION) NUMBER

 

파라미터 설명
NUMBER 숫자 혹은 숫자 데이터 컬럼
POSITION 버림 할 위치 자리

 

 

-- TRUNC(숫자 | 숫자로 된 컬럼명, [위치]) : 내림처리(절삭) 함수
SELECT TRUNC(123.456) FROM DUAL;
SELECT TRUNC(123.678) FROM DUAL;
/* 지정된 자릿수 아래는 절삭됨 */
SELECT TRUNC(123.456, 1) FROM DUAL;
SELECT TRUNC(123.456, 2) FROM DUAL; 
/* 1의 자리에 해당하는 3이 절삭됨 */
SELECT TRUNC(123.456, -1) FROM DUAL;

 

 

 

 

 

 

 

숫자 처리 함수 - CEIL


  • 인자로 전달받은 숫자 혹은 컬럼을 올림 계산하여 나온 값을 리턴하는 함수이다.

 

작성법 리턴 값 타입
CEIL(NUMBER) NUMBER

 

파라미터 설명
NUMBER 숫자 혹은 숫자 데이터 컬럼

 

--CEIL (숫자 | 숫자로 된 컬럼명) :  올림 처리 함수
SELECT CEIL(123.456) FROM DUAL;
SELECT CEIL(123.678) FROM DUAL;
    
SELECT
        ROUND(123.456)
      , FLOOR(123.456)
      , TRUNC(123.456)
      , CEIL(123.456)
    FROM DUAL;

 

 

 

숫자 처리 함수 – ROUND/FLOOR/TRUNC/CEIL

 

 

 

 

 

 

 

날짜 처리 함수


 

 

 

 

날짜 처리 함수 - SYSDATE


작성법 리턴 값 타입
SYSDATE DATE

 

 

 

 

 

날짜 처리 함수 – MONTHS_BETWEEN


  • 인자로 날짜 두 개를 전달받아, 개월 수의 차이를 숫자 데이터형으로 리턴하는 함수이다.

 

작성법 리턴 값 타입
MONTHS_BETWEEN(DATE1, DATE2) NUMBER

 

파라미터 설명
DATE1 기준이 되는 날짜를 입력한다.
DATE2 개월 수를 구하려는 날짜를 입력한다.

 

  • EMPLOYEE테이블에서 사원의 이름, 입사입,근무 개월수를 조회하라.

 

 

 

 

 

 

 

날짜 처리 함수 – ADD_MONTHS


  • 인자로 전달받은 날짜에 인자로 전달받은 숫자만큼 개월 수를 더하여 특정 날짜를
    리턴하는 함수이다.

 

작성법 리턴 값 타입
ADD_MONTHS(DATE, NUMBER) DATE

 

파라미터 설명
DATE 기준이 되는 날짜를 입력한다.
DATE2 더하려는 개월 수를 입력한다.

 

  • EMPLOYEE테이블에서 사원의 이름, 입사일, 입사 후 6개월이 된 날짜를 조회하라

 

 

 

-- 날짜 처리 함수 : SYSDATE, MONTHS_BETWEEN, ADD_MONTH, LAST_DAT, EXTRACT


-- SYSDATE : 시스템에 저장 되어 있는 날짜를 반환하는 함수
SELECT SYSDATE FROM DUAL;
    
    
    
-- MONTHS_BETWEEN(날짜, 날짜)
-- : 두 날짜의 개월 수 차이를 숫자로 리턴하는 함수

SELECT
        EMP_NAME
      , HIRE_DATE
      , CEIL(MONTHS_BETWEEN(SYSDATE, HIRE_DATE))
      FROM EMPLOYEE;
    
    
-- ADD_MONTH(날짜, 숫자)
-- 날짜에 숫자만큼 개월 수 더해서 리턴
SELECT 
        ADD_MONTHS(SYSDATE, 5)
    FROM DUAL;
    
    
    
-- EMPLOYEE 테이블에서 사원의 이름, 입사일, 엽시 후 6개월이 되는 날짜 조회
SELECT
        EMP_NAME
      , HIRE_DATE
      , ADD_MONTHS(HIRE_DATE, 6)
    FROM EMPLOYEE;



-- EMPLOYEE 테이블에서 근무년수가 20년 이상인 직원의 모든 컬럼 조회
/* 두가지 방법 */
SELECT
        *
FROM EMPLOYEE
-- WHERE ADD_MONTHS(HIRE_DATE, 240) <= SYSDATE;
-- WHERE MONTHS_BETWEEN(SYSDATE, HIRE_DATE) >= 240;

 

 

 

 

 

 

 

 

 

 

날짜 처리 함수 – NEXT_DAY


 

 

 

 

 

 

-- NEXT_DAY(기준날짜, 요일(문자|숫자))
-- : 기준날짜에서 구하려는 요일에 가장 가까운 날짜 리턴
SELECT SYSDATE, NEXT_DAY(SYSDATE, '목요일') FROM DUAL;
SELECT SYSDATE, NEXT_DAY(SYSDATE, '목') FROM DUAL;
/* 일요일 기준으로 목요일은 5번째이다. */
SELECT SYSDATE, NEXT_DAY(SYSDATE, 5) FROM DUAL;
/* 영어로 입력시 지정한 요일이 부적합하다는 오류가 뜬다. */
SELECT SYSDATE, NEXT_DAY(SYSDATE, 'THURSDAY') FROM DUAL;

/* 영어로 세션 변경후에는 THURSDAY가 잘 작동된다. 
변경 구절은 반드시 실행을 해 준후 다음 줄을 테스트 해야 한다.*/
ALTER SESSION SET NLS_LANGUAGE = AMERICAN;
SELECT SYSDATE, NEXT_DAY(SYSDATE, 'THURSDAY') FROM DUAL;
  
/* 한국어로 다시 세션을 변경 해 준다. */
ALTER SESSION SET NLS_LANGUAGE = KOREAN;
SELECT SYSDATE, NEXT_DAY(SYSDATE, '목요일') FROM DUAL;

 

 

 

 

 

 

 

 

날짜 처리 함수 – LAST_DAY


  • 인자로 전달받은 날짜가 속한 달의 마지막 날짜를 구하여 리턴한다.

 

작성법 리턴 값 타입
LAST_DAY (DATE) DATE

 

파라미터 설명
DATE 기준이 되는 날짜를 입력한다.

 

  • EMPLOYEE 테이블에서 사원의 이름,입사일, 입사일의 마지막날을 조회하세요

 

-- LAST_DAY(날짜) : '해당 월의 마지막 날짜'를 구하여 리턴
SELECT SYSDATE, LAST_DAY(SYSDATE) FROM DUAL;

 

-- EMPLOYEE 테이블에서 사원명, 
-- 입사일 - 오늘, 오늘 - 입사일 조회
SELECT 
        EMP_NAME
        /* 소수점 처리, 마이너스 값을 절대값 처리*/
      , CEIL(ABS(HIRE_DATE - SYSDATE)) "근무일수1"
      , CEIL(SYSDATE - HIRE_DATE) "근무일수2"
    FROM EMPLOYEE;
    
    
-- EMPLOYEE 테이블에서 사원명, 입사일, 입사한 월의 근무일수를 조회하세요. 
SELECT
        EMP_NAME
      , HIRE_DATE
      , LAST_DAY(HIRE_DATE) - HIRE_DATE + 1 "입사월의 근무일수"
  FROM EMPLOYEE;

 

 

 

 

 

 

 

날짜 처리 함수 – EXTRACT


  • 년, 월, 일 정보를 추출하여 리턴한다.
작성법 리턴 값 타입
EXTRACT(YEAR FROM DATE)
EXTRACT(MONTH FROM DATE)
EXTRACT(DAY FROM DATE)
DATE

 

파라미터 설명
DATE 기준이 되는 날짜를 입력한다.

 

  • EMPLOYEE테이블에서 사원 이름, 입사 년, 입사 월, 입사 일을 조회하세요

 

-- EXTRACT : 년, 월, 일 정보를 추출하여 리턴하는 함수
-- EXTRACT (YEAR FROM 날짜) : 년도만 추출
-- EXTRACT (MONTH FROM 날짜) : 월만 추출  
-- EXTRACT (DAY FROM 날짜) : 날짜만 추출
SELECT
        EXTRACT (YEAR FROM SYSDATE) 년도
      ,  EXTRACT (MONTH FROM SYSDATE) 월
      ,  EXTRACT (DAY FROM SYSDATE) 일
    FROM DUAL;

 

 

 

 

-- EMPLOYEE  테이블에서 사원 이름, 입사년, 입사월, 입사일 조회
  SELECT
        EMP_NAME 사원이름
      , EXTRACT (YEAR FROM HIRE_DATE) 입사년
      , EXTRACT (MONTH FROM HIRE_DATE) 입사월
      , EXTRACT (DAY FROM HIRE_DATE) 입사일
    FROM EMPLOYEE
      /* ORDER BY : 오름차순, 기본적으로 오름차순으로 정렬 */
      /* ASC : 오름차순 */
      /* DESC : 내림차순*/
      -- ORDER BY EMP_NAME ASC;
      -- ORDER BY EMP_NAME DESC;
      -- ORDER BY 사원이름;
      
      /* 1번인 사원이름으로 정렬하고자 할 때 */
      -- ORDER BY 1;
  
  /* 같은 입사년이라는 정렬 기준을 가졌어도 
  그 안에서 번째 기준인 사원이름으로 한번 더 정렬 된다.*/
   ORDER BY 입사년 ASC, 사원이름;

 

 

-- EMPLOYEE 테이블에서 직원의 이름, 입사일, 근무년수를 조회
-- 단, 근무년수는 [현재년도 - 입사년도]로 조회
/* 단순히 2020-1986 같은 계산이 나온다. */

SELECT
        EMP_NAME
      , HIRE_DATE
      , EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM HIRE_DATE)
    FROM EMPLOYEE;
    
    
    
-- MONTH_BETWEEN으로 근무년수 조회
SELECT
        EMP_NAME
      , HIRE_DATE
      , FLOOR(MONTHS_BETWEEN(SYSDATE, HIRE_DATE) / 12) 근무년수
  FROM EMPLOYEE;

 

 

 

 

 

 

 

형변환 함수


 

 

 

 

 

형변환 함수 – TO_CHAR


  • 날짜형 데이터를 문자형 데이터로 변환하거나, 숫자데이터를 문자형데이터로
    변환하여 리턴한다.
작성법 리턴 값 타입
TO_CHAR(DATE, [FORMAT])
TO_CHAR(NUMBER, [FORMAT])
CHARACTER

 

파라미터 설명
DATE 문자형으로 변환하려는 날짜형 데이터
NUMBER 문자형으로 변환하려는 숫자형 데이터
FORMAT 문자형으로 변환시 지정할 출력 형식

 

형식 의미   의미
YYYY 년도 표현(4자리) YY 년도 표현(2자리)
MM 월을 숫자로 표현 MON 월을 알파벳으로 표현
DAY 요일 표현 DY 요일을 약어로 표현

 

 

 

 

-- 형변환 함수
-- TO_CHAR(날짜, [포맷]) : 날짜형 데이터를 문자형 데이터로 변경
-- TO_CHAR(숫자, [포맷]) : 숫자형 데이터를 문자형 데이터로 변경
  
SELECT TO_CHAR(1234) FROM DUAL;
/* 9는 빈자리를 채우지는 않는다. */
SELECT TO_CHAR(1234, '99999') FROM DUAL;
/* 0은 빈자리를 0으로 채워준다. */
SELECT TO_CHAR(1234, '00000') FROM DUAL;
SELECT TO_CHAR(1234, 'L99999') FROM DUAL;
SELECT TO_CHAR(1234, '$99999') FROM DUAL;  
SELECT TO_CHAR(1234, '00,000') FROM DUAL; 
/* 원래 표현할 수 있는 숫자보다 작다면 잘못된 상황으로 출력된다. */
SELECT TO_CHAR(1234, '999') FROM DUAL;

 

 

 

-- 직원 테이블에서 사원명, 급여 조회
-- 급여는 '\9,000,000' 형식으로 표시하세요

SELECT
        EMP_NAME
      , TO_CHAR(SALARY, 'L99,999,999')
  FROM EMPLOYEE;

 

-- 날짜 데이터 포맷 적용시에도 TO_CHAR 함수 사용
/* 24시간 체제 */
SELECT TO_CHAR(SYSDATE, 'PM HH24:MI:SS') FROM DUAL;
/* 12시간 체제, 오전 오후로 구분 */
SELECT TO_CHAR(SYSDATE, 'AM HH:MI:SS') FROM DUAL;
SELECT TO_CHAR(SYSDATE, 'MON DY, YYYY') FROM DUAL;
/* 1-18 */
SELECT TO_CHAR(SYSDATE, 'YYYY-fmMM-DD DAY') FROM DUAL;
/* 01-18 */
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD DAY') FROM DUAL;
/* 영어로 출력 TWENTY TWENTY-TWO, 1 출력
Q는 분기를 의미한다. */
SELECT TO_CHAR(SYSDATE, 'YEAR,Q') FROM DUAL;
  
SELECT
        EMP_NAME
      , TO_CHAR(HIRE_DATE, 'YYYY-MM-DD') 입사일
  FROM EMPLOYEE;

 

/* 다음형식은 날짜 형식이 부적합합니다 라는 오류가 출력 */
SELECT
        EMP_NAME
      , TO_CHAR(HIRE_DATE, 'YYYY년 MM월 DD일') 입사일
  FROM EMPLOYEE;
  
  
  
/* 해당값을 쌍따옴표로 감싸면 문자열 처리된다. */
SELECT
        EMP_NAME
      , TO_CHAR(HIRE_DATE, 'YYYY"년" MM"월" DD"일"') 입사일
  FROM EMPLOYEE;
  
  
  
/* 시간까지 표기하여 상세입사일을 출력한다. */
SELECT
        EMP_NAME
      , TO_CHAR(HIRE_DATE, 'YYYY/MM/DD HH24:MI:SS') 상세입사일
FROM EMPLOYEE;

 

-- 오늘 날짜에 대해 년도 4자리, 년도 2자리,
-- 년도 이름으로 출력
/* RR과 YY로 년도를 표현할 수 있다. */
SELECT
        TO_CHAR(SYSDATE, 'YYYY')
      , TO_CHAR(SYSDATE, 'RRRR')
      , TO_CHAR(SYSDATE, 'YY')
      , TO_CHAR(SYSDATE, 'RR')
      , TO_CHAR(SYSDATE, 'YEAR')
    FROM DUAL;
  
-- RR과 YY의 차이
-- RR은 두자리 년도를 네자리로 바꿀 때
-- 바꿀 년도가 50년 미만이면 2000년을 적용하고
-- 50년 이상히면 1900년을 적용한다.

-- YY는 년도를 바꿀 때 현재 세기(2000년)를 적용한다.
-- 99/01/01이면 YY는 2099년으로, RR은 1999로 해석한다.

/* 2098 */
SELECT
        TO_CHAR(TO_DATE('980630', 'YYMMDD'), 'YYYY-MM-DD')
  FROM DUAL;
  
/* 1998 */
SELECT
        TO_CHAR(TO_DATE('980630', 'RRMMDD'), 'YYYY-MM-DD')
  FROM DUAL;

 

 

-- 오늘 날짜에서 월만 출력
SELECT
        TO_CHAR(SYSDATE, 'MM')
     ,  TO_CHAR(SYSDATE, 'MONTH')
     ,  TO_CHAR(SYSDATE, 'MON')
     /* 로마자로 표기되는 월 RM */
     ,  TO_CHAR(SYSDATE, 'RM') 
FROM DUAL;

 

-- 오늘 날짜에서 일(DAY)만 출력
SELECT
        TO_CHAR(SYSDATE, '"1년기준 " DDD "일 째"')
     ,  TO_CHAR(SYSDATE, '"달 기준 " DD "일 째"')  -- 1월
     ,  TO_CHAR(SYSDATE, '"주 기준 " D "일 째"')
FROM DUAL;

 

-- 오늘 날짜에서 분기와 요일 출력 처리
SELECT
        TO_CHAR(SYSDATE, 'Q "분기"')
      , TO_CHAR(SYSDATE, 'DAY')   -- 화요일
      , TO_CHAR(SYSDATE, 'DY')    -- 화
  FROM DUAL;

 

 

-- EMPLOYEE  테이블에서 이름, 입사일 조회
-- 입사일 포맷은 '2018년 6월 15일 (수)' 형식으로 출력처리 하세요
  
  SELECT
        EMP_NAME
      , TO_CHAR(HIRE_DATE, 'RRRR"년" fmMM"월" DD"일" "("DY")"')
  FROM EMPLOYEE;

 

 

 

 

 

 

 

 

형변환 함수 – TO_DATE


  • 숫자 혹은 문자형 데이터를 날짜형 데이터로 변환하여 리턴한다.
작성법 리턴 값 타입
TO_DATE(CHARACTER, [FORMAT])
TO_DATE(NUMBER, [FORMAT])
DATE

 

파라미터 설명
CHARACTER 날짜형으로 변환하려는 문자형 데이터
NUMBER 날짜형으로 변환하려는 숫자형 데이터
FORMAT 날짜형으로 변환 시 입력 포맷 지정

 

  • EMPLOYEE테이블에서 2000년도 이후에 입사한 사원의 사번,이름,입사일을 조회하세요

 

 

-- TO_DATE : 문자 혹은 숫자형 데이터를 날짜형 데이터로 변환하여 리턴
-- TO_DATE(문자형 데이터, [포맷])
-- TO_DATE(숫자형 데이터, [포맷])
  
/* 10/01/01 */
SELECT
    TO_DATE('20100101', 'RRRRMMDD')
FROM DUAL;
    
    
/* 2010, 1월 */
SELECT
    TO_CHAR(TO_DATE('20100101', 'RRRRMMDD'), 'RRRR, MON')
FROM DUAL;


/* 30-10월-04 02:30:00 오후 */
SELECT
    TO_CHAR(TO_DATE('041030 143000', 'RRMMDD HH24MISS'), 'DD-MON-RR HH:MI:SS PM')
FROM DUAL;

 

 

 

-- EMPLOYEE 테이블에서 2000년도 이후에 입사한 사원의 
-- 사번, 이름, 입사일을 조회하세요.
SELECT
        EMP_ID
      , EMP_NAME
      , HIRE_DATE
  FROM EMPLOYEE
 -- WHERE HIRE_DATE >= '20000101'; -- 자동 형변환 됨 (문자열은 날짜로 자동 형변환 됨)
 -- WHERE HIRE_DATE >= 20000101; -- 자동 형변환 안됨 (숫자는 날짜로 자동형변환 안됨)
 WHERE HIRE_DATE >= TO_DATE(20000101, 'RRRRMMDD');

 

 

 

 

 

 

 

형변환 함수 – TO_NUMBER


  • 날짜 혹은 문자형 데이터를 숫자형 데이터로 변환하여 리턴 한다.
    작성법
작성법 리턴 값 타입
TO_NUMBER (CHARACTER, [FORMAT]) NUMBER

 

파라미터 설명
CHARACTER 숫자형으로 변환하려는 문자형 데이터
FORMAT 숫자형으로 변환 시 입력 포맷 지정

 

 

 

-- TO_NUMBER (문자데이터,[포멧]) : 문자데이터를 숫자로 리턴
SELECT TO_NUMBER('123456789') FROM DUAL;

 

 

 

-- 자동 형변환
/* 자동으로 숫자로 형변환 되어 579로 계산 후 출력된다. */
SELECT '123' + '456' FROM DUAL;

 

 

 

-- 숫자로 된 문자열만 가능하다.
/* 숫자 외의 문자를 넣으면 수치가 부적합하다는 에러메세지가 뜬다. */
SELECT '123' + '456A' FROM DUAL;

SELECT
        EMP_NAME
      , HIRE_DATE
  FROM EMPLOYEE
WHERE HIRE_DATE = '90/02/06';     -- 자동형변환

 

 

 

--EMPLOYEE 테이블에서 사번이 홀수인 직원들의 정보 모두 조회
SELECT
        *
  FROM EMPLOYEE
 WHERE MOD(EMP_ID, 2) = 1;
 
 
 
-- 자동형변환이 되지만 그럼에도 TO_DATE가 필요한 경우 - 계산
SELECT 
        TO_NUMBER('1,000,000', '99,999,999') + TO_NUMBER('500,000', '999,999') 
  FROM DUAL;

 

 

 

-- 직원 테이블에서 사원 번호가 201인 사원의
-- 이름, 주민번호 앞자리, 주민번호 뒷자리,
-- 주민번호 앞자리와 뒷자리의 합을 조회하세요
-- 단, 자동 형변환 사용하지 않고 조회 
  
SELECT
        EMP_NAME
      , EMP_NO
      , SUBSTR(EMP_NO, 1, 6) --앞부분
      , SUBSTR(EMP_NO, 8)   -- 뒷부분
      , TO_NUMBER(SUBSTR(EMP_NO, 1, 6)) + TO_NUMBER(SUBSTR(EMP_NO, 8)) 결과
  FROM EMPLOYEE
 WHERE EMP_ID = TO_CHAR(201);

 

 

 

 

 

 

 

 

 

NULL 처리 함수 - NVL


  • NULL로 되어 있는 컬럼의 값을 지정한 숫자 혹은 문자로 변경하여 리턴한다.
작성법 리턴 값 타입
NVL(P1, P2) NUMBER
CHARACTER

 

파라미터 설명
P1 NULL데이터를 처리할 컬럼명 혹은 값
P2 NULL값을 변경하고자 하는 값

 

 

-- NULL 처리 함수   
-- NVL (컬럼명, 컬럼값이 NULL 일때 바꿀 값)
SELECT
        EMP_NAME
      , BONUS
      , NVL (BONUS, 0)
  FROM EMPLOYEE;
  
  
-- NVL2 (컬럼명, 바꿀값1, 바꿀값2)
-- 해당 컬럼이 값이 있으면 바꿀값1로 변경,
-- 해당 컬럼이 NULL 이면 바꿀값2로 변경
    

-- 직원 정보에서 보너스 포인트가 NULL인 직원은 0.5로
-- 보너스 포인트가 NULL 이 아닌 경우 0.7로 변경하여 조회 
SELECT
        EMP_NAME
      , BONUS
      , NVL2(BONUS, 0.7, 0.5)
  FROM EMPLOYEE;

 

 

 

 

 

 

 

 

 

 

 

선택 함수 - DECODE


  • 여러 가지 경우에 선택을 할 수 있는 기능을 제공한다.(일치하는 값)
작성법 리턴 값 타입
DECODE(표현식, 조건1, 결과1, 조건2, 결과2, 조건3, 결과3, …) 결과

 

파라미터 설명
표현식 값에 따라 선택을 다르게 할 컬럼 혹은 값 입력
조건 해당 값이 참인지 거짓인지 여부를 판단한다.
결과 해당 조건과 일치하는 경우 결과를 리턴한다.
DEFAULT 모든 조건이 불일치 시 리턴할 값

 

 

-- 선택함수
-- 여러가지 경우에 선택할 수 있는 기능을 제공한다.
-- DECODE(계산식 | 컬럼명, 조건값1, 선택값1, 조건값2, 선택값2 ...)
SELECT
        EMP_ID
      , EMP_NAME
      , EMP_NO
      , DECODE(SUBSTR(EMP_NO, 8, 1), '1', '남', '2', '여')
  FROM EMPLOYEE;
  
  
  
-- 마지막 인자로 조건값 없이 선택값을 작성하면
-- 아무것도 해당하지 않을 때 마지막에 작성한 선택값을 무조건 선택한다.

SELECT
        EMP_ID
      , EMP_NAME
      , EMP_NO
      , DECODE(SUBSTR(EMP_NO, 8, 1), '1', '남', '여')
  FROM EMPLOYEE;

 

-- 직원의 급여를 인상하고자 한다.
-- 직급 코드가 J7인 직원은 급여의 10%를 인상하고
-- 직급코드가 J6인 직원은 급여의 15%를 인상하고
-- 직급코드가 J5인 직원은 급여의 20%를 인상한다.
-- 그 외 직급의 직원은 5%만 인상한다.
-- 직원 테이블에서 직원명, 직급코드, 급여, 인상급여(위 조건)을 조회하세요.
    
SELECT
        EMP_NAME
      , JOB_CODE
      , SALARY
      , DECODE(JOB_CODE, 'J7', SALARY * 1.1,
                         'J6', SALARY * 1.15,
                         'J5', SALARY * 1.2,
                               SALARY * 1.05) 인상급여
  FROM EMPLOYEE;

 

 

 

 

 

 

 

 

 

선택 함수 – CASE


  • 여러 가지 경우에 선택을 할 수 있는 기능을 제공한다.(범위값도 가능)
작성법 리턴 값 타입
CASE
    WHEN 조건1 THEN 결과1
    WHEN 조건2 THEN 결과2
    WHEN 조건3 THEN 결과3
    ELSE 결과N
END
결과

 

파라미터 설명
조건 해당 값이 참인지 거짓인지 여부를 판단한다.
결과 해당 조건과 일치하는 경우 결과를 리턴한다.
DEFAULT 모든 조건이 불일치 시 리턴할 값

 

 

-- CASE
-- WHEN 조건식 THEN 결과값
-- WHEN 조건식 THEN 결과값
-- ELSE 결과값
-- END
SELECT
        EMP_NAME
      , JOB_CODE
      , SALARY
      , CASE
            WHEN JOB_CODE = 'J7' THEN SALARY * 1.1
            WHEN JOB_CODE = 'J6' THEN SALARY * 1.15
            WHEN JOB_CODE = 'J5' THEN SALARY * 1.2
            ELSE SALARY * 1.05
            END AS 인상급여
  FROM EMPLOYEE;

 

 

 

-- 사번, 사원명, 급여를 EMPLOYEE 테이블에서 조회하고
-- 급여가 500만원 초과이면 '고급' 
-- 300~500 만원 사이이면 '중급'
-- 그 이하는 '초급'으로 출력처리하고 별칭은 '구분'으로 한다.
    
SELECT
         EMP_ID
       , EMP_NAME
       , SALARY
       , CASE
            WHEN SALARY > 5000000 THEN '고급'
            WHEN SALARY BETWEEN 3000000 AND 5000000 THEN '중급'
            ELSE '초급'
       END 구분
       FROM EMPLOYEE;

 

 

 

 

 

그룹 함수 - SUM


  • 해당 컬럼 값들의 총 합을 구하는 함수이다.
  • EMPLOYEE 테이블에서 남자 사원의 급여 총 합을 계산하세요. 

 

  • EMPLOYEE 테이블에서 부서코드가 D5인 직원의 보너스 포함 연봉을 계산하세요.

 

 

 

 

 

그룹 함수 – AVG


  • 해당 컬럼 값들의 평균을 구하는 함수이다.
  • EMPLOYEE 테이블에서 전 사원의 보너스 평균을 소수 둘째자리에서 반올림하여 구하세요.

 

  • EMPLOYEE 테이블에서 부서코드가 D5인 직원의 보너스 포함 연봉을 계산하세요.

 

 

-- AVG (숫자가 기록된 컬럼명) : 평균을 구하여 리턴
  SELECT
        AVG(SALARY)
  FROM  EMPLOYEE;

 

 

 

 

 

 

그룹 함수 - COUNT


  • EMPLOYEE 테이블에서 전체 사원의 수를 조회하세요.

 

  • EMPLOYEE 테이블에서 D5 부서에 속해있는 사원의 수를 조회하세요.

 

  • EMPLOYEE 테이블에서 사원들이 속해 있는 부서의 수를 조회하세요.

 

 

 

 

 

 

 

그룹 함수 – MAX/MIN


  • 그룹의 최대값과 최소값을 구하여 리턴하는 함수이다.
  • EMPLOYEE 테이블에서 사원 중 가장 높은 급여와 가장 낮은 급여를 조회하세요.

 

  • EMPLOYEE 테이블에서 가장 오래된 입사일과 가장 최근 입사일을 조회하세요

 

 

 

 

 

 

 

'Programming > ORACLE' 카테고리의 다른 글

서브 쿼리 ( Subquery)  (0) 2022.01.21
JOIN  (0) 2022.01.19
GROUP BY & HAVING  (0) 2022.01.19
DML(SELECT)  (0) 2022.01.17
데이터 베이스 개요  (0) 2022.01.17

+ Recent posts