1. 설정메뉴(옵션) > 기본설정 > 고급(탭) > 메뉴 막대에서 개발자용 메뉴 보기 체크

2. 메뉴 > 개발자용 > 사용자 에이전트 > 해당 User-Agent


 

주요모바일 기기 User-agent

IE8, IE9 User-Agent 변경하기
Opera User-Agent 변경하기
FireFox User-Agent 변경하기
Safari User-Agent 변경하기
Chrome User-Agent 변경하기


출처 : http://stove99.tistory.com/41


Firefox 는 부가기능을 설치하면 된다.

설치할 부가기능 : https://addons.mozilla.org/ko/firefox/addon/user-agent-switcher/

부가기능을 설치하고 나면 메뉴 > 도구 > Default User Agent 메뉴가 생긴다.

거기에서 디폴트로 등록되 있는 User-agent 들을 선택하거나 아니면 Edit User Agent 로 원하는 User-agent 를 추가하면 된다.



주요모바일 기기 User-agent

IE8, IE9 User-Agent 변경하기
Opera User-Agent 변경하기
FireFox User-Agent 변경하기
Safari User-Agent 변경하기
Chrome User-Agent 변경하기

출처 : http://stove99.tistory.com/40

1. 주소창에 opera:config 를 입력하고 이동

2. 목록중에 ISP 라는게 있는데 고부분을 클릭해서 ID 에 원하는 User-agent 스트링을 입력후 저장하고 브라우져를 껏다가 켜면 적용된다.




출처 : http://stove99.tistory.com/38         
         http://stove99.tistory.com/39


1. IE8 User-Agent 변경하기

IE8 은 별도 Add-on 기능을 설치해야 가능하다.

http://ieaddons.com/en/addons/detail.aspx?id=2040

요기 가서 Click to Install 클릭해서 프로그램 설치한다음

익스플로러 껏다 켜면

메뉴 > 도구 > Set UA String 이라고 추가된다~

고걸 클릭하면 팝업창이 하나 뜨는데 목록 중에서 바꾸고 싶은걸 선택하거나

입력하는 란에다 직접 원하는 User-agent 스트링을 입력한 다음 적용하면 된다.~




2. IE9 User-Agent 변경하기

1. F12 를 클릭해 개발자모드로 들어간다.

2. 개발자 모드 메뉴중에 도구 > 사용자 에이젼트 문자열 변경 > 사용자 지정 으로 가서 원하는 User-agent 를 추가한다.

3. 도구 > 사용자 에이젼트 문자열 변경 > 추가시킨 User-agent 를 선택하면 적용된다.

※ 탭별로 적용되는 거라서 새로운 탭을 열면 원래 기본 User-agent 로 다시 변경된다.




주요모바일 기기 User-agent

IE8, IE9 User-Agent 변경하기
Opera User-Agent 변경하기
FireFox User-Agent 변경하기
Safari User-Agent 변경하기
Chrome User-Agent 변경하기

출처 : http://stove99.tistory.com/17

갤럭시S2
Mozilla/5.0 (Linux; U; Android 2.3.3; ko-kr; SHW-M250S Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1



갤럭시 탭
Mozilla/5.0 (Linux; U; Android 2.2.1; ko-kr; SHW-M180S Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1



아이폰 4
Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; ko-kr) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5



아이패드 2
Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; ko-kr) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5


주요모바일 기기 User-agent

IE8, IE9 User-Agent 변경하기
Opera User-Agent 변경하기
FireFox User-Agent 변경하기
Safari User-Agent 변경하기
Chrome User-Agent 변경하기

출처 : http://stove99.tistory.com/18

뭐 물론 보통브라우져에서 m.naver.com 이니 m.nate.com 이니 하는 m 시리즈로 접속을 하면 모바일 페이지로 접속을 할 수 있지만

간혹가다 보면 깐깐하게 사용자 브라우져를 체크해서 모바일 페이지로 이동이 안되는 사이트도 있다. 예를 들어 m.paran.com 같은..~

요럴땐 브라우져의 User-agent 를 모바일 브라우져의 User-agent 로 변경하면 잘 접속할 수 있다.

크롬의 경우 실행시 -user-agent 옵션을 줘서 변경할 수 있다.

윈도우 같은 경우는 간단하게 아이콘에서 오른쪽 버튼을 클릭해 속성창으로 가서 대상란에 적힌 문자열 맨 뒤에 한칸 띄우고

-user-agent "모바일 브라우져 user-agent 스트링" 요렇게 해주면 된다.

예를들어 갤럭시 S2로 접속한 것과 비슷하게 작동할려면 갤럭시 S2의 모바일 브라우져 User-agent String 을 옵션으로 줘서

-user-agent "Mozilla/5.0 (Linux; U; Android 2.3.3; ko-kr; SHW-M250S Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"

요걸 적어주고 적용을 한뒤 브라우져를 껏다 켜면 모바일 브라우져와 비슷하게 된다.



죠렇게 설정하고 다시 m.auction.co.kr 로 접속해 보자~ 모바일 옥션 화면이 정상적으로 로딩될것이다~

주요모바일 기기 User-agent

IE8, IE9 User-Agent 변경하기
Opera User-Agent 변경하기
FireFox User-Agent 변경하기
Safari User-Agent 변경하기
Chrome User-Agent 변경하기

http://www.opensymphony.com/sitemesh/


1. 3.0 버전

Homepage : http://www.sitemesh.org/

Download : http://github.com/sitemesh/sitemesh3/downloads

Document : http://www.sitemesh.org/configuration.html





2. 2.4.2 버전 이하

Download : https://java.net/downloads/sitemesh/

Document : http://pratinas.net/wiki/SiteMesh


/WEB-INF/web.xml

 <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
    </filter>

   <filter-mapping>
  <filter-name>sitemesh</filter-name>
        <url-pattern>*.do</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>ERROR</dispatcher>
 </filter-mapping>



/WEB-INF/sitemesh.xml

 <?xml version="1.0" encoding="UTF-8" ?>
<sitemesh>
    <property name="decorators-file" value="/WEB-INF/config/decorators.xml"/>
    <excludes file="${decorators-file}"/>
 
    <page-parsers>
     <parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.HTMLPageParser" />
    </page-parsers>
   
    <decorator-mappers>
        <mapper class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">
   <!--
   decorator 가 적용되는 순서이다. 에러처리시 유용하다.
   Spring이든 Struts 등 500, 404 jsp 에러페이지에 <meta name="decorator" content="error" />
   를 추가한 후 아래의 decorators.xml에 decorator에 name을 meta content와 같은 error와 지정해주면
   url pattern보다 먼저 적용된다.

   -->
   <param name="property.1" value="meta.decorator" />
   <param name="property.2" value="decorator" />
  </mapper>

  <mapper class="com.opensymphony.module.sitemesh.mapper.FrameSetDecoratorMapper">
  </mapper>

  <mapper class="com.opensymphony.module.sitemesh.mapper.AgentDecoratorMapper">
   <param name="match.MSIE" value="ie" />
   <param name="match.Mozilla [" value="ns" />
   <param name="match.Opera" value="opera" />
   <param name="match.Lynx" value="lynx" />
  </mapper>

  <mapper class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
   <param name="decorator" value="printable" />
   <param name="parameter.name" value="printable" />
   <param name="parameter.value" value="true" />
  </mapper>

  <mapper class="com.opensymphony.module.sitemesh.mapper.RobotDecoratorMapper">
   <param name="decorator" value="robot" />
  </mapper>

  <mapper class="com.opensymphony.module.sitemesh.mapper.ParameterDecoratorMapper">
   <param name="decorator.parameter" value="decorator" />
   <param name="parameter.name" value="confirm" />
   <param name="parameter.value" value="true" />
  </mapper>

  <mapper class="com.opensymphony.module.sitemesh.mapper.FileDecoratorMapper">
  </mapper>

  <mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
   <param name="config" value="${decorators-file}" />
  </mapper>
    </decorator-mappers>
   
</sitemesh>



/WEB-INF/config/decorators.xml

 <?xml version="1.0" encoding="UTF-8" ?>
<decorators>

    <excludes>
        <pattern>*.jsp</pattern>
    </excludes>
 
 
    <decorator name="none">
        <pattern>/common/*</pattern>
    </decorator>
   
    <decorator name="simple" page="/WEB-INF/views/common/decorators/simple.jsp">
        <pattern>/prototype/*Pop.do</pattern>
        <pattern>/prototype/*Simple.do</pattern>
    </decorator>
 
    <decorator name="default" page="/WEB-INF/views/common/decorators/default.jsp">
        <pattern>/prototype/*.do</pattern>
    </decorator>
   
    <decorator name="error" page="/WEB-INF/views/common/decorators/error.jsp" />

</decorators>



/WEB-INF/views/common/decorators/default.jsp
 <?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html version="-//W3C//DTD XHTML 1.1//EN" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/1999/xhtml http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>[:: <decorator:title default="프로젝트" /> ::]</title>
<decorator:head />
</head>
<body>
<h1>Header</h1>
<decorator:body />
<h2>Footer</h2>
</body>
</html>










출처 : http://blog.naver.com/nillwow/7631084


JSP 에러 페이지를 만드는 세가지 방법

JSP에서 에러페이지를 보여주는 방식은 크게 세가지로 나눌수 있습니다.

1. 첫째는 옛날 방식으로 JSP페이지에 직접 에러페이지를 표시해주는 방식입니다.
<%@ page errorPage="error.jsp" %>
이때 error.jsp에 <%@ page isErrorPage="true" %>라고 선언해야합니다.
JSP페이지에 에러가 발생하면 error.jsp로 제어가 넘어가고 error.jsp에서는
"exception"이라는 implicit 객체를 사용하여 에러메시지를 보여줄 수 있습니다.
에러를 우아하게 처리하기 위해서는 JSP페이지마다 errorPage속성을 지정해야하는
불편이 있었던 방식입니다.



2. 둘째는 서블릿2.3에서 추가된 것으로, web.xml에 에러유형별로 에러페이지를
지정해 주는 좀더 진화된 방식입니다.(첨부파일 참조)
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>

이제 JSP에서 예외가 발생하면 해당 JSP페이지에 errorPage속성을 선언 안해도
error.jsp가 자동으로 에러를 처리합니다. 그리고 error-page에서 지정해준 error.jsp에는
<%@ page isErrorPage="true" %> 등의 선언이 불필요합니다. 그러나 이전 방식과는 다르게
예외객체인 "exception"을 직접 접근하진 못합니다.
대신 새로이 추가된 속성값으로 예외객체를 불러올 수 있습니다.
Throwable throwable = (Throwable) request.getAttribute("javax.servlet.error.exception");
에러 관련하여 request 스코프에 저장된 속성의 목록입니다.

(http://network.hanbitbook.co.kr/view_news.htm?serial=78)
javax.servlet.error.status_code: 에러 상태 코드를 말해 주는 정수이다.
javax.servlet.error.exception_type: 에러가 생기게 된 예외 형을 지적해 주는 클래스 인스턴스이다.
javax.servlet.error.message: 예외 메시지를 말해주는 스트링이며, 예외 컨스트럭터로 보내어 진다.
javax.servlet.error.exception: 실제 예외가 없어지면 버릴 수 있는 객체이다.
javax.servlet.error.request_uri: 문제를 일으킨 리소스의 URI를 말해주는 스트링이다.

에러페이지를 JSTL로 작성한다면 위의 속성들은 이렇게 접근할 수 있습니다.
(가르쳐주신 ares님께 감사드립니다)
<c:out value="${requestScope['javax.servlet.error.message']}"/>
저처럼 <c:out value="${javax.servlet.error.message}"/> 로 하지 마시길...



3. 추가로 스트러츠 고유의 에러 처리법도 있습니다.
struts-config.xml에서 Exception의 타입에 따라 에러 페이지를 매핑합니다.
(ExceptionHandler를 새로 구현하셔도 됩니다)
<global-exceptions>
<exception key="error.general"
type="java.lang.Exception"
handler="org.apache.struts.action.ExceptionHandler"
path="/error.jsp"
scope="request"/>
</global-exceptions>

스트러츠를 사용하는 어플리케이션에서 Exception이 발생하면 Exception 객체는
request에 org.apache.struts.Globals.EXCEPTION_KEY 키로 저장되고 제어권은
error.jsp로 넘어갑니다. 스트러츠용 error.jsp의 예입니다.

<% Exception ex = (Exception)request.getAttribute(org.apache.struts.Globals.EXCEPTION_KEY); %>
<% if (ex != null) { %>
<table width=100% border=1 cellpadding=6 cellspacing=0>
<tr align="center" bgcolor="#FFCC66"><td>에러 메시지</td></tr>
<tr align="center"><td><b><%= ex %></b></td></tr>
</table>
<p>
<table width=100% border=1 cellpadding=6 cellspacing=0>
<tr align="center" bgcolor="lightgrey"><td>에러 스택 추적 정보
</td></tr>
<tr align="left"><td>
<pre><% ex.printStackTrace(new java.io.PrintWriter(out)); %></pre>
</td></tr>
</table>
<p>
<% } %>

※ 내용에 오류가 있으면 지적해 주시기 바랍니다.
ares
세번째 파트에 첨가를 하자면
struts 커스텀 태그 중에
<html:errors /> 태그 단 한줄을
이용해도 쉽게 에러메세지를 표현할수 있습니다.
하지만 html:errors 태그로 stack의 정보를 아는 방법은
없는것 같습니다.
2003-01-31 12:00:26.0
박종진
부연하자면 <html:errors/> 태그를 사용하실때는
(1) 에러메시지를 담고있는 메시지 리소스 파일(ex: ApplicationResources.properties)을 작성하셔야하고,
(2) Form 또는 Action에서 에러 발생시에 적절한 ActionError를 만들어주는 작업이 필요합니다.
(3) struts-config.xml의 action요소에서 input 파라미터
세팅하는 것도 빼먹어선 안되겠죠.

참고 : http://docs.oracle.com/cd/E11882_01/server.112/e26088/queries003.htm


1. 샘플 데이터 생성

CREATE TABLE EMPLOYEES
(
  EMPLOYEE_ID     NUMBER(6),
  FIRST_NAME      VARCHAR2(20 BYTE),
  LAST_NAME       VARCHAR2(25 BYTE) CONSTRAINT EMP_LAST_NAME_NN NOT NULL,
  EMAIL           VARCHAR2(25 BYTE) CONSTRAINT EMP_EMAIL_NN NOT NULL,
  PHONE_NUMBER    VARCHAR2(20 BYTE),
  HIRE_DATE       DATE CONSTRAINT EMP_HIRE_DATE_NN NOT NULL,
  JOB_ID          VARCHAR2(10 BYTE) CONSTRAINT EMP_JOB_NN NOT NULL,
  SALARY          NUMBER(8,2),
  COMMISSION_PCT  NUMBER(2,2),
  MANAGER_ID      NUMBER(6),
  DEPARTMENT_ID   NUMBER(4)
);
CREATE INDEX EMP_DEPARTMENT_IX ON EMPLOYEES(DEPARTMENT_ID);
CREATE UNIQUE INDEX EMP_EMAIL_UK ON EMPLOYEES(EMAIL);
CREATE UNIQUE INDEX EMP_EMP_ID_PK ON EMPLOYEES(EMPLOYEE_ID);
CREATE INDEX EMP_JOB_IX ON EMPLOYEES(JOB_ID);
CREATE INDEX EMP_MANAGER_IX ON EMPLOYEES(MANAGER_ID);
CREATE INDEX EMP_NAME_IX ON EMPLOYEES(LAST_NAME, FIRST_NAME);
ALTER TABLE EMPLOYEES ADD (
  CONSTRAINT EMP_SALARY_MIN
  CHECK (salary > 0),
  CONSTRAINT EMP_EMP_ID_PK
  PRIMARY KEY
  (EMPLOYEE_ID)
  USING INDEX EMP_EMP_ID_PK,
  CONSTRAINT EMP_EMAIL_UK
  UNIQUE (EMAIL)
  USING INDEX EMP_EMAIL_UK);
ALTER TABLE EMPLOYEES ADD (
  CONSTRAINT EMP_MANAGER_FK
  FOREIGN KEY (MANAGER_ID)
  REFERENCES EMPLOYEES (EMPLOYEE_ID));
INSERT INTO EMPLOYEES
SELECT *
  FROM HR.EMPLOYEES;
SELECT * FROM EMPLOYEES;



2. 테스트 쿼리

-- SYS_CONNECT_BY_PATH
    SELECT EMPLOYEE_ID
         , LAST_NAME
         , MANAGER_ID
         , LEVEL
         , LTRIM(SYS_CONNECT_BY_PATH(LAST_NAME, '/'), '/') "PATH"
         , CONNECT_BY_ROOT LAST_NAME ROOT_LAST_NAME
         , CONNECT_BY_ISLEAF
      FROM EMPLOYEES
  START WITH EMPLOYEE_ID = 100
CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID
ORDER SIBLINGS BY LAST_NAME

-- START WITH가 없을 경우 LEVEL의 값에 따라 ROW 중복 생성 (LEVEL = 2일 경우 LEVEL 값만 1,2로 다르고 값은 똑같은 ROW가 2개 생김)
    SELECT EMPLOYEE_ID
         , LAST_NAME
         , MANAGER_ID
         , LEVEL
      FROM EMPLOYEES
CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID
ORDER SIBLINGS BY EMPLOYEE_ID

-- DEPARTMENT_ID = 110 의 사원별로 자신의 상관 검색
    SELECT EMPLOYEE_ID
         , LAST_NAME
         , MANAGER_ID
         , SYS_CONNECT_BY_PATH (LAST_NAME, '/') "PATH"
         , CONNECT_BY_ROOT (LAST_NAME) ROOT_LAST_NAME
         , LEVEL LVL
      FROM EMPLOYEES
     WHERE DEPARTMENT_ID = 110
CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID
  ORDER BY EMPLOYEE_ID, MANAGER_ID, LVL DESC, "PATH"

-- 사원부터 자신의 상관 검색
    SELECT EMPLOYEE_ID
         , LAST_NAME
         , MANAGER_ID
         , SYS_CONNECT_BY_PATH (LAST_NAME, '/') "PATH"
         , SYS_CONNECT_BY_PATH (LAST_NAME, CHR(27)) "PATH2"
         , CONNECT_BY_ROOT (LAST_NAME) ROOT_LAST_NAME
         , LEVEL LVL
      FROM EMPLOYEES
START WITH EMPLOYEE_ID = 103
CONNECT BY EMPLOYEE_ID = PRIOR MANAGER_ID
  ORDER BY LVL DESC

-- LEVEL 별로 순번
  SELECT A.*
       , LPAD (' ', 5 * (LVL - 1), ' ') || NVL2 (MANAGER_ID, ROW_NUMBER () OVER (PARTITION BY MANAGER_ID ORDER BY LAST_NAME), NULL) TREE
       , COUNT(*) OVER(PARTITION BY MANAGER_ID) CNT
    FROM (           SELECT EMPLOYEE_ID
                          , LAST_NAME
                          , MANAGER_ID
                          , LEVEL LVL
                          , LTRIM (SYS_CONNECT_BY_PATH (LAST_NAME, '/'), '/') "PATH"
                          , SYS_CONNECT_BY_PATH (LAST_NAME, '/') PATH2
                       FROM EMPLOYEES
                 START WITH EMPLOYEE_ID = 100
                 CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID
          ORDER SIBLINGS BY LAST_NAME) A
ORDER BY ROWNUM


-- LEVEL 한단계 부모의 값 조회
  SELECT A.*
       , DECODE (LVL
               , 1, NULL
               , SUBSTR (PATH2, INSTR (PATH2, '/', 1, LVL - 1)+ 1
                                         , INSTR (PATH2, '/', 1, LVL) - INSTR (PATH2, '/', 1, LVL - 1) - 1)) LVL_PARENT
       , LPAD (' ', 5 * (LVL - 1), ' ') || NVL2 (MANAGER_ID, ROW_NUMBER () OVER (PARTITION BY MANAGER_ID ORDER BY ROWNUM), NULL) TREE
       , NVL2 (MANAGER_ID, ROW_NUMBER () OVER (PARTITION BY MANAGER_ID ORDER BY ROWNUM), NULL) TREE2
       , COUNT (*) OVER (PARTITION BY MANAGER_ID) CNT
    FROM (           SELECT EMPLOYEE_ID
                          , LAST_NAME
                          , MANAGER_ID
                          , LEVEL LVL
                          , LTRIM (SYS_CONNECT_BY_PATH (LAST_NAME, '/'), '/') "PATH"
                          , SYS_CONNECT_BY_PATH (LAST_NAME, '/') PATH2
                       FROM EMPLOYEES
                 START WITH EMPLOYEE_ID = 100
                 CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID
          ORDER SIBLINGS BY LAST_NAME) A
ORDER BY ROWNUM


3. Javascript에서 CHR(27) 구분하기

"메뉴1\u001b서브메뉴1".split(String.fromCharCode(27));




=======================================================================================


Recursive Subquery 사용으로 Connect by 처리

출처 : https://ukja.tistory.com/m/359


Oracle 11gR2부터 Recursive Subquery Factoring이라는 기능을 제공합니다. 이 기능을 이용하면 Connect By 구문을 대신할 수 있죠. 아래에 간단한 사용법이 있습니다.

SQL>  select lpad(' ', 2 * level - 2, ' ') || ename as ename   2        , empno   3        , mgr   4        , level   5     from emp   6  connect by mgr = prior empno   7    start with mgr is null   8  / 
SQL> with emps (ename,empno,mgr,lvl) as   2  ( select ename   3         , empno   4         , mgr   5         , 1   6      from emp   7     where mgr is null   8     union all   9    select emp.ename  10         , emp.empno  11         , emp.mgr  12         , emps.lvl + 1  13      from emp  14           join emps on (emp.mgr = emps.empno)  15  ) search depth first by empno set a  16  select lpad(' ', 2 * lvl - 2, ' ') || ename as ename  17       , empno  18       , mgr  19       , lvl  20    from emps  21    order by a  22  / 

굳이 Connect By로 잘 사용하고 있었던 것을 왜 다시 Recursive Subquery Factoring을 사용해야 하는지를 고민해보면 딱히 떠오르는 것이 없는데요. 다음과 같은 유형의 쿼리에서 유용하게 사용할 수 있을 것 같습니다.

1. 다음과 같은 두 개의 테이블 T_MATERIAL과 T_COMPOSE가 있습니다. 테이블 T_MATERIAL은 "약"(material_type=Med) 또는 "약의 성분"(material_type=Mat) 데이터를 가지고 있습니다. 테이블 T_COMPOSE는 하나의 약이 어떤 하위 성분과 하위 약으로 이루어져 있는지의 관계를 나타냅니다. 하위 약은 다시 하위 약 또는 하위 성분을 가지므로 계층 구조가 됩니다. 그리고 각 하위 약 또는 하위 성분이 몇 %를 구성하고 있는지의 정보(contain_pct)를 가지고 있습니다.

SQL> create table t_material(   2  	material_id		number,   3  	material_name	varchar2(10),   4  	material_type	varchar2(3) -- Med = medicine, Mat = material   5  );  Table created.  SQL>  SQL> create table t_compose (   2  	medicine_id			number,   3  	material_id			number,   4  	contain_pct			number   5  );  Table created. 

이제 다음과 같이 데이터를 생성합니다.

  SQL> begin   2  	insert into t_material values(1, 'medicine1', 'Med');   3  	insert into t_material values(2, 'medicine2', 'Med');   4  	insert into t_material values(3, 'material1', 'Mat');   5  	insert into t_material values(4, 'medicine3', 'Med');   6  	insert into t_material values(5, 'material2', 'Mat');   7  	insert into t_material values(6, 'medicine4', 'Med');   8  end;   9  /  PL/SQL procedure successfully completed.  SQL>  SQL> begin   2  	insert into t_compose values(1, 2, 0.3); -- Med   3  		insert into t_compose values(2, 6, 0.5);   4  			insert into t_compose values(6, 3, 0.8);   5  			insert into t_compose values(6, 5, 0.2);   6  		insert into t_compose values(2, 5, 0.5);   7  	insert into t_compose values(1, 3, 0.3); -- Mat   8  	insert into t_compose values(1, 4, 0.2); -- Med   9  		insert into t_compose values(4, 3, 0.7);  10  		insert into t_compose values(4, 5, 0.3);  11  	insert into t_compose values(1, 5, 0.2); -- Mat  12  end;  13  /  PL/SQL procedure successfully completed. 

1번 약은 (2번 약 30% + 3번 성분 30% + 4번 약 20% + 5번 성분 20%) 으로 이루어져있죠. 2번 약은 (6번 약 50% + 5번 약 50%)로 이루어져 있고, 6번 약은 (3번 성분 80% + 5번 성분 20%)로 이루어져 있습니다. 이런 식으로 계층 구조를 이루고 있습니다.

계층 구조를 지니면서 성분의 함량(contain_pct) 정보가 존재합니다. 여기서 이런 쿼리가 필요해집니다. 1번 약을 구성하는 각 성분의 함량은 어떻게 되는가? 즉, 1번 약을 구성하는 성분인 3번 성분(material1)과 5번 성분(material2)는 각각 몇 %인가?

위와 같은 쿼리가 까다로운 것은 계층 구조를 따라 모든 노드의 값(여기서는 contain_pct)를 알아야하기 때문입니다. 간단하게 계산해보면 3번 성분(material1)의 함량을 구하려면 계층 구조를 따라가면서 0.3*0.5*0.8 + 0.3 + 0.2*0.7 = 0.56 = 56%와 같은 계산이 필요합니다.

Connect By 구문에서는 현재 값과 이전 값(부모 값)만을 알 수 있습니다. 이 한계를 극복하기 위해 나온 것이 SYS_CONNECT_BY_PATH같은 함수죠. 아래와 같이 각 노드의 모든 함량 정보를 얻을 수 있습니다.

SQL> col pholder format a10 SQL> col pct_path format a20 SQL> select   2  	lpad('-',level,'-') as pholder,   3  	medicine_id,   4  	material_id,   5  	contain_pct,   6  	sys_connect_by_path(contain_pct,'/') as pct_path   7  from   8  	t_compose   9  connect by medicine_id = prior material_id  10  start with medicine_id = 1  11  ;  PHOLDER    MEDICINE_ID MATERIAL_ID CONTAIN_PCT PCT_PATH ---------- ----------- ----------- ----------- -------------------- -                    1           2          .3 /.3 --                   2           5          .5 /.3/.5 --                   2           6          .5 /.3/.5 ---                  6           3          .8 /.3/.5/.8 ---                  6           5          .2 /.3/.5/.2 -                    1           3          .3 /.3 -                    1           4          .2 /.2 --                   4           3          .7 /.2/.7 --                   4           5          .3 /.2/.3 -                    1           5          .2 /.2  10 rows selected. 

위의 값을 실제로 계산하려면 다음과 같이 별도의 함수를 이용한 로직이 필요하게 됩니다.

SQL> create or replace function get_total_pct(pct_path in varchar2)   2  return number   3  is   4  	v_idx1		number := 0;   5  	v_idx2		number;   6  	v_temp		number;   7  	v_total		number := 1;   8  begin   9  	v_idx1 := instr(pct_path, '/');  10    11  	loop  12    13  		v_idx2 := instr(pct_path, '/', v_idx1+1);  14  		if v_idx2 = 0 then  15  			v_idx2 := length(pct_path)+1;  16  		end if;  17    18  		v_temp := to_number(substr(pct_path, v_idx1+1, v_idx2-v_idx1-1));  19  		v_total := v_total * v_temp;  20    21  		v_idx1 := v_idx2;  22    23  		exit when v_idx1 > length(pct_path);  24    25  	end loop;  26    27  	return v_total;  28  end;  29  /  Function created. 

CONNECT BY 구문과 SYS_CONNECT_BY_PATH 함수, 그리고 위에서 정의한 함수 GET_TOTAL_PCT를 이용하면 다음과 같이 원하는 값을 얻을 수 있습니다.

SQL> with c as (   2  	select   3  		material_id,   4  		get_total_pct(sys_connect_by_path(contain_pct,'/')) as comp_pct   5  	from   6  		t_compose   7  	connect by medicine_id = prior material_id   8  	start with medicine_id = 1   9  )  10  select  11  	m.material_name,  12  	sum(c.comp_pct) as total_pct  13  from  14  	c,  15  	t_material m  16  where  17  	c.material_id = m.material_id  18  	and m.material_type = 'Mat'  19  group by  20  	m.material_name  21  ;  MATERIAL_N  TOTAL_PCT ---------- ---------- material1         .56 material2         .44 

(음... 더 멋진 방법이 있을 듯... )

Recursive Subquery Factoring에서는 위의 작업을 보다 직관적으로 처리할 수 있습니다. 다음과 같이 부모의 값을 받아서 함량(contain_pct)을 계속 곱해가면 최종 자식 노드의 함량을 알 수 있죠. 그 값을 SUM 하면 함량의 합이 됩니다. 즉, Recursive Subquery Factoring의 장점은 SYS_CONNECT_BY_PATH 같은 함수의 도움을 빌리지 않아도 모든 모드의 값을 이용할 수 있다는 것입니다.

SQL> with recur_mat(comp_pct, material_id)   2  as (   3  	select   4  		contain_pct,   5  		material_id   6  	from   7  		t_compose   8  	where   9  		medicine_id = 1  10  	union all  11  	select  12  		p.comp_pct * c.contain_pct,  -- 부모 * 현재   13  		c.material_id  14  	from  15  		recur_mat p,  16  		t_compose c  17  	where  18  		c.medicine_id = p.material_id  19  )  20  select  21  	m.material_name,  22  	sum(r.comp_pct) as total_pct  23  from  24  	recur_mat r,  25  	t_material m  26  where  27  	r.material_id = m.material_id  28  	and m.material_type = 'Mat'  29  group by  30  	m.material_name  31  ;  MATERIAL_N  TOTAL_PCT ---------- ---------- material1         .56 material2         .44 

아... 설명이 좀 구질구질했는데요. Recursive Subquery Factoring을 이용함으로써 좀 더 작업이 간편해지는 몇 안되는 예제 중 하나라서 올려봅니다.

앞으로 11gR2가 본격적으로 사용되면 활용 예제가 더 많이 소개될 것으로 기대합니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


참고 : http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_9016.htm


테이블에서 어느 조건을 만족하는 데이터(행)가 있다면 Update 없으면 Insert 하는 구문을 한번에 해결 할 수 있다.

Merge Into 사용전 방법 : 
    레코드 존재유무 조회(Select) -> 있음 -> Update
                                             -> 없음 -> Delete

샘플.
 MERGE INTO bonuses D
   USING (SELECT employee_id, salary, department_id FROM employees
   WHERE department_id = 80) S
   ON (D.employee_id = S.employee_id)
   WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary*.01
     DELETE WHERE (S.salary > 8000)
   WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus)
     VALUES (S.employee_id, S.salary*.01)
     WHERE (S.salary <= 8000);

MERGE INTO 테이블(뷰) : 대상 테이블(뷰). Update, Delete, Insert 를 하게 될 테이블(뷰)
USING 테이블(뷰/서브쿼리) : 비교 테이블(뷰/서브쿼리).
ON : 비교 조건
WHEN MATCHED THEN UPDATE [DELETE] : 비교조건에 만족하는 레코드가 있을 경우 해당 레코드에 대해 실행
     10G부터는 DELETE도 포함되었으며 UPDATE문 없이 단독으로 사용하지 못한다.
WHEN NOT MATCHED THEN INSERT : 비교조건에 만족하지 않았을 경우 실행

+ Recent posts