<procedure id="tmSaveProj" parameterClass="map">

DECLARE

V_CNT NUMBER;

BEGIN

SELECT COUNT(*)

 INTO V_CNT

 FROM BUS_AGRMT_MST

WHERE PROJ_PLAN_NO = #PROJ_PLAN_NO#

  AND PROJ_PLAN_SEQ = #PROJ_PLAN_SEQ#

  AND PART_SHAPE = #CHRG_PART_ORG_SHAPE#

  AND PART_ORG_NO = #CHRG_PART_ORG_NO#;


IF V_CNT = 0 THEN

INSERT INTO BUS_AGRMT_MST (

PROJ_PLAN_NO,

PROJ_PLAN_SEQ,

PART_SHAPE,

PART_ORG_NO,

RCPT_BK_CD,

RCPT_ACC_NO,

INS_ID,

INS_DT,

UPT_ID,

UPT_DT

)VALUES(

#PROJ_PLAN_NO#,

#PROJ_PLAN_SEQ#,

#CHRG_PART_ORG_SHAPE#,

#CHRG_PART_ORG_NO#,

#IN_BK_CD#,

#IN_BK_ACC_NO#,

#SESS_USER_ID#,

SYSDATE,

#SESS_USER_ID#,

SYSDATE

);

ELSE

UPDATE BUS_AGRMT_MST

  SET RCPT_BK_CD = #IN_BK_CD#,

RCPT_ACC_NO = #IN_BK_ACC_NO#,

UPT_ID = #SESS_USER_ID#,

UPT_DT = SYSDATE

WHERE PROJ_PLAN_NO = #PROJ_PLAN_NO#

  AND PROJ_PLAN_SEQ = #PROJ_PLAN_SEQ#

  AND PART_SHAPE = #CHRG_PART_ORG_SHAPE#

  AND PART_ORG_NO = #CHRG_PART_ORG_NO#;

END IF;

END;

</procedure>





// V_CNT 가 0 이라면 값이 없음으로. INSERT 를 하며 1 이상이면 값이 기존에 있음으로 UPDATE 를 실행 

'오라클' 카테고리의 다른 글

TOAD 단축키  (0) 2012.10.31
오라클 과거 데이터를 보는 방법. [SYSTIMESTAMP]  (0) 2012.10.30
오라클 힌트 모음 (예제)  (0) 2012.08.31
BYPASS_UJVC -> MERGE 문으로 변경 하자.  (0) 2012.08.31
오라클 procedure 정리  (0) 2012.08.30
Posted by 사라링

JSTL 기초

2012. 10. 8. 17:51

JSTL 기초, Part 1: Expression Language (한글)

JSP 애플리케이션용 MA 단순화하기

Mark A. Kolb, 소프트웨어 엔지니어

요약:  JSP Standard Tag Library (JSTL)은 일반적인 웹 애플리케이션 기능(반복(iteration)과 조건, 데이터 관리 포맷, XML 조작, 데이터베이스 액세스)을 구현하는 커스텀 태그 라이브러리 모음이다. 소프트웨어 엔지니어인 Mark Kolb은 JSTL 태그의 사용방법을 설명한다. 표현층(presentation layer)에서 소스 코드를 제거하여 소프트웨어 관리를 단순화시키는 방법도 설명한다. 이외에도 JSTL의 단순화된 Expression Language에 대한 설명도 포함되어 있다.

원문 게재일:  2003 년 11 월 18 일 (출판일: 2003 년 2 월 11 일) 
난이도:  초급 
페이지뷰:  24253 회

JavaServer Pages (JSP)는 J2EE 플랫폼을 위한 표준 표현 레이어(presentation-layer) 이다. JSP는 페이지 콘텐트를 동적으로 생성할 수 있는 전산을 수행 할 수 있는 스크립팅 엘리먼트와 액션을 제공한다. 스크립팅 엘리먼트는 프로그램 소스 코드가 JSP 코드에 포함될 수 있도록 한다. 페이지가 사용자 요청에 대한 응답으로 렌더링 될 때 실행할 목적이다. 액션(actions)은 전산 작동을 JSP 페이지의 템플릿 텍스트를 구성하고 있는 HTML 이나 XML과 거의 흡사하게하는 태그로 인캡슐한다. JSP 스팩에 표준으로 정의된 몇 가지의 액션들이 있다. 하지만 JSP 1.1 부터 개발자들은 커스텀 태그 라이브러리 형태로 자신만의 액션들을 만들 수 있다.

JSP Standard Tag Library (JSTL)는 JSP 1.2 커스텀 태그 라이브러리 모음으로서 광범위한 서버측 자바 애플리케이션에 일반적으로 쓰이는 기본 기능들을 구현한다. JSTL은 데이터 포맷, 반복 콘텐트 또는 조건 콘텐트 같은 전형적인 표현 레이어를 위한 표준 구현을 제공하기 때문에, JSP 작성자들이 애플리케이션 개발에 집중하는데 도움이 된다.

물론, 스크립틀릿, 익스프레션, 선언 같은 JSP 스크립팅 엘리먼트를 사용하는 태스크를 구현할 수 있다. 예를 들어 조건 콘텐트(conditional content)는 세 개의 스크립틀릿(Listing 1의 하이라이트 부분)을 사용하여 구현될 수 있다. 페이지 내에 프로그램 소스 코드를 임베딩하는 것에 의존하기 때문에 스크립팅 엘리먼트가 소프트웨어 관리 태스크를 매우 복잡하게 하는 경향이있더라도 JSP 페이지는 그들을 사용한다. Listing 1의 스크립틀릿 예제는 브레이스들의 적절한 매칭에 매우 의존한다. 조건화된 콘텐트 내에 추가 스크립틀릿을 중첩하는 것은 신택스 에러가 갑자기 일어났다면 페이지가 JSP 콘테이너에 의해 컴파일 될 때 결과 에러 메시지를 합리화하는 것은 도전이 될 수 있다.


Listing 1. 스크립틀릿을 통해 조건 콘텐트 구현하기
  <% if (user.getRole() == "member")) { %>
    <p>Welcome, member!</p>
<% } else { %>
    <p>Welcome, guest!</p>
<% } %>

그와 같은 프로그램을 해결하는데에는 프로그래밍 경험이 많이 필요하다. JSP 페이지의 마크업이 페이지 레이아웃과 그래픽 디자인에 익숙한 디자이너에 의해 개발 및 관리되는데 반해 그와 같은 페이지 내의 스크립팅 엘리먼트는 문제가 생길 때 프로그래머가 개입해야한다. 하나의 파일안에 있는 코드에 대한 책임을 공유하는 것은 JSP 페이지의 개발, 디버깅, 향상을 성가신일로 만든다. JSTL은 일반적인 기능을 커스텀 태그 라이브러리의 표준 세트로 패키징했기 때문에 JSP 작성자들이 스크립팅 엘리먼트에 대한 필요를 줄이고 관련된 관리 비용을 피할 수 있도록 한다.

이 기사의 다른 시리즈

Part 2, "core 분석" (2003년 3월) 
Part 3, "보여지는 것도 중요하다!" (2007년 3월) 
Part 4, "SQL과 XML 콘텐트에 액세스 하기" (2007년 4월)

JSTL 1.0

02년 6월에 릴리스된 JSTL 1.0은 네 개의 커스텀 태그 라이브러리(coreformatxmlsql)와 두 개의 범용 태그 라이브러리 밸리데이터(ScriptFreeTLV & PermittedTaglibsTLV)로 구성되어 있다. core 태그 라이브러리는 커스텀 액션을 제공하여 범위 변수를 통해 데이터를 관리할 수 있도록하며 페이지 콘텐트의 반복과 조건화를 수행할 수 있도록 한다. 또한 URL에서 생성 및 작동할 수 있는 태그도 제공한다. format 태그 라이브러리는 이름이 시사하는 바와 같이 데이터 특히 숫자와 날짜를 포맷하는 액션을 정의한다. 국지화 된 리소스 번들을 사용하여 JSP 페이지의 국제화도 지원한다. xml 라이브러리에는 XML을 통해 표현된 데이터를 조작할 수 있는 테그가 포함되어 있다. sql 라이브러리는 관계형 데이터베이스를 쿼리하는 액션을 정의한다.

두 개의 JSTL 태그 라이브러리 밸리데이터는 개발자들이 JSP 애플리케이션 내에서 표준을 코딩하도록 한다. ScriptFreeTLV 밸리데이터를 설정하여 JSP 페이지 내에 있는 다양한 스크립팅 엘리먼트(스크립틀릿, 표현, 선언)의 다양한 유형을 사용하는 것을 막는다. 이와 비슷하게 PermittedTaglibsTLV 밸리데이터는 애플리케이션의 JSP 페이지들에 의해 액세스된 커스텀 태그 라이브러리(JSTL 태그 라이브러리 포함)을 제한한다.

JSTL은 J2EE 플랫폼에 필요한 컴포넌트가 될 것이지만 적은 수의 애플리케이션 서버들만이 이를 포함하고 있는 것이 현실이다. JSTL 1.0의 레퍼런스 구현은 Apache Software Foundation의 Jakarta Taglibs 프로젝트의 일부로서 사용할 수 있다. (참고자료). 레퍼런스 구현에 있는 커스텀 태그 라이브러리는 JSTL 지원을 추가하기 위해 JSP 1.2와 Servlet 2.3 이상 스팩을 지원하는 모든 애플리케이션 서버에 통합될 수 있다.

Expression language

JSP 1.2에서 JSP 액션의 애트리뷰트는 정적 캐릭터 스트링이나 익스프레션을 사용하여 지정된다. 예를 들어 Listing 2의 경우 정적 값들은 <jsp:setProperty> 액션의 name과 property 애트리뷰트를 위해 지정된다. 반면 익스프레션은 이것의 값 애트리뷰트를 지정하는데 사용된다. 이 액션은 요청 매개변수의 현재 값을 이름이 붙여진 빈 속성으로 할당하는 효과를 갖고 있다. 이러한 방식으로 사용된 익스프레션은 request-time attribute values이라 일컬어지며 애트리뷰트 값을 동적으로 지정하기위한 JSP 스팩에 내장된 유일한 메커니즘이다.


Listing 2. request-time attribute value을 결합하는 JSP 액션
<jsp:setProperty name="user" property="timezonePref"
          value='<%= request.getParameter("timezone") %>'/>


request-time attribute values가 익스프레션을 사용하여 지정되기 때문에 다른 스크립팅 엘리먼트와 같은 소프트웨어 관리 문제가 일어날 수 있다. 이런 이유로 인해 JSTL 커스텀 태그는 동적 애트리뷰트 값을 지정하기 위한 대안 메커니즘을 지원한다. JSP 익스프레션을 사용하는 것 보다 JSTL 액션용 애트리뷰트 값이 단순화 된 expression language (EL)를 사용하여 지정될 수 있다. EL은 JSP 컨테이너에 있는 데이터를 검색 및 조작할 식별자, 접근자, 연산자를 제공한다. EL은 EcmaScript(참고자료)와 XML Path Language (XPath)에 약간 의존하기 때문에 신택스는 페이지 디자이너와 프로그래머 모두 에게 익숙하다. EL은 객체와 속성들을 검색하면서 간단한 작동을 수행한다. 이것은 프로그래밍 언어도 스크립팅 언어도 아니다. JSTL 태그와 결합하면 간단하고 편리한 표기를 사용하여 복잡한 작동이 표현될 수 있다. EL 익스프레션은 달러 표시($)와 중괄호 ({})를 앞에 붙여 사용하여 범위를 정한다.(Listing 3)


Listing 3. JSTL 액션: EL 익스프레션 범위 지정
<c:out value="${user.firstName}"/>


여러개의 익스프레션들과 정적 텍스트를 결합하여 스트링 연결을 통해 동적 애트리뷰트 값을 만들 수 있다.(Listing 4). 개별 익스프레션들은 식별자, 접근자, 리터럴, 연산자로 구성되어 있다. 식별자는 데이터 센터에 저장된 데이터 객체를 참조하는데 사용된다. EL은 11 개의 식별자를 보유하고 있다. 11 개의 EL 내장 객체에 상응하는 것들이다. 다른 모든 식별자들은 범위 변수를 참조하는 것으로 간주된다. 접근자는 객체의 속성 또는 컬렉션의 엘리먼트를 검색하는데 사용된다. 리터럴은 고정된 값들(숫자, 문자, 스트링, 부울, null)을 나타낸다. 연산자는 데이터와 리터럴이 결합 및 비교될 수 있도록 한다.


Listing 4. 정적 텍스트와 여러 EL 익스프레션을 결합하여 동적 애트리뷰트 값 지정하기
<c:out value="Hello ${user.firstName} ${user.lastName}"/>


범위 변수(Scoped variables)

<jsp:useBean> 액션을 통한 JSP 에이피아이는 데이터가 저장될 수 있도록 하며 JSP 컨테이너 내에 네 개의 다른 범위에서 데이터가 검색될 수 있도록 한다. JSTL은 이러한 범위 내에 객체를 할당하고 제거할 추가 액션을 제공한다. 더욱이, EL은 범위 변수 같은 객체들을 검색하는 빌트인 지원을 제공한다. 특히 EL의 내장 객체 중 하나라도 상응하지 않는 EL 익스프레션에 있는 식별자는 네 개의 JSP 스콥 중 하나에 저장된 객체를 참조하는 것으로 자동 간주된다:

  • 페이지 범위
  • 요청 범위
  • 세션 범위
  • 애플리케이션 범위

페이지 범위에 저장된 객체들은 특정 요청에 대한 페이지가 프로세스 되는 동안 검색될 수 있다. 요청 범위 내에 저장된 객체들은 요청 프로세스에 참여한 모든 페이지들이 프로세스 하는 동안 검색될 수 있다. 객체가 세션 범위에 저장되어있다면 웹 애플리케이션과의 단일 인터랙트브 세션 동안 사용자가 액세스 한 페이지로 검색될 수 있다. 웹 애플리케이션이 언로드(unload) 될 때 까지 애플리케이션 범위에 저장된 객체는 모든 페이지에서 접근가능하며 모든 사용자들이 접근할 수 있다.

캐릭터 스트링을 희망하는 범위에 있는 객체로 매핑하여 범위안에 객체를 저장할 수 있다. 이러한 경우에는 같은 캐릭터 스트링을 제공하여 범위에서 객체를 검색할 수도 있다. 스트링은 범위 매핑 중 검색되고 매핑된 객체는 리턴된다. Servlet API 내에서 그와 같은 객체들은 상응하는 범위의 애트리뷰트로서 언급된다. EL의 경우 애트리뷰트와 관련된 캐릭터 스트링은 변수 이름으로 간주될 수도 있다.

EL에서 내장 객체들과 관련이 없는 식별자들은 JSP 범위에 저장된 객체들을 명명하는 것으로 간주된다. 그와 같은 식별자는 페이지 범위를 검사하고 그 다음에는 요청 범위, 세션 범위, 애플리케이션 범위 순으로 검사한다. 식별자의 이름이 그 범위에 저장된 객체 이름과 매칭되는지의 여부가 테스트된다. 첫 번째 매치는 EL 식별자의 값으로 리턴된다. EL 식별자는 범위 변수를 참조하는 것으로 간주될 수 있다.

기술적인 관점에서 보면 내장 객체로 매핑하지 않는 식별자는 PageContext 인스턴스의 findAttribute() 메소드를 사용하여 평가되면서 현재 핸들되는 요청에 대해 익스프레션이 발생하는 페이지의 프로세싱을 나타낸다. 식별자의 이름은 이 메소드에 대한 인자로서 전달된다. 이것은 같은 이름을 가진 애트리뷰트에 대한 네 개의 범위를 검색한다. 발견된 첫 번째 매치는 findAttribute() 메소드 값으로 리턴된다. 그와 같은 애트리뷰트가 네 개의 범위 중에 없으면 null이 리턴된다.

궁극적으로 범위 변수는 네 개의 EL 식별자로서 사용될 수 있는 이름을 가진 JSP 범위의 에트리뷰트라고 할 수 있다. 영숫자 이름으로 할당되는 한 범위 변수는 JSP 에 존재하는 모든 메커니즘으로 만들어져 애트리뷰트를 설정할 수 있다. 여기에는 빌트인<jsp:useBean> 액션은 물론 setAttribute() 메소드가 포함된다. 게다가 네 개의 JSTL 라이브러리에서 정의된 많은 커스텀 태그들은 스스로 범위 변수로서 애트리뷰트 값을 설정할 수 있다.

내장 객체(Implicit objects)

11 개의 EL 내장 객체용 식별자는 표 1과 같다. JSP 내장 객체와 혼동하지 말것!

표 1. EL 내장 객체

Category식별자설명
JSPpageContext현재 페이지의 프로세싱과 상응하는 PageContext 인스턴스
범위pageScope페이지 범위 애트리뷰트 이름과 값과 관련된 Map
requestScope요청 범위 애트리뷰트 이름과 값과 관련된 Map
sessionScope세션 범위 애트리뷰트 이름과 값과 관련된 Map
applicationScope애플리케이션 범위 애트리뷰트 이름과 값과 관련된 Map
요청 매개변수param요청 매개변수의 기본 값을 이름으로 저장하는 Map
paramValues요청 매개변수의 모든 값을 String 어레이로서 저장하는 Map
요청 헤더header요청 헤더의 기본 값을 이름으로 저장하는 Map
headerValues요청 헤더의 모든 값을 String 어레이로서 저장하는 Map
쿠키cookie요청에 수반되는 쿠키들을 이름으로 저장하는 Map
초기화 매개변수initParam웹 애플리케이션의 콘텍스트 초기화 매개변수를 이릉으로 저장하는 Map

JSP와 EL 내장 객체가 일반적인 하나의 객체를 갖는 반면(pageContext) 다른 JSP 내장 객체는 EL에서 접근 가능하다. 페이지콘텍스트가 다른 8 개의 JSP 내장 객체 모두에 액세스 할 수 있는 속성을 갖고 있기 때문이다.

남아있는 모든 EL 내장 객체들은 맵(map)이다. 이름에 상응하는 객체들을 탐색한다. 첫 번째 네 개의 맵은 이전에 언급한 다양한 애트리뷰트 범위를 나타낸다. 특정 범위 내의 식별자들을 검색하는데 사용될 수 있다. EL이 기본적으로 사용하는 순차적인 탐색 프로세스에 의존하지 않는다.

다음 네 개의 맵은 요청 매개변수와 헤더의 값을 반입하는 용도이다. HPPT 프로토콜이 요청 매개변수와 헤더가 다중 값을 가질 수 있도록 하기 때문에 각각 한 쌍의 맵이 있다. 각 쌍 중에서 첫 번째 맵은 요청 매개변수 또는 헤더에 대한 기본 값을 리턴한다. 실제 요청 시 첫 번째로 지정된 값이 무엇이든 상관없다. 두 번째 맵은 매개변수나 헤더의 값 모두 검색될 수 있도록 한다. 이 맵의 핵심은 매개변수 또는 헤더의 이름이다. 값들은 String 객체의 어레이이다.

쿠키 내장 객체는 요청으로 설정된 쿠키에 대한 접근을 제공한다. 이 객체는 요청과 관련된 모든 쿠키들의 이름을 Cookie 객체들로 매핑하면서 쿠키들의 속성을 나타낸다.

마지막 EL 내장 객체인 initParam은 웹 애플리케이션과 관련된 모든 콘텍스트 초기와 매개변수의 이름과 값을 저장하는 맵이다. 초기화 매개변수들은애플리케이션의 WEB-INF 디렉토리에 있는 web.xml 전개 디스크립터 파일을 통해 정의된다.

접근자(Accessors)

EL 식별자는 내장 객체 또는 범위 변수로서 설명될 수 있기 때문에 자바 객체로 평가해야한다. EL은 상응하는 자바 클래스에서 프리머티브를 래핑/언래핑한다. 하지만 대부분의 경우 식별자들은 자바 객체에 대한 포인터가 된다.

결과적으로 이러한 객체들의 속성이나, 어레이와 컬렉션의 경우 그들의 엘리먼트에 액세스하는 것이 바람직하다. 이를 위해 EL은 두 개의 다른 접근자를 제공한다. 닷(dot) 오퍼레이터(.)와 브래킷 오퍼레이터([])이다. 이들은 속성과 엘리먼트들이 EL을 통해 연산될 수 있도록 한다.

닷 연산자는 객체의 프로퍼티에 접근하는데 사용된다. ${user.firstName} 익스프레션에서 닷 연산자는 user 식별자에 의해 참조된 객체 중 firstName이라는 이름을 가진 속성에 액세스 한다. EL은 자바 빈 규정을 사용하여 객체 속성에 접근하기 때문에 이 속성에 대한 게터(일반적으로 getFirstName())는 이 익스프레션이 정확히 계산하기 위해서 반드시 정의되어야 한다. 액세스되는 속성이 객체일 때 닷 연산자는 재귀적으로 적용될 수 있다. 예를 들어 가상의 user 객체가 자바 객체로서 구현된 address 속성을 갖고 있다면 닷 연산자는 이 객체의 속성에 액세스 하기 위해 사용될 수도 있다. ${user.address.city} 익스프레션은 이 address 객체 중 중첩된 city 속성을 리턴한다.

브래킷 연산자는 어레이와 컬렉션의 엘리먼트를 검색하는데 사용된다. 어레이와 컬렉션(java.util.List를 구현하는 컬렉션)의 경우 검색될 엘리먼트 인덱스는 브래킷 안에 나타난다. 예를 들어 ${urls[3]} 익스프레션은 이 urls 식별자에 의해 참조된 어레이 또는 컬렉션의 네 번째 엘리먼트를 리턴한다.

java.util.Map 인터페이스를 구현하는 컬렉션의 경우 브래킷 연산자는 관련 키를 사용하여 맵에 저장된 값을 찾는다. 이 키는 브래킷 내에서 지정되고 상응하는 값은 익스프레션 값으로 리턴된다. 예를 들어 ${commands["dir"]} 익스프레션은 commands 식별자에 의해 참조된 Map의 "dir" 키와 관련된 값을 리턴한다.

익스프레션이 브래킷안에 나타날 수 있다. 중첩된 익스프레션의 계산 결과는 컬렉션이나 어레이의 적절한 엘리먼트를 검색하는 인덱스 또는 키로 작용한다. 닷 연산자가 true라면, 브래킷 연산자도 재귀적으로 적용될 수 있다. 이는 EL이 다차원 어레이, 중첩 컬렉션, 또는 둘의 결합에서 엘리먼트를 검색 할 수 있도록 한다. 더욱이 닷 연산자와 브래킷 연산자는 상호운용성이 있다. 예를들어 한 어레이의 엘리먼트가 객체라면 브래킷 연산자는 그 어레이의 엘리먼트를 검색하는데 사용될 수 있고 닷 연산자와 결합하여 엘리먼트 속성 중 하나를 검색할 수 있다. (예를 들어 ${urls[3].protocol}).

EL이 동적 애트리뷰트 값을 정의하는 간한한 언어로서 작용한다고 볼 때, 자바 접근자와는 다른 EL 접근자의 재미있는 특성 중 하나는null에 적용될 때 예외를 던지지 않는다는 점이다. EL 접근자가 적용되는 객체(예를 들어 ${foo.bar}와 ${foo["bar"]}의 foo 식별자)가 null이면 접근자 적용 결과 역시 null이다. 이는 대부분의 경우, 도움이 되는 일이다.

마지막으로 닷 연산자와 브래킷 연산자는 상호 교환될 수 있다. 예를 들어 ${user["firstName"]}은 user 객체의 firstName 속성을 검색하는데 사용될 수 있다. ${commands.dir}가 commands 맵에서 "dir" 키와 관련된 값을 반입하는데 사용될 수 있는것과 같은 이치이다.

연산자(Operators)

식별자와 접근자를 사용하여 EL은 애플리케이션 데이터(범위 변수를 통해 노출) 또는 환경 관련 정보(EL 내장 객체를 통해 노출)를 포함하고 있는 객체 계층을 트래버스 할 수 있다. 그와 같은 데이터에 간단히 접근하는 것은 많은 JSP 애플리케이션에 필요한 표현 로직을 구현하는데 종종 부적합하다.

EL에는 EL 익스프레션으로 접근된 데이터를 조작 및 비교할 여러 연산자를 포함하고 있다. 이러한 연산자들을 표 2에 요약했다.

표 2. EL 연산자

Category연산자
산술+-*/ (or div), % (or mod)
관계형== (or eq), != (or ne), < (or lt), > (or gt), <= (or le), >= (or ge)
논리&& (or and), || (or or), ! (or not)
타당성검사empty

산술 연산자는 더하기, 빼기, 나누기를 지원한다. 다른 연산자들도 제공된다. 나누기와 나머지 연산자들은 비 상징 이름들이라는 대안을 갖고 있다. 산술 연산자의 사용법을 설명하는 예제 익스프레션은 Listing 5에 설명되어 있다. 산술 연산자를 한 쌍의 EL 익스프레션에 적용한 결과는 그러한 익스프레션에 의해 리턴된 숫자 값에 대한 연산자에 적용한 결과이다.


Listing 5. 산술 연산자를 사용하는 EL 익스프레션
${item.price * (1 + taxRate[user.address.zipcode])}

관계형 연산자는 숫자 또는 텍스트 데이터를 비교할 수 있도록 한다. 비교 결과는 부울 값으로서 리턴된다. 논리적 연산자는 부울 값이 결합될 수 있도록 하며 새로운 부울 값을 리턴한다. EL 논리적 연산자는 중첩된 관계형 연산자 또는 논리적 연산자의 결과에 적용될 수 있다. (Listing 6).


Listing 6. 관계형 연산자 및 논리적 연산자를 사용하는 EL 익스프레션
${(x >= min) && (x <= max)}

EL 연산자는 empty 이다. 데이터의 타당성 검사에 특히 유용하다. empty 연산자는 하나의 익스프레션을 인자로 취한다.(${empty input}). 그리고 익스프레션이 empty 값으로 계산했는지의 여부를 나타내는 부울 값을 리턴한다. null로 계산한 익스프레션은 empty로 간주된다. 어떤 엘리먼트도 없는 컬렉션이나 어레이와 같다. empty 연산자는 인자가 길이가 0인 String으로 계산했다면 true로 리턴한다.

EL 연산자의 우선순위는 표 3에 정리되어 있다. Listing 5와 6에 제안된 것 처럼 괄호는 그룹 익스프레션에 사용되고 일반적인 우선순위를 따른다.

표 3. EL 연산자 우선순위 (위->아래, 왼쪽->오른쪽)

[].
()
unary -not!empty
*/div%mod
+, binary -
() <><=>=ltgtlege
==!=eqne
&&and
||or

리터럴(Literals)

숫자, 캐릭터 스트링, 부울, null은 EL 익스프레션에서 리터럴 값으로 지정될 수 있다. 캐릭터 스트링은 싱글 쿼트 또는 더블 쿼트로 범위가 지정된다. 부울 값은 true와 false로 계산된다.

Taglib 지시문

앞서 언급했지만 JSTL 1.0에는 네 개의 커스텀 태그 라이브러리가 포함되어 있다. 익스프레션 언어로 JSTL 태그의 인터랙션을 설명하기 위해 JSTL core 라이브러리에서 여러 태그들을 검토할 것이다. 모든 JSP 커스텀 태그 라이브러리로 true가 된다면 taglib 지시문은 이 라이브러리 태그를 사용할 수 있는 페이지에 포함되어야한다. 이 특정 라이브러리에 대한 지시문은 Listing 7에 나타나있다.


Listing 7. JSTL core 라이브러리의 EL 버전용 테그립 지시문
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

실제로 JSTL core 라이브러리에 상응하는 두 개의 Taglib 지시문이 있다. JSTL텐에서 EL은 옵션이기 때문이다. JSTL 1.0 의 네 개의 커스텀 태그 라이브러리들은 동적 애트리뷰트 값을 지정할 때 EL 대신 JSP 익스프레션을 사용하는 대안 버전을 갖고있다. 이러한 대안 라이브러리는 JSP의 전통적인 요청시간 애트리뷰트 값에 의존하기 때문에 RT 라이브러리로 일컬어진다. 반면 익스프레션 언어를 사용하는 것은 EL 라이브러리라고 한다. 개발자들은 대안 Taglib 지시문을 사용하는 각각의 라이브러리의 버전들을 구별한다. RT 버전의 코어 라이브러리를 사용하기 위한 지시문은 Listing 8에 나와있다. 하지만 지금은 EL에 집중해야 하기 때문에 지금 필요한 것은 이 지시문들 중 첫 번째 것이다.


Listing 8. RT 버전의 JSTL core 라이브러리용 태그립 지시문
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c_rt" %>

변수 태그

첫 번째 JSTL 커스텀 태그는 <c:set> 액션이다. 이미 언급했듯이 범위 변수는 JSTL에서 핵심적인 역할을 하고 <c:set> 액션은 태그 기반의 매커니즘을 제공하여 범위 변수의 생성 및 설정에 쓰인다. 이 액션의 신택스는 Listing 9와 같다. var 애트리뷰트는 범위 변수 이름을 정하고 scope 애트리뷰트는 변수가 머물게 될 범위를 나타내고, value 애트리뷰트는 변수가 될 값을 지정한다. 지정된 변수가 이미 존재하면 지시된 값으로 할당된다. 그렇지 않다면 새로운 범위 변수가 만들어지고 그 값으로 초기화된다.


Listing 9. <c:set> 액션 신택스
<c:set var="name" scope="scope" value="expression"/>


scope 애트리뷰트는 선택적이며 page로 기본 설정되어 있다.

<c:set>의 두 예제는 Lisitng 10에 설명되어 있다. 첫 번째 예제에서 세션 범위 변수는 String 값으로 설정된다. 두 번째에서는 익스프레션은 숫자 값을 설정하는데 사용된다. square라는 페이지 범위 변수는 x 라는 요청 매개변수 값을 배가시킨 결과로 할당된다.


Listing 10. <c:set> 액션 예제
<c:set var="timezone" scope="session" value="CST"/>
<c:set var="square" value="${param['x'] * param['x']}"/>



애트리뷰트를 사용하는 대신 범위 변수용 값을 <c:set> 액션의 바디 콘텐트로 설정할 수 있다. 이러한 접근방식을 사용하여 Listing 10의 첫 번째 예제를 Listing 11과 같이 재작성할 수 있다. 더욱이 <c:set> 태그의 바디 콘텐트가 커스텀 태그를 적용하는 것도 가능하다.<c:set>의 바디 안에서 만들어진 모든 콘텐트는 String 값 같이 지정된 변수에 할당된다..


Listing 11. 바디 콘텐트를 통해 <c:set> 액션용 값 지정하기
<c:set var="timezone" scope="session">CST</c:set>

JSTL core 라이브러리에는 범위 변수를 관리하는 두 번째 태그(<c:remove>)가 포함되어 있다. 이름에서 시사되는 바와 같이<c:remove> 액션은 범위 변수를 지우는데 사용되고 두 개의 애트리뷰트를 취한다. var 애트리뷰트는 제거될 변수를 명명하고 선택적인 scope 애트리뷰트는 제거되어야 할 범위를 나타낸다. (Listing 12).


Listing 12. <c:remove> 액션 예제
<c:remove var="timezone" scope="session"/>

아웃풋

<c:set> 액션은 익스프레션의 결과가 범위 변수로 할당될 수 있도록 하는 반면 개발자들은 익스프레션 값을 저장하는 대신 간단히 디스플레이하기를 원한다. 이는 JSTL의 <c:out> 커스텀 태그의 몫이다. (Listing 13). 이 태그는 value 애트리뷰트에서 지정된 익스프레션을 계산한다. 그런다음 결과를 프린트한다. 선택적 default 애트리뷰트가 지정되면 value 애트리뷰트의 익스프레션이 null 또는 비어있는 String으로 계산될 때 <c:out> 액션은 값을 프린트한다.


Listing 13. <c:out> 액션 신택스
<c:out value="expression" default="expression" escapeXml="boolean"/>

escapeXml 애트리뷰트 또한 선택사항이다. "<", ">", "&" 같은 캐릭터가 <c:out> 태그에 의해 아웃풋 될 때 종료되는지의 여부를 제어한다. escapeXml이 true로 설정되어 있다면 이 캐릭터들은 상응하는 XML 인터티(<>&)로 바뀐다.

예를 들어, user라는 세션 범위 변수가 있다고 가정해보자. 이것은 사용자에 대한 username과 company라는 두 개의 속성들을 정의하는 클래스의 인스턴스이다. 이 객체는 사용자가 사이트에 접근할 때마다 세션에 할당된다. 하지만 이 두 개의 속성들은 사용자가 실제로 로그인하기 전까지 설정되지 않는다. (Listing 14). 일단 사용자가 로그인하면 "Hello"가 디스플레이 되고 뒤따라서 사용자 이름과 감탄부호가 나온다. 사용자가 로그인하기 전에 여기에서 생긴 콘텐트는 "Hello Guest!" 라는 구(phrase)가 된다. 이 경우 username 속성이 초기화되지 않았기 때문에 <c:out> 태그는 default 애트리뷰트 값을 프린트한다.


Listing 14. <c:out> 액션 예제 (디폴트 콘텐트)
Hello <c:out value="${user.username}" default=="Guest"/>!


<c:out> 태그의 escapeXml 애트리뷰트를 사용하는 Listing 15를 보자. company 속성이 자바 String 값인 "Flynn & Sons"으로 설정되었다면 이 액션에서 생긴 콘텐트는 Flynn & Sons이 된다. 이 액션이 HTML 또는 XML 콘텐트를 만드는 JSP 페이지의 일부라면 이 캐릭터의 스트링 중간에 있는 앰퍼샌트 부호는 HTML 또는 XML이 문자를 제어하고 이 콘텐트의 렌더링 또는 파싱을 방해하는것으로 해석하고 끝난다. escapeXml 애트리뷰트의 값이 true로 설정되면 생성된 콘텐트는 Flynn & Sons이 된다. 이 콘텐트를 만나는 브라우저 또는 파서는 인터프리테이션에 아무 문제가 없다. HTML과 XML이 JSP 애플리케이션에서 가장 일반적인 콘텐트 유형이라면 escapeXml애트리뷰트의 디폴트 값이 true라는 것은 놀라운 일이 아니다.


Listing 15. <c:out> 액션 예제)
<c:out value="${user.company}" escapeXml=="false"/>


디폴트 값으로 변수 설정하기

동적 데이터를 단순하게 하는 것 외에도 디폴트 값을 지정하는 <c:out>의 기능은 <c:set>을 통해 변수 값을 설정할 때에도 유용하다. 범위 변수에 할당된 값이 <c:set> 태그의 바디 콘텐트로 지정될수 있고 value 애트리뷰트로서도 가능하다. <c:out> 액션을 <c:set> 태그의 바디 콘텐트에 중첩하여 변수 할당은 이것의 디폴트 값을 이용할 수 있다. (Listing 11).

이러한 접근 방식은 Listing 16에도 설명되어 있다. 외부 <c:set> 태그의 작동은 단순하다.


Listing 16. <c:set>과 <c:out> 결합: 디폴트 변수 값 제공
<c:set var="timezone" scope=="session">
   <c:out value="${cookie['tzPref'].value}" default=="CST"/>
</c:set>

요청에 제공된 tzPref 라는 이름의 쿠키가 없다. 내장 객체를 사용한 검색은 null이 된다는 것을 의미한다. 익스프레션은 전체적으로 null을 리턴한다. value 애트리뷰트를 계산한 값이 null 이기 때문에 <c:out> 태그는 default 애트리뷰트를 계산한 결과를 아웃풋한다.


참고자료

필자소개

Mark Kolb는 소프트웨어 엔지니어이며 Web Development with JavaServer Pages, 2nd Edition의 공동저자이다

Posted by 사라링

//팝업에서 오브젝트를 부모창으로 보내기 위한 작업 FUNCTION

<script type="javascript" ev:event="ondblclick">

<![CDATA[cardInfo();]]>

</script>



function cardInfo() {

if(!fGridReturn("fg1")) return;

var obj = doc.addGrid("fg1").getRowObj(fg1.row);

try{

opener.javascript.purCardAprovPopUp(obj);

}catch(e){

if(opener.javascript.comInfo(obj) == false){

return false;

}

}

window.close();

}




//데이터 그리드의 컬럼 타입이 체크박스 인경우 컬럼명에 체크박스를 넣어 버튼 클릭시 모두 클릭 되도록 함. 

fg1.fixedcellcheckbox(0,1)=true;


//문자타입의 데이터 값 수식 . 

 := parseInt(model.getValue("/root/main/detail/TRF_AMT"),10)

    -- 10진법의 숫자로 변환 . 


// input 타입의 입력 창에 포커스(마우스 클릭) 했을때 한영을 지정된 것으로 변경 하는 옵션

 imemode="hangul"

-- 한글 

 imemode="alpha"

-- 영문 


// datagrid 의 col 값이 전체 width 값만큼 조정 되는 것을 막음 

     fg1.extendLastCol = "false"


// input 박스 두개  기간 체크

<script type="javascript" ev:event="xforms-value-changed">

<![CDATA[

fDayPeriodCheck("/root/search/APROV_DT_FR","/root/search/APROV_DT_TO", "str" );

doFg1Search();

model.refresh();

]]>

</script>

<script type="javascript" ev:event="onkeydown">

<![CDATA[

fEnterFunction("doFg1Search",event.target);

]]>

</script>

// date 유효성 검사 하나 

fDayCheck("/root/main/PUBCT_DT","input10");

//grid 의 date 유효성 검사  

<col ref="FRM_DT" type="inputdate">

<script type="javascript" ev:event="xforms-value-changed">

<![CDATA[

fDayCheck(fg3.row,"fg3.FRM_DT");

]]>

</script>

</col>


//grid 에서 emp_pop 셋팅 function 사용 

<script type="javascript" ev:event="onbuttonclick">

<![CDATA[

doFg23Insert("O");

]]>

</script>

<script type="javascript" ev:event="onkeyup">

<![CDATA[

 if(event.keyCode == 13){

doFg23Insert("O");

 }

]]>

</script>


function doFg23Insert(emp_OA){

var owner_nm ="";

if(emp_OA=="O"){

owner_nm =  fg3.valueMatrix(fg3.row, fg3.colRef("OWNER_NM"));

}else {

owner_nm =  fg2.valueMatrix(fg2.row, fg2.colRef("CARD_ADMIN_NM"));

}

model.makeValue("/root/temp/EMP_NM",owner_nm);

if(owner_nm!=""){

popupValueSet("/root/temp/EMP_NM", "EMP_NM", "E");

popupValueSet("/root/temp/EMP_NO", "EMP_NO", "");

}else {

popupValueSet("/root/temp/EMP_NM", "EMP_NM", "");

popupValueSet("/root/temp/EMP_NO", "EMP_NO", "");

}

      //popupValueSet("/root/temp/DEPT_NM", "DEPT_NM", "");

      empPopup("/root/temp/EMP_NM");

      if(emp_OA!="O"){

      if (doFg2Check())return; // 기존에 추가된 관리자 인지 확인

      }

     

      if(emp_OA=="O"){

      fg3.valueMatrix(fg3.row, fg3.colRef("OWNER_NM")) = model.getValue("/root/temp/EMP_NM");

    fg3.valueMatrix(fg3.row, fg3.colRef("OWNER")) = model.getValue("/root/temp/EMP_NO");

fg3.colWidth(0)=30;

      }else {

      fg2.valueMatrix(fg2.row, fg2.colRef("CARD_ADMIN_NM")) = model.getValue("/root/temp/EMP_NM");

    fg2.valueMatrix(fg2.row, fg2.colRef("CARD_ADMIN")) = model.getValue("/root/temp/EMP_NO");

fg2.colWidth(0)=30;

      }

   

model.refresh();

}







//input 박스 에서 emp_pop 셋팅

<script type="javascript" ev:event="onbuttonclick">

<![CDATA[

popupValueSet("/root/search/OWNER_NM","EMP_NM","");

popupValueSet("/root/search/OWNER_NO","EMP_NO","");

empPopup();

]]>

</script>

<script type="javascript" ev:event="xforms-value-changed">

<![CDATA[

popupValueSet("/root/search/OWNER_NM","EMP_NM","E");

popupValueSet("/root/search/OWNER_NO","EMP_NO","");

empPopup("");

]]>

</script>

<script type="javascript" ev:event="onkeypress">

<![CDATA[

fEnterKey("");

]]>

</script>



//데이터 그리드 더블클릭후 팝업 데이터 넘기기

<script type="javascript" ev:event="ondblclick">

<![CDATA[

if(!fGridReturn("fg1")) return;

model.makeValue("/root/temp/ACCT_CD", fg1.valueMatrix(fg1.row, fg1.colRef("ACCT_CD")));

model.makeValue("/root/temp/ACCT_NM", fg1.valueMatrix(fg1.row, fg1.colRef("DIS_NM")));

model.makeValue("/root/temp/FRM_DT", model.getValue("/root/search/FRM_DT"));

model.makeValue("/root/temp/TO_DT", model.getValue("/root/search/TO_DT"));

loadPopUp("/etr/xrw/mis/act/main/act_4003_007.xrw", "", "1010", "590", "/root/temp", "/root/temp"

]]>

</script>



// 더블클릭후 받은 데이터 처리

function doInit(){

doImportSearch("CORP","");

model.makeValue("/root/search/ACCT_NM", model.getValue("/root/temp/ACCT_NM"));

model.makeValue("/root/search/ACCT_CD", model.getValue("/root/temp/ACCT_CD"));

model.makeValue("/root/search/FRM_DT", model.getValue("/root/temp/FRM_DT")); 

     model.makeValue("/root/search/TO_DT", model.getValue("/root/temp/TO_DT"));


input3.disabled=false;

model.refresh();

model.setFocus("input7");

}



// 최초 실행시 사용 할수 있도록 위치 </model> 바로 위

<script type="javascript" ev:event="xforms-model-construct-done">

<![CDATA[

doInit();

]]>

</script>


// 데이터 그리드를 초기화

     fg1.rebuild();


//데이터그리드 스크롤 되지 않게 고정

fg1.FrozenCols = 3;       // 좌측 3개의 칼럼을 고정시킨다.

 fg1.FrozenRows = 2;    // 상단 2개의 행을 고정시킨다.



// 클릭시 특정 REF 값을 기준으로 모두 check 또는 check false 하는 것
var statVal;
if(fg1.valueMatrix(fg1.row, fg1.colRef("isChecked"))=="true") statVal="false";
else if(fg1.valueMatrix(fg1.row, fg1.colRef("isChecked"))==""||fg1.valueMatrix(fg1.row, fg1.colRef("isChecked"))=="false") statVal="true";
for(var i=fg1.fixedRows; i<fg1.rows; i++) if(fg1.valueMatrix(i, fg1.colRef("SLIP_NO"))==fg1.valueMatrix(fg1.row, fg1.colRef("SLIP_NO"))) fg1.valueMatrix(i,                                 
fg1.colRef("isChecked")) = statVal;

// 클릭시 같은 row 를 check 또는 check false 하는 것 
if(!fGridReturn("fg1")) return;
if(fg1.valueMatrix(fg1.row, fg1.colRef("isChecked"))=="true"){
fg1.valueMatrix(fg1.row, fg1.colRef("isChecked"))="false";
}else{
fg1.valueMatrix(fg1.row, fg1.colRef("isChecked"))="true";
}


// 데이터 있는지 확인.

if(fg1.rows==1){

   jsAlert("그리드 안에 데이터가 없습니다."); 

return;

  }


//xrw 오픈시 특정 값을 받는 쿼리 .jsp 의 request.getParameter 와 같다. 

model.makeValue("/root/main/search/CARD_CLS", model.property("CARD_CLS"));



// 특정 데이터 그리드의 row 를 추가한다. 

   doc.addGrid("fg2").insertRow("last");



// 입력창을 FOCUS 하게함

    model.setFocus("input1");



//데이터 그리드에 값 셋팅 하기 

    doc.addGrid("fg1").setAction("/module/mis/act/main/Act_7001_007.do").setMode("getList").setSendRef("/root/search").getList("true");

                    대상그리드               네임스페이스                                                   XML ID                     MAP 에 가져갈 검색 조건 ## 


// 가져온 데이터 그리드의 여러개의 ROW 중 조건에 맞는 경우에만 ROW 에 백그라운드를 다르게 하여 표현 하게함 

    for(var i=fg1.fixedRows; i<fg1.rows; i++) if(fg1.valueMatrix(i, fg1.colRef("OUT_CHK"))=="Y") fg1.rowStyle(i, "data", "background-color") = "#ffddff";


//대상 데이터 그리드의 데이터 ROW 숫자를 캡션 안에 넣어 준다. 

   getGridRowCnt("fg1", "caption37");



// 선택된 ROW의 특정 컬럼 값을 임시 저장 하여 sedRef 에 추가 하기 

model.makeValue("/root/search/search2/CARD_NO", fg1.valueMatrix(fg1.row, fg1.colRef("CARD_NO"))); doc.addGrid("fg2").setAction("/module/mis/act/main/Act_7001_007.do").setMode("getFg2List").setSendRef("/root/search/search2/CARD_NO").getList("true");


//모든 컬럼 값을 변경 하기 ( check  속성의 값을 true 로 바꿈 )

for (var i=1; i<fg2.rows; i++){ fg2.valueMatrix(i, fg2.colRef("isChecked"))="true"; }


//submit 전에 값의 유효성 검사를 함 체크 펑션을 사용 하는경우 반드시 조건문 안에 사용 하여 값을 받아 사용 한다. 

function doSaveCheck(){

if(!chkValue(model.getValue("/root/main/CARD_NO"), "input1", "카드번호를 입력해 주십시요.")) return false;

}


// submit 할때 특정 값이 아닌 데이터 그리드의 값을 모두 보낼때 사용 //대상 데이터 그리드의 isChecked 값을 찾아서 플래그 한후에 getUpdateData() 를 이용 하여 ref 값을 생성후

    subMit 시에 ref 값으로 보내 준다. JAVA 단에서 받는다. 

        -- XRW

fSetGridAllFlag("fg2");

model.makeValue("/root/temp/updateData3", fg2.getUpdateData());

** getUpdateData() 는 특정 그리드의 데이터 값중 insert update delete 된 값을 지정 하여 보내 getGridUpdatedList, getGridInsertdList,getGridDeleteList 등으로 구분 하여 받을수 있다.  따라서 이것을 모두 가져와 사용 하기위해서는 모두 대상에 적용 시켜야할 필요가 있는데  fSetGridAllFlag("fg2"); 

을 사용 하여 getGridUpdatedList 로 받을수 있다. 

        --JAVA 

       List adminList = request.getGridUpdatedList("updateData3");

for(Iterator all = adminList.iterator(); all.hasNext();){

Map row = (Map)all.next();

row.put("SESS_USER_ID", map.get("SESS_USER_ID"));

row.put("CARD_NO", map.get("CARD_NO"));

createObject("act.main.act7001007.insertAdminList", row);

}


// 위의 값을 submit 하자 submit 의 경우 String 으로 결과 값을 response.addReturnValue("Y"); 돌려줘 밑과 같이 처리 한다. 

if(submit("/module/mis/act/main/Act_7001_007.do", "tmSave", "/root/main,/root/temp/updateData3","","","", false)=="Y"){

if(!fMessageBox("I_Y","doFg1Search")) return;

}else{

if(!fMessageBox("I_N","doFg1Search")) return;

}

         }


//인사의 팝업 사용 하기 


popupValueSet("/root/main/ADMIN_NM", "EMP_NM", "");

popupValueSet("/root/main/ADMIN", "EMP_NO", "");

      empPopup();

     

      if (doFg2Check())return; // 기존에 추가된 관리자 인지 확인

     

       fg2.valueMatrix(fg2.row, fg2.colRef("EMP_NM")) = model.getValue("/root/main/ADMIN_NM");

       fg2.valueMatrix(fg2.row, fg2.colRef("EMP_NO")) = model.getValue("/root/main/ADMIN");


function doFg2Check(){

         for (var i=fg2.fixedRows; i<fg2.rows; i++){

if(fg2.valueMatrix(i, fg2.colRef("EMP_NO"))==  model.getValue("/root/main/ADMIN")   ){

if(model.getValue("/root/main/ADMIN")!=""){

jsAlert("이미 등록된 관리자 입니다.");

}

return true;

}

}

}


//선택 ROW 를 삭제 한다. 

         function doDelete(){

if(jsConfirm("삭제하시겠습니까?")){

if(submit("/module/mis/act/main/Act_7001_007.do", "tmDelete", "/root/main/CARD_NO","","","",false)=="Y"){

if(!fMessageBox("D_Y","doFg1Search")) return;

}else{

if(!fMessageBox("D_N","doFg1Search")) return;

}

model.refresh();

}

}


// 추가 자체 FUNCTION 

function popAcc(obj){// 값을 받아 올때만 실행

if(model.getValue("/root/temp/ACC_FLAG")=="Y"){

// 일괄적용 (다중 적용)

for(var i=fg1.fixedRows; i<fg1.rows; i++) if(fg1.valueMatrix(i, fg1.colRef("isChecked"))=="true") fg1.rowStyle(i, "data", "background-color") = "#ffddff";

for(var i=fg1.fixedRows; i<fg1.rows; i++) if(fg1.valueMatrix(i, fg1.colRef("isChecked"))=="true"){

fg1.valueMatrix(i, fg1.colRef("STTL_BK_CD"))=obj.BK_CD;

fg1.valueMatrix(i, fg1.colRef("STTL_BK_NM"))=obj.BK_NM;

fg1.valueMatrix(i, fg1.colRef("STTL_ACC_NO"))= obj.BK_ACC_NO;

}

model.refresh();

}else {

// 단일적용

                  fg1.rowStyle(fg1.row, "data", "background-color") = "#ffddff";

fg1.valueMatrix(fg1.row, fg1.colRef("STTL_BK_CD")) =obj.BK_CD;

fg1.valueMatrix(fg1.row, fg1.colRef("STTL_BK_NM")) = obj.BK_NM;

fg1.valueMatrix(fg1.row, fg1.colRef("STTL_ACC_NO")) = obj.BK_ACC_NO;

fg1.valueMatrix(fg1.row, fg1.colRef("isChecked"))="true";

}

model.setValue("/root/temp/ACC_FLAG","N");

}




// 특정 값을 가져와 숫자로 바꾸어 값을 확인 한다. 

//결제일 맥스값 체크

function doSttlDtChange(){

var sttlDt=parseInt(model.getValue("/root/main/STTL_DT"));

if(sttlDt>31){

jsAlert("유효하지 않는 결제일입니다.");

model.setFocus("input3");

}

}


// ROW 를 삭제 하기 

    function doFg2Delete(){

if(fg2.row<fg2.fixedRows){

jsAlert("삭제할 내역을 선택해 주십시요.");

return;

}else if(jsConfirm("선택한 행을 삭제하시겠습니까?")){

fg2.deleteRow(fg2.row);

fg2.colWidth(0)=30;

     }

     }


//SELECT  형식으로 데이터 그리드를 보는것 (사업처 코드를 사용 -보이는 값과 넘기는 값이 다르다 ) 


//데이터 그리드 안에서 

<col ref="BUSI_PLC_CD" type="combo" visibility="hidden">

<choices>

<itemset nodeset="/root/temp/IMPORT/CORP">

<label ref="label"/>

<value ref="value"/>

</itemset>

</choices>

</col>


// 단일 SELECT 안에서 사용 

<select1 id="combo2" ref="/root/search/BUSI_PLC_CD" appearance="minimal" style="left:125px; top:38px; width:210px; height:17px; font-family:Gulim; text-align:center; padding-top:1; ">

<choices>

<itemset nodeset="/root/temp/IMPORT/CORP">

<label ref="label"/>

<value ref="value"/>

</itemset>

</choices>

<script type="javascript" ev:event="xforms-value-changed">

<![CDATA[

doFg1Search();

]]>

</script>

</select1>


위의 두 값은 아래와 같이 초기 셋팅 한다. 

<script type="javascript" ev:event="xforms-model-construct-done">

<![CDATA[

//doImportSearch("CORP,C320,C321,C315@ETC1='320-001'","--전체--,,,--선택--");

doImportSearch("CORP_INF","--전체--");

    //doInit();

]]>

</script>


// 특정 그리드의 checked 된 값을 숫자로 return 하는 function 사용  // act.js 에 구현

fGridChkCntReturn("fg1","isChecked","true")==0

   -- 특정 데이터그리드의 refid 값과 몇개 인제 확인할 상태값을(true or false)  넘겨 준다. 


// 데이터 별로 특정 값을 찾아 style 을 조정 하여 백그라운드를 다르게 보여 준다. 2개 이상의 값

for(var i = fg1.fixedRows ; i < fg1.rows ; i++){

var gbn = fg1.valueMatrix(i, fg1.colRef("GBN"));

if(gbn=="2"||gbn=="6"||gbn=="8"||gbn=="9"){

fg1.mergebyfree(i, fg1.colRef("CARD_NO"), i, fg1.colRef("APROV_DATE"));

fg1.cellStyle("text-align", i, fg1.colRef("CARD_NO"), i, fg1.colRef("APROV_DATE"))="center";

fg1.rowStyle(i, "data", "background-color") =  "#e1e1ff";

fg1.rowStyle(i, "data", "color") =  "#0000ff";

fg1.rowStyle(i, "data", "font-weight") =  "bold";


}else if(fg1.valueMatrix(i, fg1.colRef("GBN"))=="10"){

fg1.mergebyfree(i, fg1.colRef("CARD_NO"), i, fg1.colRef("APROV_DATE"));

fg1.cellStyle("text-align", i, fg1.colRef("CARD_NO"), i, fg1.colRef("APROV_DATE"))="center";

fg1.rowStyle(i, "data", "background-color") =  "#e1e1ff";

fg1.rowStyle(i, "data", "color") =  "#ff0000";

fg1.rowStyle(i, "data", "font-weight") =  "bold";

}

}


//공통단 엑셀 처리 xrw 부분 


T폼 지원 : saveExcel(document.title, document.title, "fg1", "1");


//따로 만든것 

    var excelFileName = "acctCd.xls";

     var fgColName= "소득코드^소득구분^표준코드^순번^소득명^필요경비세율(%)^세율";

     //var fgColName2= "소득코드^소득구분^표준코드^순번^소득명^필요경비세율(%)^세율"; //타이틀 값 두줄 넣을때 사용 fgColName 과 사이즈가 같아야함

     var fgColRef=     "INCM_CD^INCM_CLS^STAN_INCM_CD^STAN_INCM_SEQ^INCM_NM^NECES_TAX_RATE^TAX_RATE";


     model.makeValue("/root/temp/excel/colName",fgColName);

     //model.makeValue("/root/temp/excel/colName2",fgColName2);

     model.makeValue("/root/temp/excel/colRef",fgColRef);

     model.makeValue("/root/temp/excel/getList","act.main.act0009007.getList");

     model.makeValue("/root/temp/excel/title", document.title);

     model.makeValue("/root/temp/excel/fileNm",excelFileName);

     submit("/module/mis/act/main/Act_0000_007.do", "excelDown", "/root/temp/excel,/root/search", "", "excel", "", false, true);

     


// 엑셀 다운 작업 2

model.makeValue("/root/search/FINC_STAT_CD","390-001");

   

var excelFileName = "acct_Balance_sheet.xls";

var fgColName= "계정과목코^"+model.getValue("/root/main/TITLE3")+"^"+

model.getValue("/root/main/TITLE3")+"^"+model.getValue("/root/main/TITLE4")+"^"+model.getValue("/root/main/TITLE4");

     

var fgColName2= "^^금액^금액^금액^금액"; //타이틀 값 두줄 넣을때 사용 fgColName 과 사이즈가 같아야함

     var fgColRef=     "DIS_NM^SEQ^THIS_L_AMT^THIS_R_AMT^BACK_L_AMT^BACK_R_AMT";

     model.makeValue("/root/temp/excel/colName",fgColName);

     model.makeValue("/root/temp/excel/colName2",fgColName2);

     model.makeValue("/root/temp/excel/busiPlcNm",model.getValue("/root/main/TITLE1")+"     "+model.getValue("/root/main/TITLE2"));

     model.makeValue("/root/temp/excel/colRef",fgColRef);

     model.makeValue("/root/temp/excel/getList","act.main.act5002007.getList");

     model.makeValue("/root/temp/excel/title", document.title);

     model.makeValue("/root/temp/excel/fileNm",excelFileName);

     submit("/module/mis/act/main/Act_0000_007.do", "excelDown", "/root/temp/excel,/root/search", "", "excel", "", false, true);

     


Posted by 사라링

// 비어 있는 fg2 의 값을 삭제 하여 submit

for (var i=fg2.rows; i>=fg2.fixedRows; i--){

if(fg2.valueMatrix(i, fg2.colRef("EMP_NO"))== "" ){

fg2.deleteRow(i);

}

}


// row 를 삭제 하는 경우 위에서 부터 하면 전체 row 값이 바뀌기 때문에 문제가 발생 한다. 

Posted by 사라링

// 부모창에 값 넘겨 주기 

window.opener.document.getElementById("sqlInput").value=result;

// 팝업창 닫기 
window.open("about:blank","_self").close();

   // self.closed() ; 는 일부 브라우져 에서 작동을 안한다. 

Posted by 사라링

1) replaceAll 메소드 이용
public boolean isNumeric(String s) { 
    return s.replaceAll("[+-]?\\d+", "").equals("") ? true : false; 

 

2) Pattern 클래스 이용
public boolean isNumeric(String s) { 
    java.util.regex.Pattern pattern = Pattern.compile("[+-]?\\d+"); 
    return pattern.matcher(s).matches(); 




isNumeric(String.valueOf(strMap.get(col[1][i]))); 

이런식으로 데이터를 가져 오자. . 



public boolean checkDate(String str){

boolean dateValidity = true;

SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss",Locale.KOREA); //20041102101244

df.setLenient(false); // false 로 설정해야 엄밀한 해석을 함.

try {

Date dt = df.parse(str);

}

catch(ParseException pe){

dateValidity = false;

}catch(IllegalArgumentException ae){

dateValidity = false;

}

return dateValidity;

}

Posted by 사라링

컬럼 추가 삭제

2012. 9. 10. 18:49

CREATE TABLE

col_name DataType DataSize NOT NULL PK
  Constraint FOREIGN KEY
REFERENCES ( )
||




<style>



#bodyBG {

 background: #8c92ac;

 background: -moz-linear-gradient(bottom, #8c92ac, #fff) no-repeat;

 background: -o-linear-gradient(bottom, rgb(140,146,172), rgb(255,255,255)) no-repeat;

 background: -webkit-gradient(linear, center bottom, center top, from(#8c92ac), to(#fff)) no-repeat;

}

</style>



<script type="text/javascript">

$(function(){

var strCr = "";

var strRef = "";

var strRefCol = "";

$("#helperOK").live("click",function(){

var result="";

var primarySQL="";

var sqlInfo=$("#dialog-createTable tr");

//$("#dialog-createTable tr").each()

result +="CREATE TABLE "+sqlInfo.eq(0).find("#table_name").val()+"\n(\n";

for(var i=2;i<(sqlInfo.size()-3);i++){

if(sqlInfo.eq(i).find(".col_Name").val()!=""){

result +=sqlInfo.eq(i).find(".col_Name").val()+" ";

result +=sqlInfo.eq(i).find("#col_Type option:selected").text();

result +=" ("+sqlInfo.eq(i).find(".col_Size").val()+") ";

/* alert(sqlInfo.eq(i).find("#col_check").attr('checked'));

return; */

if(sqlInfo.eq(i).find("#col_check").attr('checked')){

result += " NOT NULL ,\n";

}else {

result += " ,\n";

}

if(sqlInfo.eq(i).find(".col_Primary").attr('checked')){

primarySQL ="PRIMARY KEY ("+sqlInfo.eq(i).find(".col_Name").val()+")";

}

}else {

alert("모두 채우셔야 합니다.");

return;

}

}

result +=primarySQL;

if($(".create_constraint").val()!="####_PK"){

result +=",\n CONSTRAINT "+$(".create_constraint").val();

result +=" "+$("#create_foreign option:selected").text();

result +=" REFERENCES "+$("#create_reference option:selected").text()+" ("+$("#create_reference_table option:selected").text()+")";

}else{

result +="\n"

}

result+=")";

// 이런식으로 저장 해보자. 부모장에 돌려 주기. 

window.opener.document.getElementById("sqlInput").value=result;


window.open("about:blank","_self").close();

});

$("#helperCancel").live("click",function(){

// self.closed();   <-- 일부 브라우저 에서 작동 안한다 . crom x

                        alert("닫기는 브라우저 를 전체로 닫기 때문에 막아 놓았습니다. ");

                        return;

window.open("about:blank","_self").close();

});

$("#create_foreign").live("click", function() {

var str="";

var sqlInfo=$("#dialog-createTable tr");

for(var i=2;i<(sqlInfo.size()-4);i++){

str +="<option>"+sqlInfo.eq(i).find(".col_Name").val()+"</option>";

}

if(str!=strCr){

strCr=str;

$("#create_foreign").html(str);

}

});


$("#btncolAdd").live("click",function(){

var innerTR_Col="";

innerTR_Col+="<tr>                                                                      ";

innerTR_Col+=" <td align='center'><input type='text' size='15'                       ";

innerTR_Col+=" class='col_Name' title='컬럽 이름' /></td>                           ";

innerTR_Col+=" <td align='center'><select title='데이터 타입' id='col_Type'>              ";

innerTR_Col+=" <option>VARCHAR2</option>                                     ";

innerTR_Col+=" <option>NUMBER</option>                                       ";

innerTR_Col+=" <option>CHAR</option>                                         ";

innerTR_Col+=" <option>DATE</option>                                     ";

innerTR_Col+=" <option>TIMESTAMP</option>                                    ";

innerTR_Col+=" <option>DEC</option>                                          ";

innerTR_Col+=" <option>BOLB</option>                                         ";

innerTR_Col+=" <option></option>                                             ";

innerTR_Col+=" </select></td>                                                        ";

innerTR_Col+=" <td align='center'><input type='text' size='3'                       ";

innerTR_Col+=" class='col_Size' value='10' title='데이터 사이즈' maxlength='3' />     ";

innerTR_Col+=" </td>                                                                 ";

innerTR_Col+=" <TD align='center'><input type='checkbox' id='col_check'            ";

innerTR_Col+=" title='NULL 허용 여부'  /></TD>                      ";

innerTR_Col+=" <td align='center'><input name='primary' type='radio'                 ";

innerTR_Col+=" class='col_Primary' title='기본키'/></td>         ";

innerTR_Col+="</tr>                                                                     ";

$("#btncolAddSpan").append(innerTR_Col);

});


$("#create_reference").live("click", function() {

//

$.ajax({

"url" : "tableData.do",

"type" : "post",

"dataType" : "json",

"data" : {

"dataType" : "init"

},

"success" : function(data) {

var str ="";

$.each(

data,

function(i, v) {

str +="<option>"+v.TableName+"</option>";

});

if (str != strRef) {

strRef = str;

$("#create_reference").html(str);

}


// 테이블 가져 왔는지 확인

//alert(tableListArray.toString());

},

"error" : function(info, xhr) {

if (info.readyState == '4') {

alert('문제가 발생했습니다.\n상태코드 : ' + info.status + '\n\n'

+ info.responseText);

} else {

alert('문제가 발생했습니다.\n잠시후 다시 시도해 주세요.\n 상태코드 : '

+ info.status);

}

}

});

});

$("#btncolMinus").live("click",function(){

var sqlInfo=$("#dialog-createTable tr");

var maxSize=sqlInfo.size();

if(maxSize<=6){

return;

}

$("#dialog-createTable tr").eq(maxSize-4).remove();

});

$("#create_reference_table")

.live(

"click",

function() {

var x = document

.getElementById("create_reference");

//alert(x.options[x.selectedIndex].value);

var ref_selected = x.options[x.selectedIndex].value;

var str = "";

$

.ajax({

"url" : "tableData.do",

"type" : "post",

"dataType" : "json",

"data" : {

"dataType" : "Reference_col",

"selectedItem" : ref_selected

},

"success" : function(data) {

//alert("가져온 데이터 입니다 . " + data);

$.each(data, function(i, v) {

str += "<option>"

+ v.column

+ "</option>";

});

if (str != strRefCol) {

strRefCol = str;

$("#create_reference_table")

.html(str);

}


},

"error" : function(info, xhr) {

if (info.readyState == '4') {

alert('문제가 발생했습니다.\n상태코드 : '

+ info.status

+ '\n\n'

+ info.responseText);

} else {

alert('문제가 발생했습니다.\n잠시후 다시 시도해 주세요.\n 상태코드 : '

+ info.status);

}

}

});


});

});

</script>

<div id="bodyBG">

<div id="dialog-createTable" style="width: 500px; height: 370px; ">

<form name="createForm">

<table style="width: 500px; height: 250px; overflow: auto;">

<tbody><tr>

<td colspan="3">

<h3>

CREATE TABLE <input id="table_name" name="table_name" type="text" size="15" class="create_Name" title="테이블 이름">

</h3>

</td>

<td>

</td>

<td colspan="2">

<input id="btncolAdd" type="button" value="+" title="컬럼추가">

<input id="btncolMinus" type="button" value="-" title="컬럼 삭제">

</td>

</tr>

<tr>

<td width="162" align="center">col_name</td>

<td width="153" align="center">DataType</td>

<td width="69" align="center">DataSize</td>

<td width="72" align="center">NOT NULL</td>

<td width="69" align="center">PK</td>

</tr>

<tr>

<td align="center"><input type="text" size="15" class="col_Name" title="컬럽 이름"></td>

<td align="center"><select title="데이터 타입" id="col_Type">

<option>NUMBER</option>

<option>VARCHAR2</option>

<option>CHAR</option>

<option>DATE</option>

<option>TIMESTAMP</option>

<option>DEC</option>

<option>BOLB</option>

<option></option>

</select></td>

<td align="center"><input type="text" size="3" class="col_Size" value="10" title="데이터 사이즈" maxlength="3">

</td>

<td align="center"><input type="checkbox" id="col_check" title="NULL 허용 여부" checked="checked"></td>

<td align="center"><input name="primary" type="radio" class="col_Primary" title="기본키" checked="checked"></td>

</tr>

</tbody><tbody id="btncolAddSpan" style="overflow: auto;">

</tbody>

<tbody><tr>

<td colspan="5"></td>

</tr>

<tr>

<td colspan="5">

&nbsp;&nbsp;Constraint <input type="text" size="6" class="create_constraint" title="제약명을 입력해주세요." value="####_PK">FOREIGN KEY <select id="create_foreign">

<option>클 릭</option>

</select> 

</td>

</tr>

<tr>

<td>

</td>

<td colspan="5" style="text-align: left;">

REFERENCES <select id="create_reference">

<option>클 릭</option>

</select> ( <select id="create_reference_table">

<option>클 릭</option>

</select> )

</td>

</tr>


<tr style="height: 80px">

<td colspan="3">

</td>

<td colspan="2">

<input id="helperOK" type="button" value="O K">||<input id="helperCancel" type="button" value="Cancel">

</td>

</tr>

</tbody></table>

</form>


</div>

</div>

'J-Query' 카테고리의 다른 글

jQuery 라이브러리 사용 방법  (1) 2012.11.15
sliding  (0) 2012.11.15
jQuey 1.8 이 정식 릴리즈 되었습니다.  (0) 2012.08.24
jQuery.slide()  (0) 2012.07.10
body CSS 그라데이션 흰색 - > 블랙  (0) 2012.06.22
Posted by 사라링


[국비지원]

이루고자 하는 바가 있어 국비지원학원 http://www.gukbi.com/ 을 가시게 되시면 C++, JSP, JAVA(자바), ASP.NET(닷넷), C#, Android(안드로이드), Object C(아이폰), Oracle(오라클), 네트워크 엔지니어, 시스템 엔지니어, 임베디드 같은 주 언어를 고르시기 전에 사람인 같은 취업 사이트에 먼저 키워드를 검색해서 신입을 뽑는 회사는 얼마나 되는지 나이 제한은 몇 살까지인지 지원한 사람들 학력은 어떻게 되는지 자격조건은 얼마나 갖추었는지 확인해보시고 국비지원 학원을 가셔야 합니다. 가장 좋은 코스는 졸업 후 취업은 나라에서 회사에 1년간 취업 장려금을 보조해주니 실력이 없어도 취업해서 부딪치며 스스로 배우는 것입니다. 학원 안 가고 졸업 후 바로 취업해서 일하다가 안 맞아서 중간에 그만두어도 다른 일을 찾아도 늦지 않습니다. 학원 6개월을 끝난 상태에서 취업해서 일했는데 안 맞으면 남들보다 1년 늦어버립니다.


[나이]

우선 나이가 30살 이상이시라면 IT 학원은 되도록 가지 않았으면 합니다. 학원에서 말한 것과 다르게 학원 교육 맞히고 나와도 취업하기 어렵고 취업이 되어도 남들보다 어려운 환경에서 일해야 합니다. 정말 군대 한 번 더 갔다고 생각하시지 못하신다면 국비지원 학원 가지 마세요. 학원에서 혼자서 1~2년 차가 할 일을 할 정도로 하고 나오시지 않으면 원하는 곳에는 취업하기 어렵습니다. 돈 없는 곳은 신규고용촉진장려금 http://moleg.tistory.com/2584 대상자가 아니라는 이유로도 거절할 수 있습니다.


[수습기간]

신입은 정규직으로 지원했는데 수습기간이 있거나 인턴기간이 있거나 하면 계약직이나 다를 게 없습니다. 말만 바꾼 것뿐이지 수습이나 인턴이나 계약직은 같은 말입니다. 그러니 정규직으로 지원했는데 수습기간이 있거나 인턴 기간이 있다면 그 기간 안에 회사가 원하는 성과를 내지 못하면 잘릴 수 있다는 걸 염두에 두세요. 노력 안 해서 잘린 거라고 하는 분들도 있는데 신입에 노력과 성과는 조금 다릅니다. 이 성과라는 게 대개 애매한데 새벽같이 열심히 일해도 업체에 따라 신입 혼자서 프로젝트를 진행하고 수익을 줄 수 있어야 정규직이 되는 일도 있고 사장님이 싸게 인력 파견해서 쏠쏠하게 챙기기 위한 예도 있고 사람 성격처럼 참 다양합니다. 물론 사람을 키워 기업에 프레임워크와 라이브러리를 구축하려는 괜찮은 회사도 있습니다. (100인 가까운 중소기업은 수습기간 있어도 괜찮습니다. 말로는 너는 아직 못하지만 2년 뒤 너의 모습을 기대하며 우리가 큰맘 먹고 채용해준다고 하는데 인성적으로 문제가 없는 사람은 보통 채용됩니다. 이런 회사는 정말 2년은 기다려줍니다. 2년 뒤에도 못하면 나가야죠. 2년이나 시간을 주었는데 못나한다고 내보내느냐고 따지면 당신은 염치도 없는 사람입니다.)


[계약직]

계약직 정규직 전환 같은 채용 공고를 보시더라도 무시하지 마세요. 정규직을 모집하면서 수습기간이나 인턴기간을 두는 회사랑 비슷하게 생각하시데 보통 계약직 후 정규직 전환은 계약기간 안에 함부로 자른다거나 연봉에 70~80%를 준다는 소리는 안 하고 계약직은 이력서에 적어도 경력에는 쳐주지 않아도 수습이나 인턴처럼 나쁘게 보지는 않습니다. 수습이나 인턴 후 정규직 무조건 전환이라고 말하는 회사도 말은 그렇게 해도 볼 건 다 봅니다.


[맛보기]

최종학교 졸업 후 고용보험가입이력 없는 신입(졸업 후 취업한 적 없는 사람)을 고용한 경우 노동부에 등록한 업체는 6개월(최대 1년)간 장려금을 받을 수 있습니다. 이걸 노리고 순진한 신입을 꼬드겨 수습으로 취직시키고 아주 싼 값에 맛보기를 하고 버리는 기업이 상당히 많습니다. 정규직처럼 뽑아놓고 몰래 청년 인턴 같은 걸로 올려서 돈 받는 곳도 많습니다. 그런데 멋모르는 신입은 이 기간을 가고 싶은 곳 인턴십 같은 곳에 쓰거나 자신이 해보고 싶었던 분야에 취직하는 데 쓰면 처음에 조금 못하더라도 기업에서도 나라에서 받는 돈이 있으니 1년 정도는 데리고 있어 보려고 합니다. 1년만 채우면 경력이기 때문에 어디를 가든 수습으로 입사하지 않고 바로 정직원으로 입사할 수 있습니다. 그러니 신입 때는 무조건 1년은 일 할 수 있는 곳으로 가야 합니다. 그러려면 어쩔 수 없이 안정적인 회사로 가거나 내가 학원이나 학교에서 배워서 잘할 수 있는 일을 해야 합니다.


회사가 못하는 신입을 1년 정도는 데리고 있어 보려고 하는 이유는 경력자를 데려와도 업무가 달라서 처음부터 다시 가르쳐야 하는 일이 많습니다. 그런데 나이 어리고 취업 이력 없는 신입을 쓰면 나라에서 6개월은 고용 장려금을 받을 수 있고, 6개월 후 정규직으로 전환하면 또 나라에서 6개월간 추가로 돈을 받을 수 있기 때문이지요.  http://article.joinsmsn.com/news/article/article.asp?total_id=4267806&cloc=  그런데 나는 잘 못하니까 못하는 나를 뽑아주는 회사가 고마워서 또는 돈은 적어도 빨리 취업해서 경력 쌓아야지 같은 생각으로 악덕 업체한테 맛보기 당하고 쫓겨나면 정말 안타까운 일이 벌어집니다.


본인 딴엔 열심히 한다고 수습 3개월하고 수습 2개월 연장하고 나오게 되면 정말 눈물 나지만 학벌이나 실력 없으면 다음에 취업하기 어려워집니다. 반년 날아간 겁니다. 면접 보면 그동안 뭐했느냐고 물어보는데 공백 기간 설명하기 어렵습니다. 기간이라도 짧으면 아파서 입원했다고 하지 취업했다가 수습기간에 잘렸다고 하면 안 좋은 눈으로 보는 회사가 많습니다. 고정수입이 없는 소기업은 정부지원금 하나에도 목을 맵니다. 이런 회사는 수습 5개월하고 퇴사할 때도 권고직 사직으로 절대 안 해주려고 합니다. 이유는 권고직 사직으로 직원을 퇴사시키면 새로 직원을 뽑을 때 정부 지원금을 받을 수 없기 때문입니다. 권고직 사직으로 나오지 못하시면 굴비처럼 엮은 고용보험가입 이력이 180일 넘어도 실업 수당을 받을 수 없습니다.


사회에서는 1년 이하의 경력은 철저히 무시합니다. 아무리 여러 개 굴비처럼 엮어서 2년이라도 다 무시됩니다. 프로젝트 단위로 계약직한 거는 회사 업무와 비슷하다면 조금은 인정해주는 회사도 있습니다. 그렇다고 경력에 넣어주진 않습니다. 경력자보다 실력 없고 학력도 별 볼 일 없는데 정부 장려금도 받을 수 없는 신입이라면 고용순위는 거의 바닥이라고 생각하시면 됩니다. 면접관은 자기 회사만 생각하고 이 사람은 못 견디고 뛰쳐나왔다고 생각할 수 있습니다. 면접도 보지 않고 서류에서 탈락시키는 회사도 있습니다.


맛보기는 인력 파견이든 솔루션 업체든 별 상관 없이 이루어집니다. 그러니 사회 첫단추는 내 실력이 부족하다고 연봉도 안 보고 일도 안 보고 무작정 취업하시지 마세요. 기업들도 다 정부 지원금 받고 고용하는 거니 여러분도 인터넷에서 쇼핑하듯 잘 골라서 취직하셨으면 합니다. 


이걸 제대로 못 하고 개발하는 게 좋아서 남들보다 일찍 취업하는 게 좋을 거 같아서 알아보지도 않고 무작정 취직해서 이용당하고 쫓겨나면 상처받고 업계를 뜨게 되는데 배운 게 도둑질이라 나이 먹어 다시 돌아오면 지방 출장 기본 3~6개월 주말에 올라오는 데 지치고 주말 못 쉬고 업무 분석하는데 사람에게 뜯기는 하는 곳(아주 일부 SI, ERP, 컨설팅) 우리에겐 꿈이 있다며 직원들의 희생만 요구하고 적은 임금에 공장 기계처럼 밤새도록 돌아가는 아주 일부 솔루션 업체처럼 남들이 힘들어서 안 하는 일을 할 수밖에 없습니다. 많이 생각하고 많이 고민하셔서 처음 1년은 좋은 곳에 취직하시길 바랍니다.


[연봉]

첫 연봉 별거 아닐 수 있으나 정말 일이 힘들 때 연봉 액수 때문에 이 악물고 탈옥하지 않을 수 있습니다. 연봉 적게 주려는 회사는 보통 수익 구조가 불안해서 신입에 많이 투자하기가 어려운 회사인 경우가 많습니다. 경력자를 뽑아야 하는 데 여러 가지 사정(밑에 [신입] 참고)으로 어려워서 말 잘 듣고 저렴한 신입을 한번 뽑아보자고 생각하는 회사도 많습니다. 신입을 한 번도 뽑아보지 않은 회사는 신입 수준을 모르고 뽑는 일도 있습니다. 수습기간이 있다면 그 기간 안에 회사가 나에게 바라는 성과 같은 걸 물어보시는 것으로 가늠해보는 것도 좋습니다. (사람이 급한 벤처기업은 사탕 발림으로 말 하기도 합니다.)


수익구조가 탄탄한 기업은 신입이라고 적게 주려고 하지 않습니다. 최소 연봉에도 못 미치는 기업은 절대 가지 마세요. 머리 숙이고 몸값 깎아서 들어갔다고 얘가 우리 회사에 정말 오고 싶어서 이렇게까지 하는구나 생각해주는 곳은 없습니다. 그냥 싼 맛에 잡일 시켜도 말 잘 들을 거 같은 사람 뽑은 겁니다. (학교나 학원에서 하위권이셨고 남들처럼 자격조건 갖추지 못해서 한 달 내내 구직했는데 괜찮은 기업에서는 불러주지 않는다면 적게 줘도 취직하셔야 하며 자기 기분에 따라 성내는 인간이 사수로 있어도 이 악물고 1년 버티셔야 합니다.)


[복지]

복지나 회사 꾸며놓은 회사는 이렇게 있어야 신입사원들이 여긴 좋은 회사구나! 입사지원서 넣으니까 그걸 노리고 꾸며놓은 회사도 많습니다. 실상 복지를 운운하며 연봉이 상대적으로 낮은데도 많고요. 예를 들어 야근수당 주는 회사는 보통 야근수당 운운하며 연봉이 적습니다. 정말 정시퇴근하기 어렵습니다. 수당 받으려고 안 가는 윗사람 만나면 퇴근하기 어려워집니다. 또 이런 회사에서 능력 있는 사람을 찾기 어렵습니다. 능력이 있는데 야근을 안 해서 능력 없어서 야근하는 인간보다 보수가 적거나 비슷하여서 보통 실력 있으신 분은 이직하지 이런 회사에 오래 있지 않습니다. 제 생각에는 프로젝트 좀 밀리면 도와줄 수 있고 도움받을 수 있다고 생각합니다. 하지만 사회에서 뜯겨서 능력 갖추신 분들은 대부분 개인적이고 현실적인 분들이 많은 거 같습니다.


[생활]

소기업에서 개발자로 일하시려면 출근 거리는 가까운 게 좋습니다. 일반 소기업은 야근은 일상적이기 때문에 시간 되면 저녁 먹고 (최소 9시) 보통 10시까지는 일상적으로 일하는 겁니다. 프로젝트 밀리면 주말출근은 기본이고요. 근데 출근 1시간 걸리면 평일엔 정말 병든 닭처럼 골골거리는 게 보통일 겁니다. 회사에 간이침대가 보이면 좀 의심하셔도 됩니다. 가끔 책상 구석에서 등산용 침낭을 발견할 수 있습니다. (출근 거리가 짧아도 회사가 내가 못해도 1년은 참고 기다려줄 수 있는지 고민하고 결정하셔야 합니다.)


[낚시]

SI니 SM이니 ERP니 게시판 글 낚기지 말고 취직하시고요. 차라리 남들보다 능력 없을 때는 솔루션 업체보다 파견만 가도 수당 나오는 쪽이 1년 동안 스스로 기술을 올리기 좋을 수 있습니다. 악덕 솔루션 업체도 생각보다 많습니다. 공장 기계처럼 종일 사무실에서 나가지 않고 밥 시켜먹고 눈 빠지게 코딩해야 하는 곳도 있습니다. 우리처럼 재미있는 거 개발하는 회사는 없다면서 최대한 싸게 부려 먹으려는 회사에서 일하다 보면 개발 기술보다 음식 주문받고 전화하기 선배님들 그릇 치우기 쓰레기통 비우기가 개발 도구보다 먼저 익숙해진 느낌이 들지도 몰라요.


[업무]

SI는 순수 요구를 분석해서 만들어 주는 거랑 요구를 받아서 만들어둔 SI 솔루션을 조금씩 바꿔서 만들어주는 거 크게 2가지인데 만드는 게 아직 자신 없다면 후자가 좋을 거 같고 만드는 거 자체가 재미있다면 전자가 좋을 거 같습니다. 기획된 요구서처럼 만들어주기만 하면 되는 경우 프로젝트를 회사 내부에서 진행하고 담당자가 방문해서 검수받는 걸로도 계약하는 회사가 개발자를 챙겨주는 분위기면 개발하기 편할 수 있습니다. 뭐 일정 자체가 빡빡하다면 주말에도 나오셔야겠지만요. 생각 있는 회사라면 성과보수나 주말수당은 줄 겁니다.


ERP 같은 건 회사 규모가 작다면 프로젝트 매니저가 따로 없어서 개발하고 PM하고 둘 다 하는 경우도 생기지만 온종일 모니터 보기 지겨우신 분은 이게 하기 괜찮을 수 있습니다. 어떻게 보면 고객 업무와 요구사항을 분석하는 게 개발하는 것보다 더 중요할 수 있어요. 개발을 처음 할 때는 기술이 다처럼 느껴졌지만, 한국에서 개발자로 살아가려면 고객의 업무를 익히고 요구사항을 분석해 이해하고 제안하는 기술이 더 중요한 것처럼 느껴집니다.


SM은 편할 수 있고 더러울 수 있는데 같은 회사에서 만든 솔루션 예를 들어 SI 그룹웨어나 ERP 회계 같은 걸 손봐주는 일이거나 과장 대리 같은 선배가 있어서 안 되는 부분에서 소스를 던져 주거나 붙어서 봐줄 수 있는 환경이라면 혼자서 솔루션이나 프로젝트 개발하는 것보다 남는 시간도 많고 자기 계발하기 좋을 거에요. 기간 짧게 다른 여러 군데 SI 회사가 만든 거 통합시켜 대충 돌아가게 하고 SI 인력 빠져나간 거 고쳐주거나 수정해주는 거라면 SM이라고 해도 좀 피가 나고 알이 배기고 이가 갈릴 수 있어요.


[취업]

혹시 아무 데나 빨리 취직해서 이직을 노리시는 분이신가요? 하고 싶은 거랑 전혀 다른 거로 취직하시게 되시면 경력이 쌓여도 주말에 따로 공부하지 않으면 전직 못하실 수 있고요. 들어간다고 해도 경력 버리고 신입으로 다시 들어가셔야 할 수 있어요.


첫단추 잘못 끼우셨어도 수습 5개월 지나고 연봉 절반 깎자고 해도 정규직 전환하셔서 1년은 채우셔야 합니다. 만약 못한다고 나가라고 하면 스스로 연봉 절반만 주셔도 일하겠다고 말해야 합니다. 억울해도 어쩔 수 있나요. 교통사고 당하고 싶어서 당하는 사람은 없습니다. 이 악물고 1년 채우고 나오시면 다음에 취직하기 수월해지실 겁니다. 경력 1년은 수습기간을 두는 회사는 없습니다. 있다면 앞서 경험한 쓰레기 회사입니다. 근데 보통 이 악물고 견딜 수 있는 회사는 저런 얘기도 안 하죠. 간접적으로 이거 언제까지 못 끝내면 짐 싸서 나가라고 말할 뿐입니다. 그래도 아시죠? 좀 억울하시겠지만, 사정해서라도 돈 못 받고 다니실 수 있으면 부탁하셔서 1년 채우셔야 합니다.


[신입]

기업이 신입을 뽑는 이유는 기업 프레임워크와 라이브러를 구축하려고 하는 기업도 있지만 이런 기업은 보통 규모가 있습니다. 보통은 경력자가 나갔는데 연봉은 낮아서 경력자를 찾기 어려워서, 경력에 도움 안 돼서 경력자가 꺼리는 일, 아주 고된 일이라 경력자가 피하는 일, 비전 있어 보이지만 아직은 검증되지 않아 아직 불안한 일을 시키기 위해서 뽑습니다.


경력자가 꺼리는 일에 대표적인 예는 새로 나온 기술을 검증하는 것, 구시대 언어로 작성된 프로그램을 요즘 추세에 맞는 언어로 바꾸는 것이 있습니다. 전자는 경력을 쌓아도 사회가 그 기술을 요구하지 않아서 관련 기술을 하는 회사가 없으면 이직하기 어렵습니다. 연차가 쌓여도 연봉을 올려 받지 못해도 이직하지 못하는 사태가 됩니다. 후자는 신입 기술향상 및 이직에 매우 도움이 될 수 있습니다.


[도전]

좋은데 들어가셨다면 좋지만 다른 사람보다 실력도 학벌도 부족하다면 취업할 수 있는 IT 소기업은 다 비슷비슷합니다. 들어가셨다면 1년은 죽었다 생각하시고 일하세요. 자존심은 말 안 해도 아시죠? 버리세요. 죽었다고 생각했는데 쫓겨나면 또다시 도전하세요. 더럽고 치사해서 다시는 이 일 하기 싫어질지도 모르지만, 복권 사면 맨날 꽝이더라도 가끔 5천 원 당첨되는 것처럼 언젠가 괜찮은 곳에 가게 되실 겁니다.


PS-

[정규직]

구직 사이트에 보면 정규직 채용으로 글이 많이 올라오지만, 소기업은 정규직이라는 말을 잘못 알고 있는 거 같습니다. 정규직이란 1달은 맛보기로 고용보험가입 없이 일하고 1달 뒤에 근로계약서 1년짜리를 쓰고 서로 책임감을 갖고 일하는 것을 말합니다. 정규직 면접 볼 때 수습 3개월 70~80% 지급한다면 그건 인턴 정규직 전환이지 정규직으로 고용하는 건 아닙니다. 단 수습(인턴) 3개월 동안 신입 OJT 교육을 해주는 회사는 너무 맞지 않는 사람(책임감을 안드로메다에 놓고 오신 분) 아니면 거의 정규직 전환되니 안심하고 입사하셔도 괜찮을 거 같습니다.


PS2-

실력이나 학력이 안 되신다면 수습(인턴)기간 3개월 중 기간을 단축하고 정규직으로 입사(근로 계약서 작성)한 선배가 있는지 확인하세요. 어떻게 하든 경력을 쌓으시기를 바랍니다. 회사에서 신으로 인정받는 개발자 1인이 아닌 이상 연봉은 경력 + 학력으로 받습니다.


PS3-

홈페이지 디자인에 속지 마세요. 회사는 직접 가봐야 압니다. 면접 기다리는 동안, 하는 동안, 나올 때 회사의 사람과 비품을 잘 관찰해 두었다가 비교할 수 있게 정리해두세요. 위에서도 말씀드렸지만, 처음에 급해서 고마워서 나와 잘 맞는지 비교하기를 잘 못 하면 취업에서 피곤해집니다. 개발 환경도 중요할 수 있습니다. 의자나 모니터, 에어컨 같은 부분에 돈을 안 쓰는 회사가 연봉까지 적다면 좀 생각해보셔야 합니다.


PS4- 좋은회사를고르는방법

http://www.gpgstudy.com/gpgiki/%EC%A2%8B%EC%9D%80%ED%9A%8C%EC%82%AC%EB%A5%BC%EA%B3%A0%EB%A5%B4%EB%8A%94%EB%B0%A9%EB%B2%95

Posted by 사라링


[오라클 에러] ORA-03113: 통신 채널에 EOF가 있습니다.

1. 가장 많은 원인은 서버의 Oracle 쉐도 프로세스가 예기치 않게 종료된 경우 입니다. 따라서 수행중에 갑자기 ORA-3113과 3114가 발생했다면, 우선 서버의 alert.log를 점검하여 다른 Oracle 오류가 발생했는지 알아보십시요. 

<< alert.log >> 서버가 UNIX 인경우 $ORACLE_HOME/rdbms/log/alert_.log 화일에 ORA-3113 에러가 
발생했던 시점에서 다른 에러가 발생했는지 점검 합니다. 특히 ORA-600[],[]이 발생했으면 에러 내용을 Oracle Technical Support Center로 
연락 하십시오.

2. ORA-3113의 원인 중 그 다음으로 많은 것은 SQL*NET 드라이버가 Unix의 ORACLE 실행 파일과 연결되지 않아 발생한 경우입니다. 연결을 공식적으로 수신하고 그것을 ORACLE 쉐도 프로세스에 전달한다 해도, 쉐도 프로세스는 처리방법을 모르기 때문에 어떤 방법으로도 응답하지 못할 수 있습니다. 그러므로 클라이언트는 연결순간에 ORA-3113을 보게 됩니다.

3. 세번째로 많은 원인은 서버쪽의 기계 손상이나 네트워크 고장입니다.

4. 자주 있는 것은 아니지만 같은 네트워크에서 두 서버가 같은 노드 이름을 가질 때에도 이 오류가 발생합니다.

5. ORA-3113은 토큰링 카드의 공유 RAM 크기가 16KB가 아니라 8KB로 설정 되었음을 나타내기도 합니다. 토큰 링을 사용중이라면 공유 버크 크기를 점검하고 키워 보십시요. 

6. ORA-3113은 INIT.ORA 매개변수 CONTEXT_AREA와 CONTEXT_INCR이 4096이라는 값으로 설정된 경우에도 발생합니다. 그럴때는 값을 8192로 키우면ORA-3113이 해소됩니다. 

이상 말한 모든 원인은 결국 클라이언트가 서버로부터 어떤 정보를 읽으러 갔다가 거기서 더 이상 연결이 없음을 발견했다는 뜻입니다. ORA-3113은 좀 더 진단해야 추적 가능한 더 큰 문제가 있음을 알리는 신호탄에 불과합니다. 다행히도 앞서 말한 여섯가지 정보를 참고하면 해결책을 찾는 방향은 잡힐 것입니다. 
우선 ORA-3113을 디버깅하려면, 루프백을 수행중에 같은 CONNECTING을 여러번 시도해 보는 것이 좋습니다. 즉, 서버의 어떤 툴이든 데스크탑 클라이언트에서 지정하는 것과 같은 연결 스트링을 사용하여 연결할 수 있습니다. 루프백을 수행중에도 똑같은 문제가 발생하면 데스크탑 클라이언트 쪽이 아니라 
서버쪽에 문제가 있다고 보아야 합니다. 루프백을 수행하려면 서버에서 SQLPLUS 또는 SQLDBA를 호출하고, 서버의 SQLPLUS 또는 SQLDBA 프롬프트에서 다음과 같이 입력하십시요. 

 CONNECT USERNAME/PASSWORD@t:/: 

예를 들어, SQL*NET TCP/IP를 통해 Unix 서버에 연결돼 있고 SQL*Plus를 호출하고, 같은 "t::" 연결 스트링을 사용하여, 같은 SELECT 문을 내서 루프백을 해 보십시요. 


Posted by 사라링

HINT 사용하기 ( /*+ Hint내용 */ )
 1.ALL_ROWS
      Goal : Best Throughput
      용도 : 전체 RESOURCE 소비를 최소화 시키기 위한 힌트.
             Cost-Based 접근방식.
   
      예   : SELECT /*+ALL_ROWS */ EMPNO,ENAME
             FROM   EMP
             WHERE  EMPNO = 7655;
   
   2.FIRST_ROWS
      Goal : Best Response Time
      용도 : 조건에 맞는 첫번째 row를 리턴하기 위한 Resource
             소비를 최소화 시키기위한 힌트.
             Cost-Based 접근방식.
      특징 : - Index Scan 이 가능하다면 Optimizer가 Full Table Scan 대신
               Index Scan을 선택한다.
             - Index Scan 이 가능하다면 Optimizer가 Sort-Merge 보다 
               Nested Loop 을 선택한다. 
             - Order By절에 의해 Index Scan 이 가능하다면,
               Sort과정을 피하기 위해 Index Scan을 선택한다.
             - Delete/Update Block 에서는 무시된다.      
             - 다음을 포함한 Select 문에서도 제외된다.
               .집합연산자 (Union,Intersect,Minus,Union All)
               .Group By
               .For UpDate
               .Group 함수
               .Distinct    
   
      예   : SELECT /*+FIRST_ROWS */ EMPNO,ENAME
             FROM   EMP
             WHERE  EMPNO = 7655;
   
   3.CHOOSE
      Goal : Acess되는 테이블에 통계치 존재여부에 따라 
             Optimizer로 하여금 Rule-Based Approach와 Cost-Based Approach 
             중 하나를 선택할수 있게 한다.
      용도 : Data Dictionary가 해당테이블에 대해 통계정보를 가지고 있다면
             Optimizer는 Cost-Based Approach를 선택하고,
             그렇지 않다면 Rule-Based Approach를 선택한다. 
   
      예   : SELECT /*+CHOOSE */ EMPNO,ENAME 
             FROM   EMP
             WHERE  EMPNO = 7655;
   
   4.RULE
      용도 : Rule-Based 최적화를 사용하기위해. 
   
      예   : SELECT /*+RULE */ EMPNO,ENAME
             FROM   EMP
             WHERE  EMPNO = 7655;
   
B. Access Methods 로써의 Hints
  
   1.FULL
      용도 : 해당테이블의 Full Table Scan을 유도.
      
      예   : SELECT /*+FULL(EMP) */ EMPNO,ENAME
             FROM   EMP
             WHERE  EMPNO = 7655;
           * 테이블 Alias 가 있을 경우는 반드시 Alias 사용.
              Schema Name은 사용안함(From 에 SCOTT.EMP 라고 기술해도 hint에는 EMP사용).

   2.ROWID
      용도 : 지정된 테이블의 ROWID를 이용한 Scan 유도

   3.CLUSTER
      용도 : 지정된 테이블Access에 Cluster Scan 유도.
             Cluster된 Objects에만 적용가능.
      
      예   : SELECT /*+CLUSTER(EMP) */ ENAME,DEPTNO
             FROM   EMP,DEPT
             WHERE  DEPTNO = 10
             AND    EMP.DEPTNO = DEPT.DEPTNO;

   4.HASH 
      용도 : 지정된 테이블Access에 HASH Scan 유도. 
             /*+HASH(table) */

   5.HASH_AJ
      용도 : NOT IN SubQuery 를 HASH anti-join으로 변형 
             /*+HASH_AJ */
   
   6.HASH_SJ
      용도 : correlated Exists SubQuery 를 HASH semi-join으로 변형 
             /*+HASH_SJ */

   7.INDEX
      용도 : 지정된 테이블Access에 Index Scan 유도.
           * 하나의 index만 지정되면 optimizer는 해당index를 이용.
           * 여러개의 인덱스가 지정되면  optimizer가 각 index의 
             scan시 cost를 분석 한 후 최소비용이 드는 index사용.
             경우에 따라 optimizer는 여러 index를 사용한 후 결과를
             merge하는 acees방식도 선택.
           * index가 지정되지 않으면 optimizer는 테이블의 이용가능한 
             모든 index에 대해 scan cost를 고려후 최저비용이 드는
             index scan을 선택한다. 
      예   : SELECT /*+INDEX(EMP EMPNO_INDEX) */ EMPNO,ENAME
             FROM   EMP
             WHERE  DEPTNO=10

   8.INDEX_ASC
      용도 : INDEX HINT와 동일 단,ASCENDING 으로 SCAN함을 확실히 하기위함.
 
   9.INDEX_COMBINE
      용도 : INDEX명이 주어지지 않으면 OPTIMIZER는 해당 테이블의
             best cost 로 선택된 Boolean combination index 를 사용한다.
             index 명이 주어지면 주어진 특정 bitmap index 의 
             boolean combination 의 사용을 시도한다.
           
             /*+INDEX_COMBINE(table index) */

  10.INDEX_DESC 
      용도 : 지정된 테이블의 지정된 index를 이용 descending으로 scan
             하고자할때 사용.

             /*+INDEX_DESC(table index) */

  11.INDEX_FFS
      용도 : full table scan보다 빠른 full index scan을 유도.

             /*+INDEX_FFS(table index) */

  12.MERGE_AJ
      용도 : not in subquery를 merge anti-join으로 변형

             /*+MERGE_AJ */

  13.MERGE_SJ
      용도 : correalted EXISTS subquery를 merge semi-join으로 변형

             /*+MERGE_SJ */

  14.AND_EQUAL
      용도 : single-column index의 merge를 이용한 access path 선택.
             적어도 두개이상의 index가 지정되어야한다.

            /*+AND_EQUAL(table index1,index2...) */ 
            
  15.USE_CONCAT
      용도 : 조건절의 OR 를 Union ALL 형식으로 변형한다.
             일반적으로 변형은 비용측면에서 효율적일때만 일어난다.

           /*+USE_CONCAT */             
    
              
C. JOIN 순서를 결정하는 Hints

   1.ORDERED
      용도 : from절에 기술된 테이블 순서대로 join이 일어나도록 유도.
          
           /*+ORDERED */
      예   : SELECT /*+ORDERED */ TAB1.COL1,TAB2.COL2,TAB3.COL3
             FROM   TAB1,TAB2,TAB3
             WHERE  TAB1.COL1=TAB2.COL1
             AND    TAB2.COL1=TAB3.COL1; 

   2.STAR
      용도 : STAR QUERY PLAN이 사용가능하다면 이를 이용하기 위한 HINT.
             STAR PLAN은 규모가 가장 큰 테이블이 QUERY에서 JOIN ORDER상
             마지막으로 위치하게 하고 NESTED LOOP 으로 JOIN이 일어나도록
             유도한다. 
             적어도 3개 테이블 이상이 조인에 참여해야하며 LARGE TABLE의
             CONCATENATED INDEX는 최소 3컬럼 이상을 INDEX에 포함해야한다.(***중요)
             테이블이 ANALYZE 되어 있다면 OPTIMIZER가 가장효율적인 STAR PLAN을
             선택한다.    

          /*+STAR */ 

D. JOIN OPERATION을 결정하는 HINTS.

   1.USE_NL 
      용도 : 테이블의 JOIN 시 테이블의 각 ROW가 INNER 테이블을 NESTED LOOP
             형식으로 JOIN 한다.

          /*+USE_NL(inner_table) */
    
      예   : SELECT /*+ORDERD USE_NL(CUSTOMER) */
             FROM   ACCOUNT.BALANCE,CUSTOMER.LAST_NAME,CUSTOMER.FIRST_NAME
             WHERE  ACCOUNT.CUSTNO = CUSTOMER.CUSTNO;

   2.USE_MERGE
      용도 : 지정된 테이블들의 조인이 SORT-MERGE형식으로 일어나도록 유도.

          /*+USE_MERGE(table) */
           * 괄호안의 테이블은 JOIN ORDER상의 뒤의 테이블(?)

   3.USE_HASH
      용도 : 각 테이블간 HASH JOIN이 일어나도록 유도.

          /*+USE_HASH(table) */
           * 괄호안의 테이블은 JOIN ORDER상의 뒤의 테이블(?)

   4.DRIVING_SITE
      용도 : QUERY의 실행이 ORACLE에 의해 선택된 SITE가 아닌 다른 SITE에서 
             일어나도록 유도.

          /*+DRIVING_SITE(table) */ 
      예   : SELECT /*+DRIVING_SITE(DEPT)  */
             FROM   EMP,DEPT@RSITE
             WHERE  EMP.DEPTNO = DEPT.DEPTNO;     

             DRIVING_SITE 힌트를 안쓰면 DEPT의 ROW가 LOCAL SITE로 보내져
             LOCAL SITE에서 JOIN이 일어나지만,
             DRIVING_SITE 힌트를 쓰면 EMP의 ROW들이REMOTE SITE로 보내져
             QUERY가 실행된후 LOCAL SITE로 결과가 RETURN된다.

출처 : http://sqler.pe.kr/web_board/view_list.asp?id=285&read=699&pagec=1&gotopage=1&block=0&part=myboard8&tip=ok

##############################################################################
##############################################################################
##############################################################################


oracle hint 정리

/*+ ALL_ROWS */ 
explicitly chooses the cost-based approach to optimize a statement 
block with a goal of best throughput (that is, minimum 
total resource consumption) 
가장 좋은 단위 처리량의 목표로 문 블록을 최적화하기 위해 cost-based 
접근 방법을 선택합니다. (즉, 전체적인 최소의 자원 소비)

/*+ CHOOSE */ 
causes the optimizer to choose between the rule-based 
approach and the cost-based approach for a SQL statement 
based on the presence of statistics for the tables accessed by 
the statement 
최적자(optimizer)가 그 문에 의해 접근된 테이블을 위해 통계의 존재에 
근거를 두는 SQL 문을 위해 rule-based 접근 방법과 cot-based 접근 방법 
사이에 선택하게 합니다.

/*+ FIRST_ROWS */ 
explicitly chooses the cost-based approach to optimize a statement 
block with a goal of best response time (minimum 
resource usage to return first row) 
가장 좋은 응답 시간의 목표로 문 블록을 최적화하기 위해 cost-based 접근 
방법을 선택합니다. (첫번째 행을 되돌려 주는 최소의 자원 사용)

/*+ RULE */ 
explicitly chooses rule-based optimization for a statement 
block 
문 블록을 위하여, rule-based 최적화를 고르는

 

/*+ AND_EQUAL(table index) */ 
explicitly chooses an execution plan that uses an access path 
that merges the scans on several single-column indexes 
그만큼 실행 계획을 선택합니다. 그리고 여럿의 single-column 색인에 
그 scan을 합병하는 접근 경로를 사용합니다.

/*+ CLUSTER(table) */ 
explicitly chooses a cluster scan to access the specified table 
선택합니다. 그리고, 클러스터는 그 명시된 테이블을 접근하기 위해 살핍니다.

/*+ FULL(table) */ 
explicitly chooses a full table scan for the specified table 
그 명시된 테이블을 위하여, 전체 테이블 scan을 고르는

/*+ HASH(table) */ 
explicitly chooses a hash scan to access the specified table 
선택합니다. 그리고, 해쉬는 그 명시된 테이블을 접근하기 위해 운율을 살핍니다.

/*+ HASH_AJ(table) */ 
transforms a NOT IN subquery into a hash antijoin to access 
the specified table 
변환, 그 명시된 테이블을 접근하는 해쉬 antijoin으로의 NOT IN 부속 조회

/*+ HASH_SJ (table) */ 
transforms a NOT IN subquery into a hash anti-join to access 
the specified table 
변환, 그 명시된 테이블을 접근하는 해쉬 anti-join으로의 NOT IN 부속 조회

/*+ INDEX(table index) */ 
explicitly chooses an index scan for the specified table 
그 명시된 테이블을 위하여, 색인 scan을 고르는

/*+ INDEX_ASC(table index) */ 
explicitly chooses an ascending-range index scan for the specified 
table 
그 명시된 테이블을 위하여, ascending-range 색인 scan을 고르는

/*+ INDEX_COMBINE(table index) */ 
If no indexes are given as arguments for the INDEX_COMBINE 
hint, the optimizer uses whatever Boolean combination 
of bitmap indexes has the best cost estimate. If particular 
indexes are given as arguments, the optimizer tries to use 
some Boolean combination of those particular bitmap indexes. 
어떤 색인도 INDEX_COMBINE 암시를 위해 인수로서 주어지지 않는다면, 
bitmap 색인의 결합이 어떤 부울의를 가장 좋은 수행 난이도 평가를 가지고 
있든지 최적자는 이용합니다. 
특별한 색인이 인수로서 주어진다면, 최적자는 그 특별한 bitmap 색인의 
몇몇의 부울의 결합을 사용하려고 노력합니다.

/*+ INDEX_DESC(table index) */ 
explicitly chooses a descending-range index scan for the specified 
table 
그 명시된 테이블을 위하여, descending-range 색인 scan을 고르는

/*+ INDEX_FFS(table index) */ 
causes a fast full index scan to be performed rather than a full 
table scan 
빠른 전체 색인 scan이 전체 테이블 scan이라기보다는 수행되게 합니다.

/*+ MERGE_AJ (table) */ 
transforms a NOT IN subquery into a merge anti-join to access 
the specified table 
변환, NOT IN 부속 조회, 그 명시된 테이블을 접근하기 위해 anti-join을 
합병합니다.

/*+ MERGE_SJ (table) */ 
transforms a correlated EXISTS subquery into a merge semi-join 
to access the specified table 
변환, 관련된 EXISTS 부속 조회, 접근으로 semi-join을 합병합니다, 
그 명시된 테이블

/*+ ROWID(table) */ 
explicitly chooses a table scan by ROWID for the specified 
table 
그 명시된 테이블을 위하여, ROWID에 의해 테이블 scan을 고르는

/*+ USE_CONCAT */ 
forces combined OR conditions in the WHERE clause of a 
query to be transformed into a compound query using the 
UNION ALL set operator 
힘은 질의의 WHERE 문절에 있는 UNION ALL 집합 연산자를 사용하는 합성의 
질의로 변형되는 OR 조건을 합쳤습니다.

 

/*+ ORDERED */ 
causes Oracle to join tables in the order in which they appear 
in the FROM clause 
오라클이 어느 것에 순서로 테이블을 결합시키게 합니다.

/*+ STAR */ 
forces the large table to be joined last using a nested-loops join 
on the index 
큰 있는 테이블이 최종 사용/회전율에 nested-loops를 결합시킨 힘은 
그 색인에 결합합니다.

 

/*+ DRIVING_SITE (table) */ 
forces query execution to be done at a different site from that 
selected by Oracle 
힘은 그것과 다른 오라클에 의해 선택된 사이트에 되는 실행을 질의합니다.

/*+ USE_HASH (table) */ 
causes Oracle to join each specified table with another row 
source with a hash join 
오라클이 테이블이 다른 행 자원으로 해쉬 접합으로 명시되면서 각자와 
합치게 합니다.

/*+ USE_MERGE (table) */ 
causes Oracle to join each specified table with another row 
source with a sort-merge join 
오라클이 테이블이 다른 행 자원으로 sort-merge 접합으로 명시되면서 각자와 
합치게 합니다.

/*+ USE_NL (table) */ 
causes Oracle to join each specified table to another row 
source with a nested-loops join using the specified table as the 
inner table 
오라클이 그 명시된 테이블을 그 안의 테이블로 사용하는 nested-loops 접합과 
각자와 다른 행 자원에 대한 명시된 테이블을 합치게 합니다.

 

/*+ APPEND */ , /*+ NOAPPEND */ 
specifies that data is simply appended (or not) to a table; existing 
free space is not used. Use these hints only following the 
INSERT keyword. 
데이타가 테이블로 단순히 덧붙여진다는 (or not)것 명시합니다; 무료인 
현존하는 영역은 사용되지 않습니다. 
단지 그 삽입 키 핵심어를 따르는 이 암시를 사용하시오.

/*+ NOPARALLEL(table) */ 
disables parallel scanning of a table, even if the table was created 
with a PARALLEL clause 
그 테이블이 PARALLEL 문절로 새로 만들어졌다면 테이블의 평행의 순차 검색을 
무능하게 만듭니다.

/*+ PARALLEL(table, instances) */ 
allows you to specify the desired number of concurrent slave 
processes that can be used for the operation. 
DELETE, INSERT, and UPDATE operations are considered for 
parallelization only if the session is in a PARALLEL DML 
enabled mode. (Use ALTER SESSION PARALLEL DML to 
enter this mode.) 
당신이 그 연산을 위해 사용될 수 있는 동시의 슬레이브(slave) 프로세스의 
요구된 수를 명시하는 것을 허락합니다. 
그 세션이 가능하게 된 PARALLEL DML에 모드를 있다면, DELETE, INSERT, UPDATE 
연산은 단지 parallelization에 대해 고려됩니다. (사용은 이 모드에 들어가기 
위해 평행의 세션 DML을 변경합니다.)

/*+ PARALLEL_INDEX */ 
allows you to parallelize fast full index scan for partitioned 
and nonpartitioned indexes that have the PARALLEL attribute 
parallelize에 당신에게 빠른 가득한 색인 scan을 허락합니다. 그런데, 
그것은 PARALLEL 속성을 가지고 있는 색인을 분할했고 nonpartitioned했습니다.

/*+ NOPARALLEL_INDEX */ 
overrides a PARALLEL attribute setting on an index 
병렬이 색인을 나아가는 것을 속하게 하는 대체


/*+ CACHE */ 
specifies that the blocks retrieved for the table in the hint are 
placed at the most recently used end of the LRU list in the 
buffer cache when a full table scan is performed 
그 블록이 찾아서 가져왔다는 것을 명시합니다. 그리고 그 테이블을 위해 
그 암시에 놓여집니다. 그런데, 그것은 가장 최근에 사용된 언제 그 버퍼 캐쉬, 
가득한 테이블 scan에 있는 LRU 리스트의 끝입니다. 수행됩니다.

/*+ NOCACHE */ 
specifies that the blocks retrieved for this table are placed at 
the least recently used end of the LRU list in the buffer cache 
when a full table scan is performed 
그 명시합니다. 그리고, 그 블록은 이 테이블을 위해 검색되면서 최근에 사용된 
언제 그 버퍼 캐쉬, 가득한 테이블 scan에 있는 LRU 리스트의 가장 작은 끝에 
놓여집니다. 수행됩니다.

/*+ MERGE (table) */ 
causes Oracle to eval!uate complex views or subqueries before 
the surrounding query 
오라클이 그 둘러싸는 질의 전에 복잡한 뷰나 부속 조회를 평가하게 합니다.

/*+ NO_MERGE (table) */ 
causes Oracle not to merge mergeable views 
오라클이 mergeable 뷰를 합병하지 않게 하지 않습니다

/*+ PUSH_JOIN_PRED (table) */ 
causes the optimizer to eval!uate, on a cost basis, whether or 
not to push individual join predicates into the view 
개개 접합을 미는 것이 그 뷰 안으로 단정 하든 간에 비용 방식으로 최적자가 
평가하게 합니다.

/*+ NO_PUSH_JOIN_PRED (table) */ 
Prevents pushing of a join predicate into the view 
접합 술부 중에서 그 뷰로 밀면서, 막는

/*+ PUSH_SUBQ */ 
causes nonmerged subqueries to be eval!uated at the earliest 
possible place in the execution plan 
원인은 그 실행 계획에서의 가장 이른 가능한 장소에 평가되는 부속 조회를 
nonmerged했습니다.

/*+ STAR_TRANSFORMATION */ 
makes the optimizer use the best plan in which the transformation 
has been used. 
최적자가 그 변형이 사용된 가장 좋은 계획을 사용하는 제작

http://luke.tistory.com/entry/oracle-hint-%EC%A0%95%EB%A6%AC


Posted by 사라링

/


바이 패스 문을 머지 문으로 변경 하자. 


*목표*

   프로시져 에서 바이패스 문으로 작성된 UPDATE 문 3개를  머지문을 이용 하여 사용 할수 있도록 하라. 

   업데이트 문이 기본적으로 수정을 목표로 한다면 머지 문은 INSERT DELETE UPDATE  를 하나의 쿼리 내에서 실행 할수 잇는 개념으로 보면 될듯 하다.  


왜그래야하징? 

보통 
update(select ~) 

where col='a'

set a.d=b.d

여기서 (select ~) 안에는  join 등을 이용 하여 여러개의 테이블을 검색 하여 검색된 컬럼 내에서 의 값들을 검색된 컬럼으로 setting 한다. 


형식의 업데이트 사용. 

위의 방식은 옳지 않다 그 이유는 ? 

Undocumented HIT (잘못되어도 오라클에서 보장 하지 않음)    - 보장을 하지 않는 단다. 오라클이. .. 휴 (신상진 멍청이)
 
일반적으로 특정 테이블을 Update하기 위해서는 WHERE절에 EXISTS 또는 IN 등의 Sub-Query로 조인 조건을 만족하는
Row를 먼저 Check하고, 조건을 만족하는 Row에 대하여 SET 절에서 필요한 데이터를 검색하여 Update하는 것이 보통이다.
 
이 때, Update 해야 하는 Row가 많을 경우 WHERE절이나 SET절에서 테이블을 반복적으로 Random 액세스해야 하는 부담이
발생하므로 처리 범위가 넓은 Update문의 경우에는 'Updatable Join View'를 활용할 필요가 있다.
 
이 때, 조인되는 2개의 테이블은 반드시 1:1 또는 1:M의 관계여야 하며,
Update되는 컬럼의 테이블은 M쪽 집합이어야 한다.
이것은 1쪽 집합인 Parent table의 조인 컬럼이 UK 또는 PK로 설정되어 있어야 함을 의미한다. 
이 조건을 만족하지 못하는 Updatable Join View는 에러를 Return하며 실행되지 않는다.
(ORA-01779 cannot modify a column which maps to a non key-preserved table)
 
그러나, 일반적으로 View 또는 2개 이상의 테이블을 조인한 집합을 엑세스하는
경우가 많으므로 위의 UK나 PK Constraint를 설정하기 어려운 것이 현실이다.
 
따라서, 이러한 Constraint를 피해서 Updatable Join View를 사용할 수 있도록

BYPASS_UJVC 라는 힌트를 사용하여 튜닝할 수 있다.

update /*+ BYPASS_UJVC */
(select ~) 

where col='a'

set a.d=b.d

 

이런식으로. 말이다. 하지만 11g 이상부터는 오라클은 BYPASS_UJVC 을 지원 하지 않는다 . 따라서 merge 문을 이용 하여 

복잡한 update 문을 수행 해야 한다. 


MERGE INTO table_1 A

USING( SELECT ~  ) B

ON( A.SEQ=B.SEQ)     -- 반드시 이퀄 조인 이어야만 한다. B 의 SELECT 문은 상관 없다. 

WHEN MATCHED THEN 

SET A.DD = B.CC 

WHERE COL='a'


형식 으로 하면 된다. 


많은 데이터를 할경우 오라클 튜닝 힌트가 필요 하다. 







CREATE OR REPLACE PROCEDURE IBS.SP_ACT_BS ( P_BUSI_PLC_CD IN VARCHAR2,

                                            P_FRM_DT  IN VARCHAR2,

                                            P_TO_DT   IN VARCHAR2)

IS

V_BACK_FRM_DT   VARCHAR(20) :=SUBSTR(P_FRM_DT,0,4)-1||'0101';

V_BACK_TO_DT    VARCHAR(20) :=SUBSTR(P_FRM_DT,0,4)-1||'1231';

V_FINC_STAT_CD  VARCHAR(100) :='390-001' ;


V_BACK_YY    VARCHAR(20) :=SUBSTR(P_FRM_DT,0,4)-1;

V_THIS_YY    VARCHAR(20) :=SUBSTR(P_FRM_DT,0,4);



BEGIN

....  

END SP_ACT_BS;



1



       UPDATE /*+ BYPASS_UJVC */

            (SELECT A.THIS_REM_AMT AS SET_THIS_AMT,

                    A.BACK_REM_AMT AS SET_BACK_AMT,

                    B.THIS_AMT AS GET_THIS_AMT,

                    B.BACK_AMT AS GET_BACK_AMT 

               FROM ACT_ACCOUNT A

                    INNER JOIN(SELECT C.SEQ,

                                      SUM(CASE WHEN TEMP1 = V_THIS_YY THEN DECODE(B.DR_CR,'D',TO_NUMBER(TEMP6-TEMP7),TO_NUMBER(TEMP7-TEMP6)) ELSE 0 END) AS THIS_AMT,

                                      SUM(CASE WHEN TEMP1 = V_BACK_YY THEN DECODE(B.DR_CR,'D',TO_NUMBER(TEMP6-TEMP7),TO_NUMBER(TEMP7-TEMP6)) ELSE 0 END) AS BACK_AMT

                                 FROM ACT_PRT A 

                                      INNER JOIN ACT_ACCT_CD B ON A.TEMP3 = B.ACCT_CD

                                      INNER JOIN ACT_FS_DTL  C ON B.ACCT_CD = C.ACCT_CD AND C.BUSI_PLC_CD = P_BUSI_PLC_CD AND C.FINC_STAT_CD = V_FINC_STAT_CD

                                WHERE TEMP_CLS = 'SP_ACT_BS' 

                                  AND C.SEQ = MST.SEQ

                                GROUP BY C.SEQ)B ON A.SEQ = B.SEQ

              WHERE BUSI_PLC_CD = P_BUSI_PLC_CD AND FINC_STAT_CD = V_FINC_STAT_CD AND A.SEQ = MST.SEQ

             ) SET SET_THIS_AMT = GET_THIS_AMT,

                   SET_BACK_AMT = GET_BACK_AMT;

                                                                                              ▽

             

     MERGE INTO ACT_ACCOUNT A 

     USING (

        SELECT C.SEQ,

        SUM(CASE WHEN TEMP1 = V_THIS_YY THEN DECODE(B.DR_CR,'D',TO_NUMBER(TEMP6-TEMP7),TO_NUMBER(TEMP7-TEMP6)) ELSE 0 END) AS THIS_AMT,

        SUM(CASE WHEN TEMP1 = V_BACK_YY THEN DECODE(B.DR_CR,'D',TO_NUMBER(TEMP6-TEMP7),TO_NUMBER(TEMP7-TEMP6)) ELSE 0 END) AS BACK_AMT

        FROM ACT_PRT A 

        INNER JOIN ACT_ACCT_CD B ON A.TEMP3 = B.ACCT_CD

        INNER JOIN ACT_FS_DTL  C ON B.ACCT_CD = C.ACCT_CD AND C.BUSI_PLC_CD = P_BUSI_PLC_CD AND C.FINC_STAT_CD = V_FINC_STAT_CD

        WHERE TEMP_CLS = 'SP_ACT_BS' 

        AND C.SEQ = MST.SEQ

        GROUP BY C.SEQ    

      ) B ON (A.SEQ=B.SEQ)

      WHEN MATCHED THEN 

        UPDATE 

          SET A.THIS_REM_AMT = B.THIS_AMT,

                  A.BACK_REM_AMT = B.BACK_AMT

            WHERE  BUSI_PLC_CD = P_BUSI_PLC_CD AND FINC_STAT_CD = V_FINC_STAT_CD AND A.SEQ = MST.SEQ

          ;




2.



    UPDATE /*+ BYPASS_UJVC */

         (SELECT A.THIS_REM_AMT AS SET_THIS_REM_AMT,

                 A.BACK_REM_AMT AS SET_BACK_REM_AMT,

                 A.THIS_CN_REM_AMT AS SET_THIS_CN_REM_AMT,

                 A.BACK_CN_REM_AMT AS SET_BACK_CN_REM_AMT,

                 B.THIS_REM_AMT AS GET_THIS_REM_AMT,

                 B.BACK_REM_AMT AS GET_BACK_REM_AMT

           FROM ACT_ACCOUNT A

                INNER JOIN (SELECT THIS_REM_AMT,BACK_REM_AMT

                              FROM ACT_ACCOUNT

                             WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

                               AND FINC_STAT_CD = '390-002'

                               AND SEQ = '7700')B

                   ON 1=1

          WHERE A.FINC_STAT_CD = V_FINC_STAT_CD

            AND A.SEQ = '7300'

         )  SET SET_THIS_REM_AMT = GET_THIS_REM_AMT,

                SET_BACK_REM_AMT = GET_BACK_REM_AMT,

                SET_THIS_CN_REM_AMT = GET_THIS_REM_AMT,

                SET_BACK_CN_REM_AMT = GET_BACK_REM_AMT;

                                                                                                            ▽

       

    MERGE INTO ACT_ACCOUNT  A

           USING (

           SELECT THIS_REM_AMT,BACK_REM_AMT

           FROM ACT_ACCOUNT

           WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

           AND FINC_STAT_CD = '390-002'

          AND SEQ = '7700'          

           ) B 

           ON(1=1)

           WHEN MATCHED THEN 

             UPDATE 

               SET A.THIS_REM_AMT = B.THIS_REM_AMT,

                A.BACK_REM_AMT = B.BACK_REM_AMT,

                A.THIS_CN_REM_AMT = B.THIS_REM_AMT,

                A.BACK_CN_REM_AMT = B.BACK_REM_AMT

                WHERE A.FINC_STAT_CD = V_FINC_STAT_CD

                AND A.SEQ = '7300';

                

                




3.

         

    UPDATE /*+ BYPASS_UJVC */

         ( SELECT A.SEQ AS SEQ,THIS_R_AMT AS SET_THIS_R_AMT,

                  BACK_R_AMT AS SET_BACK_R_AMT,

                  THIS100,

                  BACK100,

                  THIS4600,

                  BACK4600,

                  THIS6300,

                  BACK6300

             FROM ACT_ACCOUNT A

                  INNER JOIN(SELECT SEQ, 

                                    SUM(CASE  WHEN  SEQ = '100'   THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS100,

                                    SUM(CASE  WHEN  SEQ = '100'   THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK100,

                                    SUM(CASE  WHEN  SEQ = '4600'  THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS4600,

                                    SUM(CASE  WHEN  SEQ = '4600'  THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK4600,

                                    SUM(CASE  WHEN  SEQ = '6300'  THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS6300,

                                    SUM(CASE  WHEN  SEQ = '6300'  THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK6300

                               FROM ACT_ACCOUNT

                              WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

                                AND FINC_STAT_CD = V_FINC_STAT_CD)B ON A.SEQ = B.SEQ

            WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

              AND FINC_STAT_CD = V_FINC_STAT_CD

              AND A.SEQ IN('4500','6200','7400','7500')

          ) SET SET_THIS_R_AMT = CASE WHEN SEQ = '4500' THEN THIS100

                                      WHEN SEQ = '6200' THEN THIS4600 

                                      WHEN SEQ = '7400' THEN THIS6300 

                                      WHEN SEQ = '7500' THEN THIS4600+THIS6300 END,

                SET_BACK_R_AMT = CASE WHEN SEQ = '4500' THEN BACK100

                                      WHEN SEQ = '6200' THEN BACK4600 

                                      WHEN SEQ = '7400' THEN BACK6300 

                                      WHEN SEQ = '7500' THEN BACK4600+BACK6300 END;


                                                                                                            ▽



        MERGE INTO ACT_ACCOUNT A

        USING (SELECT SEQ, 

        SUM(CASE  WHEN  SEQ = '100'   THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS100,

        SUM(CASE  WHEN  SEQ = '100'   THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK100,

        SUM(CASE  WHEN  SEQ = '4600'  THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS4600,

        SUM(CASE  WHEN  SEQ = '4600'  THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK4600,

        SUM(CASE  WHEN  SEQ = '6300'  THEN THIS_R_AMT ELSE 0 END) OVER() AS THIS6300,

        SUM(CASE  WHEN  SEQ = '6300'  THEN BACK_R_AMT ELSE 0 END) OVER() AS BACK6300

         FROM ACT_ACCOUNT

         WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

         AND FINC_STAT_CD = V_FINC_STAT_CD

        ) B

        ON (A.SEQ = B.SEQ)

       WHEN MATCHED THEN

        UPDATE 

        SET  A.THIS_R_AMT = CASE WHEN SEQ = '4500' THEN THIS100

                                      WHEN SEQ = '6200' THEN THIS4600 

                                      WHEN SEQ = '7400' THEN THIS6300 

                                      WHEN SEQ = '7500' THEN THIS4600+THIS6300 END,

                A.BACK_R_AMT = CASE WHEN SEQ = '4500' THEN BACK100

                                      WHEN SEQ = '6200' THEN BACK4600 

                                      WHEN SEQ = '7400' THEN BACK6300 

                                      WHEN SEQ = '7500' THEN BACK4600+BACK6300 END

        WHERE BUSI_PLC_CD = P_BUSI_PLC_CD

              AND FINC_STAT_CD = V_FINC_STAT_CD

              AND A.SEQ IN('4500','6200','7400','7500');



머지문을 추가로 공부 하자. !!  

Listing 4. Merge delete

MERGE INTO customer as C
USING customer_trans as CT
ON C.customer_num != CT.customer_num
WHEN MATCHED THEN  DELETE
WHEN NOT MATCHED THEN INSERT VALUES (CT.customer_num, CT.fname, 
CT.lname, CT.company, CT.address1, CT.address2, CT.city, 
CT.state, CT.zipcode, CT.phone);

Listing 5 shows other usages of the merge statement where the WHEN MATCHED and WHEN NOT MATCHED clauses are optionally used. When one of these clauses is not specified, that part of the merge is ignored. In the first merge statement, all the non-matching rows would be ignored and matched rows would be updated in the sale table. Similarly, in the second mergestatement, only non-matched rows would be inserted into the customer table, and all the matched rows would be ignored.


Listing 5. Exclusive update, insert, or delete
MERGE INTO customer AS C
USING customer_trans AS CT
ON C.customer_num = CT.customer_num
WHEN MATCHED THEN UPDATE SET C.phone = CT.phone;

MERGE INTO customer as C
USING customer_trans as CT
ON C.customer_num = CT.customer_num
WHEN NOT MATCHED THEN INSERT VALUES (CT.customer_num, CT.fname, 
CT.lname, CT.company, CT.address1, CT.address2, CT.city, CT.state,
CT.zipcode, CT.phone);

The Informix implementations of the merge statement allow the flexibility of ordering the WHEN MATCHED and WHEN NOT MATCHED clauses, as shown in Listing 6.


Listing 6. Flexible ordering of update, insert, and delete clauses
MERGE INTO customer as C
USING customer_trans as CT
ON C.customer_num = CT.customer_num
WHEN NOT MATCHED THEN INSERT VALUES (CT.customer_num, CT.fname, 
CT.lname, CT.company, CT.address1, CT.address2, CT.city, CT.state,
CT.zipcode, CT.phone)
WHEN MATCHED THEN UPDATE SET C.phone = CT.phone;

하나의 쿼리에서 INSERT DELETE UPDATE 된다. 


MERGE 문 성능 최적화

SQL Server 2008 R2
이 항목은 아직 평가되지 않았습니다.이 항목 평가

SQL Server 2008에서는 MERGE 문을 사용하여 단일 문에서 여러 DML(데이터 조작 언어) 작업을 수행할 수 있습니다. 예를 들어 원본 테이블과의 차이점에 따라 대상 테이블에서 행을 삽입, 업데이트 및 삭제하여 두 테이블을 동기화해야 하는 경우가 있습니다. 일반적으로 이러한 작업은 개별 INSERT, UPDATE 및 DELETE 문을 포함하는 저장 프로시저 또는 일괄 처리를 실행하여 수행합니다. 그러나 이는 원본 테이블과 대상 테이블의 데이터가 적어도 각 문에 대해 한 번씩 여러 번 계산되고 처리됨을 의미합니다.

MERGE 문을 사용하면 여러 개의 개별 DML 문을 단일 문으로 대체할 수 있습니다. 이렇게 하면 작업이 하나의 문 내에서 수행되므로 원본 및 대상 테이블의 데이터가 처리되는 횟수가 최소화되어 쿼리 성능이 향상됩니다. 단, 성능 향상을 위해서는 인덱스, 조인 및 기타 고려 사항이 올바르게 설정되어야 합니다. 이 항목에서는 MERGE 문을 사용할 때 최적의 성능을 얻는 데 도움이 되는 최선의 방법 권장 사항에 대해 설명합니다.

MERGE 문의 성능을 높이려면 다음 인덱스 지침을 따르는 것이 좋습니다.

  • 원본 테이블의 조인 열에 고유한 포함 인덱스를 만듭니다.

  • 대상 테이블의 조인 열에 고유한 클러스터형 인덱스를 만듭니다.

이러한 인덱스를 통해 조인 키의 고유성을 확보하고 테이블의 데이터가 정렬되도록 할 수 있습니다. 쿼리 최적화 프로그램이 중복 행을 찾아 업데이트하기 위해 별도로 유효성 검사를 수행할 필요가 없고 추가 정렬 작업도 필요 없으므로 쿼리 성능이 개선됩니다.

예를 들어 다음 MERGE 문에서 원본 테이블 dbo.Purchases와 대상 테이블 dbo.FactBuyingHabits는 ProductID 및 CustomerID 열에서 조인됩니다. 이 문의 성능을 높이려면 dbo.Purchases 테이블의 ProductID 및CustomerID 열에 고유 또는 기본 키 인덱스(클러스터형 또는 비클러스터형)를 만들고 dbo.FactBuyingHabits 테이블의 ProductID 및 CustomerID 열에 클러스터형 인덱스를 만듭니다. 이 테이블을 만드는 데 사용된 코드는MERGE를 사용하여 데이터 삽입, 업데이트 및 삭제에서 볼 수 있습니다.

MERGE dbo.FactBuyingHabits AS Target
USING (SELECT CustomerID, ProductID, PurchaseDate FROM dbo.Purchases) AS Source
ON (Target.ProductID = Source.ProductID AND Target.CustomerID = Source.CustomerID)
WHEN MATCHED THEN
    UPDATE SET Target.LastPurchaseDate = Source.PurchaseDate
WHEN NOT MATCHED BY TARGET THEN
    INSERT (CustomerID, ProductID, LastPurchaseDate)
    VALUES (Source.CustomerID, Source.ProductID, Source.PurchaseDate)
OUTPUT $action, Inserted.*, Deleted.*; 


MERGE 문의 성능을 높이고 정확한 결과를 얻으려면 다음 조인 지침을 따르는 것이 좋습니다.

  • ON <merge_search_condition> 절에는 원본 및 대상 테이블의 데이터 비교를 위한 조건을 나타내는 검색 조건만 지정합니다. 즉, 대상 테이블에서 원본 테이블의 해당 열과 비교할 열만 지정해야 합니다. 상수와 같은 다른 값에 대한 비교는 포함하지 않습니다.

원본 또는 대상 테이블에서 행을 필터링하려면 다음 방법 중 하나를 사용합니다.

  • 적절한 WHEN 절에 행 필터링을 위한 검색 조건을 지정합니다(예: WHEN NOT MATCHED AND S.EmployeeName LIKE 'S%' THEN INSERT...).

  • 필터링된 행을 반환하는 원본 또는 대상에 대한 뷰를 정의하고 이 뷰를 원본 또는 대상 테이블로 참조합니다. 대상 테이블에 대해 정의된 뷰의 모든 동작은 뷰 업데이트를 위한 조건을 충족해야 합니다. 뷰를 사용하여 데이터를 업데이트하는 방법은 뷰를 통해 데이터 수정을 참조하십시오.

  • WITH <common table expression> 절을 사용하여 원본 또는 대상 테이블에서 행을 필터링합니다. 이 방법은 ON 절에 추가 검색 조건을 지정하는 것과 비슷하며 잘못된 결과를 생성할 수 있으므로 사용하지 않는 것이 좋으며 사용할 경우 구현 전에 철저히 테스트해야 합니다.

자세한 내용은 MERGE를 사용하여 데이터 삽입, 업데이트 및 삭제를 참조하십시오.

조인의 쿼리 최적화

MERGE 문의 조인 작업은 SELECT 문의 조인과 동일한 방식으로 최적화됩니다. 즉, SQL Server에서 조인을 처리할 때 쿼리 최적화 프로그램은 여러 가지 가능한 방법 중 가장 효율적인 방법을 선택합니다. 조인에 대한 자세한 내용은 조인 기본 사항 및 고급 쿼리 튜닝 개념을 참조하십시오. 원본 및 대상의 크기가 비슷하고 ‘인덱스를 위한 최선의 방법’ 섹션에 설명된 인덱스 지침을 원본 및 대상 테이블에 적용한 경우 merge join 연산자가 가장 효율적인 쿼리 계획입니다. 두 테이블 모두 한 번만 검색되고 데이터를 정렬할 필요가 없기 때문입니다. 원본 테이블이 대상 테이블보다 작은 경우 nested loops 연산자가 좋습니다.

MERGE 문에서 OPTION (<query_hint>) 절을 지정하여 특정 조인을 사용하도록 강제할 수 있습니다. 해시 조인은 인덱스를 사용하지 않으므로 MERGE 문에 대한 쿼리 힌트로는 사용하지 않는 것이 좋습니다. 쿼리 힌트에 대한 자세한 내용은 쿼리 힌트(Transact-SQL)를 참조하십시오. 다음 예에서는 OPTION 절에 중첩 루프 조인을 지정합니다.

USE AdventureWorks2008R2;
GO
BEGIN TRAN;
MERGE Production.ProductInventory AS pi
USING (SELECT ProductID, SUM(OrderQty) 
       FROM Sales.SalesOrderDetail AS sod
       JOIN Sales.SalesOrderHeader AS soh
         ON sod.SalesOrderID = soh.SalesOrderID
         AND soh.OrderDate BETWEEN '20030701' AND '20030731'
       GROUP BY ProductID) AS src(ProductID, OrderQty)
ON (pi.ProductID = src.ProductID)
WHEN MATCHED AND pi.Quantity - src.OrderQty >= 0 
    THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty
WHEN MATCHED AND pi.Quantity - src.OrderQty <= 0 
    THEN DELETE
OUTPUT $action, Inserted.*, Deleted.*
OPTION (LOOP JOIN);
GO
ROLLBACK TRAN;


SELECT, INSERT, UPDATE 또는 DELETE 문이 매개 변수 없이 실행되는 경우 SQL Server 쿼리 최적화 프로그램은 내부적으로 문을 매개 변수화하는 방법을 선택할 수 있습니다. 즉, 쿼리에 포함된 모든 리터럴 값이 매개 변수로 대체됩니다. 예를 들어 INSERT dbo.MyTable (Col1, Col2) VALUES (1, 10) 문은 내부적으로 INSERT dbo.MyTable (Col1, Col2) VALUES (@p1, @p2)로 구현될 수 있습니다. 단순 매개 변수화라고 하는 이 프로세스는 새 SQL 문을 이전에 컴파일된 기존의 실행 계획과 비교하는 관계형 엔진의 기능을 개선합니다. 쿼리 컴파일 및 다시 컴파일 빈도가 낮아지므로 쿼리 성능이 개선될 수 있습니다. 쿼리 최적화 프로그램은 MERGE 문에 단순 매개 변수화 프로세스를 적용하지 않습니다. 따라서 리터럴 값을 포함하는 MERGE 문의 경우 MERGE 문이 실행될 때마다 새 계획이 컴파일되므로 개별 INSERT, UPDATE 또는 DELETE 문보다 성능이 떨어집니다.

쿼리 성능을 높이려면 다음 매개 변수화 지침을 따르는 것이 좋습니다.

  • ON <merge_search_condition> 절과 MERGE 문의 WHEN 절에 있는 모든 리터럴 값을 매개 변수화합니다. 예를 들어 리터럴 값을 적절한 입력 매개 변수로 대체하여 MERGE 문을 저장 프로시저에 통합할 수 있습니다.

  • 문을 매개 변수화할 수 없는 경우 TEMPLATE 형식의 계획 지침을 만들고 PARAMETERIZATION FORCED 쿼리 힌트를 이 계획 지침에 지정합니다. 자세한 내용은 계획 지침을 사용하여 쿼리 매개 변수화 동작 지정을 참조하십시오.

  • MERGE 문이 데이터베이스에서 자주 실행되는 경우 데이터베이스의 PARAMETERIZATION 옵션을 FORCED로 설정하는 것이 좋습니다. 이 옵션을 설정할 때는 신중해야 합니다. PARAMETERIZATION 옵션은 데이터베이스 수준 설정이므로 데이터베이스에 대한 모든 쿼리의 처리 방식에 영향을 미칩니다. 자세한 내용은 강제 매개 변수화를 참조하십시오.

MERGE 문에서 TOP 절은 원본 테이블과 대상 테이블이 조인되고 삽입, 업데이트 또는 삭제 동작에 적합하지 않은 행이 제거된 후에 영향을 받는 행의 개수나 비율을 지정합니다. TOP 절은 조인된 행 수를 지정된 값으로 더 줄이며, 삽입, 업데이트 또는 삭제 동작은 나머지 조인된 행에 순서 없이 적용됩니다. 즉, 행은 WHEN 절에 정의된 동작에 순서 없이 분산됩니다. 예를 들어 TOP (10)을 지정하면 10개 행이 영향을 받습니다. 이 10개의 행 중 7개가 업데이트되고 3개가 삽입되거나, 1개가 삭제되고 5개가 업데이트되고 4개가 삽입될 수 있습니다.

일반적으로 TOP 절을 사용하여 큰 테이블에서 일괄 처리로 DML(데이터 조작 언어) 작업을 수행합니다. MERGE 문에서 TOP 절을 이러한 용도로 사용하는 경우 다음 내용을 알고 있어야 합니다.

  • I/O 성능이 영향을 받을 수 있습니다.

    MERGE 문은 원본 테이블과 대상 테이블 모두에서 전체 테이블 검색을 수행합니다. 작업을 여러 일괄 처리로 나누면 일괄 처리 하나당 수행되는 쓰기 작업의 수를 줄일 수 있지만 각 일괄 처리에서는 원본 테이블과 대상 테이블에서 전체 테이블 검색을 수행합니다. 그 결과 읽기 작업이 쿼리의 성능에 영향을 줄 수 있습니다.

  • 잘못된 결과가 발생할 수 있습니다.

    연속된 모든 일괄 처리는 새로운 행을 대상으로 해야 합니다. 그렇지 않은 경우 대상 테이블에 중복 행을 잘못 삽입하는 등의 원하지 않는 동작이 발생할 수 있습니다. 이러한 현상은 대상 일괄 처리에는 없었지만 전체 대상 테이블에는 있었던 행이 원본 테이블에 포함되는 경우 발생할 수 있습니다.

    정확한 결과를 보장하려면

    • ON 절을 사용하여 기존 대상 행에 영향을 미치는 원본 행과 완전히 새로운 행을 확인합니다.

    • WHEN MATCHED 절에 추가 조건을 사용하여 이전 일괄 처리에 의해 대상 행이 이미 업데이트되었는지 여부를 확인합니다.

    TOP 절은 이러한 절이 적용된 후에 적용되므로 문을 실행할 때마다 하나의 완전히 일치하지 않는 행이 삽입되거나 하나의 기존 행이 업데이트됩니다. 다음 예에서는 원본 및 대상 테이블을 만든 다음 일괄 처리 작업에서 올바르게 TOP 절을 사용하여 대상을 수정하는 방법을 알려 줍니다.

    CREATE TABLE dbo.inventory(item_key int NOT NULL PRIMARY KEY, amount int, is_current bit);
    GO
    CREATE TABLE dbo.net_changes(item_key int NOT NULL PRIMARY KEY, amount int);
    GO
    
    MERGE TOP(1) dbo.inventory
    USING dbo.net_changes
    ON inventory.item_key = net_changes.item_key
    WHEN MATCHED AND inventory.is_current = 0
      THEN UPDATE SET amount += net_changes.amount, is_current = 1
    WHEN NOT MATCHED BY TARGET
      THEN INSERT (item_key, amount, is_current) VALUES(item_key, amount, 1)
    OUTPUT deleted.*, $action, inserted.*;
    GO
    
    
    

    다음 예에서는 잘못된 TOP 절 구현 방법을 보여 줍니다. is_current 열 확인이 원본 테이블과의 조인 조건에 지정됩니다. 즉, 하나의 일괄 처리에 사용된 원본 행이 다음 일괄 처리에 “일치하지 않는 항목”으로 처리되어 원하지 않는 삽입 작업이 수행될 수 있습니다.

    MERGE TOP(1) dbo.inventory
    USING dbo.net_changes
    ON inventory.item_key = net_changes.item_key AND inventory.is_current = 0
    WHEN MATCHED
      THEN UPDATE SET amount += net_changes.amount, is_current = 1
    WHEN NOT MATCHED BY TARGET
      THEN INSERT (item_key, amount, is_current) values(item_key, amount, 1)
    OUTPUT deleted.*, $action, inserted.*;
    GO
    
    
    

    다음 예에서도 잘못된 방법을 보여 줍니다. CTE(공통 테이블 식)를 사용하여 일괄 처리의 행 읽기 횟수를 제한하면 TOP(1)에 의해 선택된 행이 아닌 다른 대상 행에 일치하는 모든 원본 행이 “일치하지 않는 항목”으로 처리되어 원하지 않는 삽입 작업이 수행될 수 있습니다. 또한 이 방법은 업데이트할 수 있는 행의 수만 제한하므로 각 일괄 처리에서는 “일치하지 않는” 모든 원본 행의 삽입을 시도합니다.

    WITH target_batch AS (
      SELECT TOP(1) *
      FROM dbo.inventory
      WHERE is_current = 0
      )
    MERGE target_batch
    USING dbo.net_changes
    ON target_batch.item_key = net_changes.item_key
    WHEN MATCHED
      THEN UPDATE SET amount += net_changes.amount, is_current = 1
    WHEN NOT MATCHED BY TARGET
      THEN INSERT (item_key, amount, is_current) values(item_key, amount, 1)
    OUTPUT deleted.*, $action, inserted.*;
    GO
    
    
    

MERGE 문을 사용하여 OPENROWSET(BULK…) 절을 테이블 원본으로 지정하면 원본 데이터 파일의 데이터를 대상 테이블로 효율적으로 대량 로드할 수 있습니다. 이렇게 하면 전체 파일이 하나의 일괄 처리에서 처리됩니다.

대량 병합 프로세스의 성능을 높이려면 다음 지침을 따르는 것이 좋습니다.

  • 대상 테이블의 조인 열에 클러스터형 인덱스를 만듭니다.

  • OPENROWSET(BULK…) 절에 ORDER 및 UNIQUE 힌트를 사용하여 원본 데이터 파일의 정렬 방식을 지정합니다.

    기본적으로 대량 작업은 데이터 파일이 정렬되지 않았음을 전제로 합니다. 따라서 쿼리 최적화 프로그램이 보다 효율적인 쿼리 계획을 생성할 수 있도록 원본 데이터를 대상 테이블의 클러스터형 인덱스에 따라 정렬하고, ORDER 힌트를 사용하여 순서를 나타내야 합니다. 힌트는 런타임에 유효성이 검사됩니다. 데이터 스트림이 지정된 힌트를 따르지 않으면 오류가 발생합니다.

이러한 지침을 통해 조인 키의 고유성을 확보하고 원본 파일의 데이터 정렬 순서가 대상 테이블과 일치하도록 할 수 있습니다. 추가 정렬 작업이 필요 없고 불필요한 데이터 복사가 없으므로 쿼리 성능이 향상됩니다. 다음 예에서는 MERGE 문을 사용하여 플랫 파일 StockData.txt에서 대상 테이블 dbo.Stock으로 데이터를 대량 로드합니다. 대상 테이블의 StockName에 기본 키 제약 조건을 정의하면 원본 데이터와 조인하는 데 사용되는 열에 클러스터형 인덱스가 만들어집니다. ORDER 및 UNIQUE 힌트는 대상 테이블의 클러스터형 인덱스 키 열에 매핑되는 데이터 원본의 Stock 열에 적용됩니다.

이 예를 실행하려면 먼저 C:\SQLFiles\ 폴더에 'StockData.txt'라는 텍스트 파일을 만듭니다. 이 파일에는 쉼표로 구분된 두 개의 데이터 열이 있어야 합니다. 예를 들어 다음과 같은 데이터를 사용합니다.

Alpine mountain bike,100

Brake set,22

Cushion,5

그런 다음 C:\SQLFiles\ 폴더에 'BulkloadFormatFile.xml'이라는 xml 서식 파일을 만듭니다. 다음과 같은 정보를 사용합니다.

<?xml version="1.0"?>

<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<RECORD>

<FIELD ID="1" xsi:type="CharTerm" TERMINATOR="," MAX_LENGTH="25"/>

<FIELD ID="2" xsi:type="CharTerm" TERMINATOR="\r\n" MAX_LENGTH="5"/>

</RECORD>

<ROW>

<COLUMN SOURCE="1" NAME="Stock" xsi:type="SQLNVARCHAR"/>

<COLUMN SOURCE="2" NAME="Delta" xsi:type="SQLSMALLINT"/>

</ROW>

</BCPFORMAT>

USE AdventureWorks2008R2;
GO
CREATE TABLE dbo.Stock (StockName nvarchar(50) PRIMARY KEY, Qty int CHECK (Qty > 0));
GO
MERGE dbo.Stock AS s
USING OPENROWSET (
    BULK 'C:\SQLFiles\StockData.txt',
    FORMATFILE = 'C:\SQLFiles\BulkloadFormatFile.xml',
    ROWS_PER_BATCH = 15000,
    ORDER (Stock) UNIQUE) AS b
ON s.StockName = b.Stock
WHEN MATCHED AND (Qty + Delta = 0) THEN DELETE
WHEN MATCHED THEN UPDATE SET Qty += Delta
WHEN NOT MATCHED THEN INSERT VALUES (Stock, Delta);
GO


다음 기능을 사용하여 MERGE 문의 성능을 측정 및 진단할 수 있습니다.

  • sys.dm_exec_query_optimizer_info 동적 관리의 merge stmt 카운터를 사용하여 MERGE 문에 대한 쿼리 최적화 횟수를 반환할 수 있습니다.

  • sys.dm_exec_plan_attributes 동적 관리 함수의 merge_action_type 특성을 사용하여 MERGE 문의 결과로 사용되는 트리거 실행 계획의 유형을 반환할 수 있습니다.

  • SQL Trace를 사용하여 다른 DML(데이터 조작 언어) 문에 대해 사용하는 것과 동일한 방식으로 MERGE 문에 대한 문제 해결 데이터를 수집할 수 있습니다. 자세한 내용은 SQL Trace 소개를 참조하십시오.

이 정보가 도움이 되었습니까?  


'오라클' 카테고리의 다른 글

프로시져.INSERT 또는 UPDATE  (0) 2012.10.15
오라클 힌트 모음 (예제)  (0) 2012.08.31
오라클 procedure 정리  (0) 2012.08.30
오라클 FUNCTION() 생성 예제  (0) 2012.08.30
DB2 함수 정리.  (0) 2012.08.29
Posted by 사라링

오라클 procedure 정리

2012. 8. 30. 10:36

※ 스토어드 프로시져는 반드시 수정, 삭제전에 소스백업을 해놓고 수정, 삭제해야 한다.

 

PL/SQL (ProcedureLanguage/SQL) 정의

 

Oracle 에서 스토어드 프로시져를 개발할때 사용하는 언어(문법)입니다.

PL/SQL은 ANSI SQL 보다 확장된 SQL로써 좀더 다양한 처리를 구현할수 있습니다.

* PL/SQL과 스토어드 프로시져는 같은 의미로 해석함

 

스토어드 프로시져 사용목적

 

일반 SQL 실행

 

흐름 : 클라이언트에서 서버로 단일 쿼리를 날려 실행함

 

단점 : 여러개의 SQL를 실행시 실행할 SQL 갯수만큼 실행

 

스토어드 프로시져 SQL 실행

 

흐름 : 서버에 스토어드 프로시져를 먼저 생성후 클라이언트에서 호출하여 실행함

 

장점 : 여러개의 SQL을 실행시 하나의 스토어드 프로시져에 담아 컴파일하여 서버에 생성한후 한번만 호출하여 실행

 

※ 스토어드 프로시져 생성후 커밋처리를 해줘야 한다.

 

※ 스토어드 프로시져 생성시 정적쿼리에서 사용하는 테이블, 컬럼이 해당 DB에 존재하지 않으면 에러를 발생한다. 단 동적쿼리의 테이블과 컬럼은 체크하지 않는다.

 

※ 스토어드 프로시져 생성시 다른 스토어드 프로시져를 호출하는 명령이 있을때 해당 스토어드 프로시져가 현재 존재안하면 에러를 발생한다.

 

스토어드 프로시져 사용이점

 

1. 프로그램 의존성이 낮고 독립성이 높다.

 

PRO-C, SQC, 쉘스크립트에서 스토어드 프로시져 수정시 스토어드 프로시져만 컴파일하면 된다. PRO-C, SQC, 쉘스크립트 파일은 재컴파일을 안해도 된다.

 

오라클 스토어드 프로시저로 할수없는 SQL

 

DDL(CREATE TABLE, DROP TABLE, ALTER TABLE)

 

PL/SQL 사용의 장점?

 

 

변수를 선언하고 사용할수 있으며 조건문을 사용할수 있다.

예외처리도 가능하며 네트웍트랙픽도 감소시켜주며 프로그래개발을 모듈화할수 있다.

트랜잭션 로직으로 개발가능하다.

서버에 이미 저장되어있으므로 실행속도가 빠르다.

서버에 저장되어 있으므로 보안에 좋다.

 

PL/SQL 블록 구조

 

※ PL/SQL 블럭 내부에는 SQL명령문과 PL/SQL명령문이 포함되어있다.

 

DECLARE

 

--변수,상수 선언

 

BEGIN

 

--실행 가능 SQL문,PL/SQL문

 

EXCEPTION

 

--에러처리

 

END;

 

※ BEGIN과 END SECTION은 꼭 필수 부이다.

 

※ SP는 컴파일하기전에 사용된 컬럼과 테이블이 있는지 체크한후에 컴파일을

시작하며 존재하지 않는 컬럼,테이블이 있다면 에러를 발생한다.

 

PL/SQL 블록에서 사용못하는 SQL문

 

CREATE,GRANT

 

※ SELECT,UPDATE,INSERT,DELETE는 사용가능하다.

 

PL/SQL 에서 여러행을 리턴하는 방법

 

여러개의 리턴값을 넘길때 "var1 || '/' || var1" 와 같이 구분자를 넣어서 하나로 넘겨서 사용하면 됩니다.

사용예제

CREATE OR REPLACE FUNCTION FUN_EX

(

IN_VAR1 VARCHAR2,

IN_VAR2 VARCHAR2,

IN_VAR2 VARCHAR2) RETURN NUMBER IS

RTN_VAR VARCHAR2(100);

INTO1 VARCHAR2(10);

INTO2 VARCHAR2(10);

INTO3 VARCHAR2(10);

BEGIN

SELECT VAR1, VAR2, VAR3

INTO INTO1, INTO2, INTO3

FROM 테스트

WHERE 조건하나

AND 조건둘

AND.. .;

RTN_VAR := INTO1 || '/' || INTO2 || '/' || INTO3;

-- 구분자는 편의대로 정하시면 됩니다.

RETURN RTN_VAR;

EXCEPTION

WHEN NO_DATA_FOUND THEN

RETURN 0; -- check

WHEN OTHERS THEN

RETURN 0; -- check

END FUN_EX;

 

PL/SQL 데이타타입 종류

 

   v_hire      date;

 

    v_deptno    number(2) not null = 0;
    /* not null이 붙으면 꼭 초기화를 해야한다. */

 

    v_location  varchar2(13) := 'allanta';
    /* not null이 없으면 꼭 초기화를 하지 않아도 된다. */

    /* 가변길이 문자열 */

 

    v_name      char(4) := 'lee';

    /* 고정길이 문자열 */

 

    v_address   long;

    /* 32760 바이트를 저장할수는 문자열 */

 

    v_true      boolean;

    /* 진리값을 저장한다. */

 

    c_com       constant number :=1400;

    /* constant는 상이다. */

 

    v_age       emp.age%TYPE;

    v_age2      v_age%TYPE;
    /* %TYPE을 쓰는이유는 TABLE의 컬럼의 데이타형이 달라지거나 제어할 TABLE */
    /* 컬럼과 같게하기위해 매번 확인하야하는 번거로움을 없애기 위해 */

    /* 큰시스템에서는 프로시저형이 달라지면 다바꾸어야 하기때문에 테이블의 형을 참조

    /* 해서 사용하면 테이블구조가 바뀌어도 자동으로 바뀌게 좋다. 대신 제대로 형을 */

    /* 참조해야 한다.*/

    v_loc loc /* 구조화되지않은 큰데이타 저장 4기가바이트까지 저장 */

 

PL/SQL 블록 유형

 

Anonymouse 유형(매번 서버에 전송하여 컴파일하여 실행함)

 

[declare]

begin

--실행소스

[exception]

end;

 

Procedure 유형(초이 한번만 서버에 생성하여 컴파일하여 호출하여 실행함)

 

CREATE OR REPLACE Procedure name

is

begin

--실행소스

[exception]

end;

 

Function 유형(초이 한번만 서버에 생성하여 컴파일하여 호출하여 실행함)

 

CREATE OR REPLACE Function name

Return datatype

is

begin

--실행소스

[exception]

end;

 

Anonymouse PL/SQL 블록 유형

 

클라이언트에서 매번 PL/SQL블록을 만들어 서버에 전송하여 자동으로 컴파일되어 실행하는 방식으로 여러 SQL문을 하나의 PL/SQL블록으로 만들어 보내면 한번에 여러 SQL문실행이 가능하지만 매번 서버에서 컴파일되기때문에 PROCEDURE,FUNCTION유형보다 성능이 좋지 않다.

 

Anonymouse PL/SQL 사용예제

 

create table test
(
  a number,
  b date
);

 

select * from test;

 

/* select문에서 INTO절사용시 1개의 행(로우)만이 into절에 변수에 저장가능하다. */
Declare
  /* 2개의 선언 */
  a account.a_strday%TYPE;
  b account.a_in%TYPE;
begin
  /* a_strday,a_in의 값을 a,b에 넣어라 */
  select a_strday,a_in
  into a,b
  from account
  where a_type = '뮤직';
end;
/* select문에서 INTO절사용시 1개의 행(로우)만이 into절에 변수에 저장가능하다. */

 

 

/* 1개의 행의 컬럼값을 v_empno에 저장한후 test테이블에 대입한후 commit한다. */
Declare
  v_empno number;
  v_date  date := sysdate;
begin
  select 1
    into v_empno
  from dual;
  insert into test (a,b) values (v_empno,v_date);
  commit;
end;
/* 1개의 행의 컬럼값을 v_empno에 저장한후 test테이블에 대입한후 commit한다. */

 

 

/* PL/SQL UPDATE예제(UPDATE,DELETE는 다중행처리 가능) */
Declare
  v_sale number := 2000;
begin
  update test set a = v_sale;
  delete from test where a = v_sale;
  commit;
end;
/* PL/SQL UPDATE예제(UPDATE,DELETE는 다중행처리 가능) */

 

 

스토어드 프로시저 PL/SQL 블록 유형?

 

스토어드 프로시저란 어떤 업무를 수행 하기 위한 절차를 뜻하며 반복되는 코딩을

모듈화 하기 위하여 함수나 프로시져를 사용합니다.

 

보통 DB에서는 데이타를 조회하여 가져오고 그 데이타를 받는 언어쪽에서는 데이타를

가공하여 화면에 출력을 합니다. 하지만 DB에서 데이타조회와 가공까지 하면 좀더 빠르게

화면 출력이 되며 서버부하를 줄일수 있습니다.

 

ORACLE의 PROCEDURE을 이용하면 서버부하를 줄이며 좀더 빠른 화면출력이 되며 여러

쿼리들을 하나의 프로시저,함수로 만들어 한번에 실행시킬수 있습니다.

 

예를 들어 인터넷을 통해 극장표를 구매하는 프로시저로 표현 한다면

 

[극장표 구매 Procedure 시작 ]

1. 예매 사이트에 접속 한다.
2. 예매할 영화 선택 한다.
3. 예매 일자와 시간을 선택 한다.
4. 예매 매수를 선택 한다.
5. 위 과정이 모두 끝났으면 결재를 한다.

[극장표 구매 Procedure 종료 ]

 

위와 같이 어떤 프로세스 절차를 기술해 놓을것을 프러시저 라고 합니다.

 

Procedure에서 Procedure를 호출하는 방법

 

A프로시져에서 "프로시져명(변수, 변수2);" 명령으로 B프로시져를 호출할수 있다.

 

PROCEDURE and FUNCTION 의 차이점?

 

프로시저와 펀션 둘다 파라미터를 받고 리턴값을 지정할수있으나 가장 큰 차이점은

function은 반드시 리턴값을 하나만 가져야 하지만 procedure는 여러개의 리턴값을 가질수 있습니다.

 

생성된 PROCEDURE & FUNCTION 리스트보기

 

SELECT * FROM USER_OBJECTS WHERE OBJECT_TYPE='PROCEDURE';

SELECT * FROM USER_OBJECTS WHERE OBJECT_TYPE='FUNCTION';

 

PROCEDURE & FUNCTION 삭제하기

 

DROP FUNCTION lee2;

DROP PROCEDURE lee2;

 

※ 삭제시 아래 메세지가 출력되는 경우

ORA-04021: 객체 KMS.KM_GET_KNOWLEDGE_LIST_PAGE의 잠금 대기중 시간이

초과됐습니다.

library lock 이 발생하여 해당 procedure 를 사용중이거나, 머 library lock 이 발생하면

해당 package, procedure, function 에 대한 작업을 할수 없습니다.

lock 관련 체크 script 를 통해서 체크 해보세요.

 

생성된 Procedure & Function의 내용보기

 

select * from user_source;

select * from user_source where name='DATETOSTRDAY';

또는

SELECT * FROM ALL_SOURCE WHERE OWNER = '소유자' AND NAME = '프로시져명'

 

※ MSSQL, MySQL, DB2도 특정 테이블에 프로시져와 펑션의 내용이 들어있습니다.

 

PROCEDURE & FUNCTION 실행하기

 

EXECUTE lee;

--PROCEDURE 실행하기EXEC lee;

--PROCEDURE 실행하기 예제 select function_name(column) from table_name;

 

PROCEDURE & FUNCTION 기본 문법

 

create or replace procedure lee
--파라미터 선언부

is
--변수 선언부
begin
--실행부(쿼리적용)
end lee;

 

create procedure lee
--파라미터 선언부

is
--변수 선언부
begin
--실행부(쿼리적용)
end lee;

 

LOOP문  :LOOP와 END LOOP사이의 문장을 반복 수행하며 BREAK문으로 빠져나감

WHILE문 : 제어 조건이 TRUE가 아닐 때까지 문장을 반복수행

 

PROCEDURE 예제

 

※ 주의할점

1) END; 와 END 프로시저&함수명; 은 같다. 보통 안쓰는게 편합니다.

2) CREATE OR replace PROCEDURE LEE 와 CREATE PROCEDURE LEE의 차이는 전자는 같은 프로시저 이름으로 계속 생성해도 새롭게 내용이 갱신되고 후자는 같은 프로시저명이 있으면 에러를 내고 실행을 멈춥니다.

3)프로시저나 함수 생성시에 에러가 나면 sqlplus에서 show error을 쳐서 에러내용을 확인합니다.

 

숫자를 받은후 UPDATE쿼리를 합니다.

 

CREATE OR REPLACE PROCEDURE LEE1
(v_empno IN NUMBER) /* 정수형 파라미터를 v_empno변수로 받는다. */
IS
BEGIN
UPDATE emp SET sal = sal  * 1.1
WHERE empno = v_empno;/* query runtime */
COMMIT; /* update query commit */
END LEE1;
/
execute lee1(7369);

 

숫자를 받은후 UPDATE쿼리를 합니다.

 

CREATE OR REPLACE PROCEDURE LEE2
(v_empno in emp.sal%type)

/* v_empno변수에 파라미터로 받고 v_empno type은 emp테이블의 sal컬럼과 같은 타입이다. */
IS
BEGIN
UPDATE emp SET sal = sal  * 1.1
WHERE empno = v_empno;/* query runtime */
COMMIT; /* update query commit */
END LEE2;
/

execute lee1(7369);

 

list 테이블에서 사번을 입력받아 COMMISSION_PCT 값을 수정하시오.

COMMISSION_PCT 는 영업사원이 담당한 
주문(s_ord)  총금액(s_ord.total)으로 결정한다.
update_comm 라는  procedure 작성하시오.
a. 총금액이 100,000 미만이면 COMMISSION_PCT는 10
b. 총금액이 100,000 이상이고 1,000,000 미만이면 15
c. 총금액이 1,000,000 이상이면 20
d. NULL이면 0

 

create table list
(
id number,
money number,
comm number
);

 

create or replace procedure update_comm
(a_id in list.id%type)
is
v_total      list.money%type;
v_comm       list.comm%type;
begin
    select sum(money)
     into   v_total
    from   list
    where id = a_id;
    if   v_total < 100000 then
         v_comm := 10;
    elsif v_total < 1000000 then
         v_comm := 15;
    elsif v_total >= 1000000 then
         v_comm := 20;
    else
         v_comm := null;
    end if;
    dbms_output.put_line('사번      : '||to_char(a_id));
    dbms_output.put_line('TOTAL : '||to_char(v_total, '999,999,999'));
    dbms_output.put_line('커미션   : '||to_char(v_comm,'999.99'));
    update list
    set comm = v_comm
    where id = a_id;
    commit;
end;
/

execute update_comm(1);

 

FUNCTION 예제

 

사원번호를 입력받아 사원의 월급을 출력합니다.

 

CREATE OR REPLACE FUNCTION lee3
(id IN NUMBER)
return number
IS
value1 NUMBER;

BEGIN
select sal into value1 from emp where empno=id;
return(value1);
END;
/

select lee3(empno) from emp;

 

부서번호을 입력받아 부서명을 return 해주는 함수를 만들자.

함수명 : f_deptname
인자    : id(s_dept.id%type)
리턴값 : varchar2

사원정보에 대해서 
사번(id), 이름(last_name), 부서번호, 부서명을 조회하자.

 

create or replace function f_deptname
(a_id in dept.deptno%type)
return  varchar2
is
  v_dname      dept.dname%type;
begin
   select dname
     into v_dname
  from dept
 where  deptno= a_id;
 return(v_dname);
end;
/
select f_deptname(deptno) from dept;

 

S_EMP 테이블에서 사번을 입력받아 해당 사원의 급여에 따른 
세금을 구하시오. 
급여가 1000 미만이면 급여의 5%, 급여가 2000 미만이면 7%, 
급여가 3000 미만이면 9%,  그 이상은 12%로   세금을 정한다.

 

create or replace function f_tax

/*
정수형 파라미터 설정
(id in emp.empno%type)
*/
(id in number)
return  number

/* 변수선언 */
is
v_sal      NUMBER;
/* v_sal      s_emp.salary%type; */
v_tax      NUMBER;

/* 함수 실행문 */
begin
    select sal
     into   v_sal
    from emp
    where empno = id;

    if   v_sal < 1000 then
         v_tax := v_sal * 0.05;
    elsif v_sal < 2000 then
         v_tax := v_sal * 0.07;
    elsif v_sal < 3000 then
         v_tax := v_sal * 0.09;
    else
         v_tax := v_sal * 0.12;
    end if;
    return(v_tax);

end;
/
select f_tax(empno) from emp;

 

/*
함수명:숫자형식요일리턴함수
함수설명:날짜를 입력받아 요일을 숫자로 리턴하는 함수
함수작성자:이 준식
*/


create or replace FUNCTION DateToNumDay(DateValue IN date)/*받는 변수*/
return number/*리턴 데이타형*/
IS
ShowDay number;/*숫자변수 선언*/
BEGIN
/**********************************/
SELECT decode(to_char(DateValue,'D'),
              '2','1',
              '3','2',
              '4','3',
              '5','4',
              '6','5',
              '7','6',
              '1','7') "요일숫자" into ShowDay
from   dual;
/**********************************/
return(ShowDay);/*해당 변수를 리턴*/

end;

/

 

/*
함수명:문자형식요일리턴함수
함수설명:숫자요일을 입력받아 문자로 리턴하는 함수
함수작성자:이 준식
*/

 

create or replace FUNCTION NumdayToStrDay(NumValue IN number)/*받는 변수*/
return varchar2/*리턴 데이타형*/
IS
StrValue varchar2(10);/*문자변수 선언*/
BEGIN
/**********************************/
SELECT decode((NumValue),
              1,'월요일',
              2,'화요일',
              3,'수요일',
              4,'목요일',
              5,'금요일',
              6,'토요일',
              7,'일요일') "요일" into StrValue
from   dual;
/**********************************/
return(StrValue);/*해당 변수를 리턴*/

end;
/

 

/*
함수명:문자형식요일리턴함수
함수설명:날짜를 입력받아 요일을 문자로 리턴하는 함수
함수작성자:이 준식
*/

 

create or replace FUNCTION DateToStrDay(DateValue IN date)/*받는 변수*/
return varchar/*리턴 데이타형*/
IS
ShowDay varchar(10);/*숫자변수 선언*/
BEGIN
/**********************************/
SELECT decode(to_char(DateValue,'D'),
              '1','일요일',
              '2','월요일',
              '3','화요일',
              '4','수요일',
              '5','목요일',
              '6','금요일',
              '7','토요일') "요일" into ShowDay
from   dual;
/**********************************/
return(ShowDay);/*해당 변수를 리턴*/

end;

/

 

/*
함수명:숫자형식콤마문자리턴함수
함수설명:숫자를 입력받아 콤마처리를 문자로 리턴하는 함수
함수작성자:이 준식
*/

 

create or replace FUNCTION IntToComma(IntValue IN number)/*받는 변수*/
return varchar/*리턴 데이타형*/
IS
ShowDay varchar(20);/*문자변수 선언*/
BEGIN
/**********************************/

 

SELECT replace(to_char(IntValue,'999,999,999,999'),' ','') into ShowDay

from   dual;
/*SELECT trim(to_char(IntValue,'999,999,999,999')) into ShowDay*/

 

/**********************************/
return(ShowDay);/*해당 변수를 리턴*/
end;
/

 

/*
함수명:문자형식 숫자,문자,null 체크 출력
함수설명:문자형식을 숫자인지 문자와 숫자인지 null인지 알려준다.
함수작성자:이 준식
*/

 

create or replace function NumCheck(asString varchar2)
return varchar2
is
 nTemp number(1);
begin
 select sign(asString)
 into nTemp
 from dual;

 if nTemp is null then
  return 'null';
 end if;
 
 return 'number';
exception
 when others then
  return 'string';
end;
/

 

/*
함수명:출력컬럼명을 보내서 해당날짜에 그컬럼값을 출력한다.

함수작성자:이 준식
*/

 

create or replace function test_01(param1 IN date,param2 IN VARCHAR2)
 return varchar2
is
 ls_code varchar2(20);
BEGIN
 SELECT decode(param2,'a_type',a_type) INTO ls_code from ACCOUNT where A_DATE = param1;
 return ls_code;
/* 정상적으로 실행되었으나 조회갯수가 0개면 발생 */
exception when no_data_found then
 return '데이타가 없습니다.';
/* return null; */
END test_01;
/

 

select test_01(sysdate) from dual;
select test_01(to_date('2005-10-18','yyyy-mm-dd'),'a_type') from dual;

 

create or replace function test_01(param1 IN varchar2)
 return varchar2
is
 ls_code varchar2(20);
BEGIN
 SELECT A_TYPE INTO ls_code from ACCOUNT where A_DATE = to_date(param1,'yyyy-mm-dd');
 return ls_code;
/* 정상적으로 실행되었으나 조회갯수가 0개면 발생 */
exception when no_data_found then
 return '데이타가 없습니다.';
/* return null; */
END test_01;
/

 

select test_01(sysdate) from dual;
select test_01('2005-10-18') from dual;

 

프로시져는 SQL문에서 호출할수 없지만 함수는 SQL문에서 호출할수 있다.

 

PL/SQC 자동실행 방법

 

윈도우의 경우

보조프로그램 > 시스템도구 > 예약된 작업에서 .bat 파일을 만든후 등록함

 

유닉스의 경우

쉘스크립트 파일을 만든후 크론에 등록하거나 nohup으로 실행

 

오라클의 기능을 이용한 경우

dbms_job 패키지를 이용한다.

'오라클' 카테고리의 다른 글

오라클 힌트 모음 (예제)  (0) 2012.08.31
BYPASS_UJVC -> MERGE 문으로 변경 하자.  (0) 2012.08.31
오라클 FUNCTION() 생성 예제  (0) 2012.08.30
DB2 함수 정리.  (0) 2012.08.29
ORDERY BY 절 사용  (0) 2012.08.29
Posted by 사라링

 프로젝트 참여 과정중 예제로 오라클 프로시져를 만들 일이 생겨서 간단히 summery  해 두겠습니다 ^^

/* 예제 테이블 생성*/
create table exam1 (
yoil varchar2(1),
gyosi varchar2(2),
cnt varchar2(2),
gyo_name varchar2(50),
gang_dongsil varchar2(12),
judamdang varchar2(20),
gyo_code varchar2(10),
gangjwa_code varchar2(20))

/* 예제 데이터 집어 넣기 */
/*insert data*/
insert into exam1 values ('1', '13', '6' , '유통기관경영론' , '(059-610)', '' ,'251.563', '');
insert into exam1 values ('1', '14', '6' , '유통기관경영론' , '(059-610)', '' ,'251.563', '');
insert into exam1 values ('1', '15', '6' , '유통기관경영론' , '(059-610)', '' ,'251.563', '');
insert into exam1 values ('1', '16', '6' , '유통기관경영론' , '(059-610)', '' ,'251.563', '');
insert into exam1 values ('1', '17', '6' , '유통기관경영론' , '(059-610)', '' ,'251.563', '');
insert into exam1 values ('1', '18', '6' , '유통기관경영론' , '(059-610)', '' ,'251.563', '');
insert into exam1 values ('1', '23', '6' , '행정법일반이론' , '(017-105)', '' ,'270.721', '');
insert into exam1 values ('1', '24', '6' , '행정법일반이론' , '(017-105)', '' ,'270.721', '');
insert into exam1 values ('1', '25', '6' , '행정법일반이론' , '(017-105)', '' ,'270.721', '');
insert into exam1 values ('1', '26', '6' , '행정법일반이론' , '(017-105)', '' ,'270.721', '');
insert into exam1 values ('2', '16', '6' , '러시아예술과 문화' , '(003-204)', '' ,'100.124', '');
insert into exam1 values ('2', '17', '6' , '러시아예술과 문화' , '(003-204)', '' ,'100.124', '');
insert into exam1 values ('2', '18', '6' , '러시아예술과 문화' , '(003-204)', '' ,'100.124', '');
insert into exam1 values ('2', '19', '6' , '러시아예술과 문화' , '(003-204)', '' ,'100.124', '');
insert into exam1 values ('2', '20', '6' , '러시아예술과 문화' , '(003-204)', '' ,'100.124', '');
insert into exam1 values ('2', '21', '6' , '러시아예술과 문화' , '(003-204)', '' ,'100.124', '');

다음과 같이 예제 테이블과 예제 데이터를 만들었으면, select 를 해보겠습니다.
/* 일반적인 SELECT 문 */
select
    yoil, gyo_name, gang_dongsil, judamdang, gyo_code, gangjwa_code
from
    exam1

다음과 같은 결과 값이 나왔습니다.


그런데 문제는 요일에 따른 교시를 나타내고 싶습니다.

1 | 13, 14, 15, 16, 17 | 유통기관경영론 | (059-610) | .....

이렇게 데이터를 만들고 싶을때!



UDF : 사용자 FUNCTION 을 만듭니다. Procedure 라고도 부르는것 같군요

/*user function */
CREATE OR REPLACE FUNCTION GET_ALL_PROJECTp_gyo_code IN varchar) RETURN VARCHAR2
IS
RESULT_STR VARCHAR2 (200);
BEGIN
  SELECT SUBSTR(MAX( SYS_CONNECT_BY_PATH (gyosi,'|')),2) INTO RESULT_STR
  FROM (
    SELECT gyosi, ROWNUM rnum
    FROM EXAM1
    WHERE exam1.gyo_code = p_gyo_code
  )
  START WITH rnum = 1
  CONNECT BY PRIOR rnum = rnum -1; 
  RETURN RESULT_STR;
END GET_ALL_PROJECT;
/

GET_ALL_PROJECT : 함수 이름
p_gyo_code  :  p_gyo_code : 프로시져 안에서 쓰일 변수 
IN varchar : 파라미터  타입
RESULT_STR : 리턴값
UDF 생성후 쿼리를 다시 한번 날리겠습니다.
select
    yoil, gyo_name, gang_dongsil, judamdang, gyo_code, gangjwa_code, count(gyosi) as gyosi_cnt , GET_ALL_PROJECT(gyo_code)
from
    exam1
group by
    yoil, gyo_name, gang_dongsil, judamdang, gyo_code, gangjwa_code ;


다음과 같은 결과 화면을 얻을 수 있습니다 ^^



어려우면서도 쉬운것 같은 UDF 네요 - - 히힛-

'오라클' 카테고리의 다른 글

BYPASS_UJVC -> MERGE 문으로 변경 하자.  (0) 2012.08.31
오라클 procedure 정리  (0) 2012.08.30
DB2 함수 정리.  (0) 2012.08.29
ORDERY BY 절 사용  (0) 2012.08.29
최상위 코드와 그 최상위 코드를 대상으로 그룹 .  (0) 2012.08.24
Posted by 사라링

DB2 함수 정리.

2012. 8. 29. 17:44


기본정보보기
DESCRIBE TABLE table-name [show detail]
DESCRIBE SELECT * FROM tablename;

 

인덱스 정보보기 
DESCRIBE INDEXES FOR TABLE table-name [show detail]


등록 테이블 리스트 보기
LIST  TABLES  FOR  ALL ;

 

- LOCK 상태 확인
GET SNAPSHOT FOR LOCKS ON depsdb
  
유지되는 잠금현재 잠금대기중인 에이전트응용프로그램명
  
응용프로그램 상태총대기시간모드상태 등을 확인


SELECT * FROM staff FETCH FIRST 5 ROWS ONLY
라고 하면 하면 처음 5개의 row가 나옵니다.

 

SELECT bus_mkt_id, svc_mgmt_num, svc_cd, svc_num, cell_equip_modl_cd, line_num 
  FROM coispc.vcell_num 
 WHERE svc_mgmt_num = ? 
 ORDER BY eff_dt_tm desc FETCH FIRST 1 ROWS ONLY
  WITH UR


SELECT INTEGER(SUBSTR(CHAR(CURRENT DATE),1,1)||SUBSTR(CHAR(CURRENT DATE),3,2)||SUBSTR(CHAR(CURRENT DATE),6,2)||SUBSTR(CHAR(CURRENT DATE),9,2)),
       INTEGER(SUBSTR(CHAR(CURRENT TIMESTAMP),12, 2) || SUBSTR(CHAR(CURRENT TIMESTAMP),15,2) || SUBSTR(CHAR(CURRENT TIMESTAMP),18,2) || SUBSTR(CHAR(CURRENT TIMESTAMP),21,1))
FROM SYSIBM.SYSDUMMY1
WITH UR


CREATE FUNCTION month_between (p_start date, p_end date)
RETURNS SMALLINT
BEGIN atomic
      DECLARE v_year_diff  SMALLINT DEFAULT 0;
      DECLARE v_month_diff SMALLINT DEFAULT 0;
      DECLARE v_diff       SMALLINT DEFAULT 0;
             
      SET v_year_diff  = YEAR(p_start) - YEAR(p_end);
      SET v_month_diff = MONTH(p_start) - MONTH(p_end);
 
      IF v_year_diff != 0 THEN
         set v_diff = v_year_diff * 12;
      END if;
 
      SET v_diff = v_diff + v_month_diff;
 
      RETURN v_diff;
END@


@@@ Oracle
 Decode기능
ex1)
SELECT  rownumber,CASE WHEN  zip_code BETWEEN '100091' AND '100091' THEN '91'
                WHEN  zip_code BETWEEN '100092' AND '100092' THEN '92'
                WHEN  zip_code BETWEEN '100093' AND '100093' THEN '93'
                WHEN  zip_code BETWEEN '100094' AND '100094' THEN '94'
                WHEN  zip_code BETWEEN '100095' AND '100095' THEN '95'
             ELSE   '99'
          END
  FROM ( 
        SELECT zip_code,ROWNUMBER() OVER (ORDER BY zip_code) AS rownumber
          FROM zipcode
       ) AS t 
 WHERE ROWNUMBER  BETWEEN 20 AND 30

 

ex2)
SELECT  ROWNUMBER,CASE zip_code 
                  WHEN  '100091' THEN '91'
                  WHEN  '100092' THEN '92'
                  WHEN  '100093' THEN '93'
                  WHEN  '100094' THEN '94'
                  WHEN  '100095' THEN '95'
                  ELSE   '99'
                  END  
  FROM (                       
        SELECT  zip_code,ROWNUMBER() OVER (ORDER BY zip_code) AS rownumber
          FROM zipcode
       ) AS t 
 WHERE ROWNUMBER  BETWEEN 20 AND 30

 

 

@@@ INTEGER형으로 변환

ex)
SELECT INTEGER(zip_code)
  FROM zipcode
 FETCH FIRST 5 ROWS ONLY

 

 

@@@ DOUBLE형으로 변환

ex)
SELECT DOUBLE(zip_code)
  FROM zipcode
 FETCH FIRST 5 ROWS ONLY

 

 

@@@ SUBSTR

ex)
SELECT SUBSTR(zip_code,1,3)
  FROM zipcode
 FETCH FIRST 5 ROWS ONLY

 

 

@@@ CHAR

ex)
SELECT CHAR(doseo)
  FROM zipcode
 FETCH FIRST 5 ROWS ONLY

 

 

@@@ COALESCE - Oracle Nvl()기능 
컬럼 타입에 따라 인수를 결정한다. COALESCE(자형,문자형표시)  COALESCE(숫자형,숫자형표시)

ex)
SELECT COALESCE(doseo,'1')
  FROM zipcode
 FETCH FIRST 5 ROWS ONLY

 

 

@@@ ||
문자연결기능
SELECT COALESCE(doseo,'1') || zip_code
  FROM zipcode
 FETCH FIRST 5 ROWS ONLY

 

 

@@@ page기능
SELECT t.zip_code,page
FROM (
       SELECT zip_code,((ROWNUMBER() OVER() -1)/ 10+1) AS page
         FROM zipcode
     ) AS t
 WHERE t.page = 3
 FETCH FIRST 100 ROWS ONLY
  WITH UR

 

 

@@@ year 구하기
ex1)
SELECT  YEAR(CURRENT TIMESTAMP)
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

ex2)
SELECT  YEAR('2004-05-16')
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

ex3)
SELECT  YEAR(CURRENT DATE)
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ month 구하기

ex1)
SELECT  MONTH(CURRENT TIMESTAMP)
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

ex2)
SELECT  MONTH('2004-08-16')
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ day 구하기

ex1)
SELECT  DAY(CURRENT TIMESTAMP)
  FROM SYSIBM.SYSDUMMY1
  WITH UR
EX2)
SELECT  DAY('2004-08-16')
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ week 구하기
ex)
SELECT  WEEK('2004-05-16')
  FROM SYSIBM.SYSDUMMY1
  WITH UR

@@@ time 구하기

ex)
SELECT  CURRENT TIME
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ dayofyear 구하기(오늘이 365일중 몇번째 날짜)
ex)
SELECT  DAYOFYEAR(CURRENT DATE)
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ dayname 구하기(요일 이름)

ex)
SELECT  DAYNAME(CURRENT DATE)
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ CONCAT 문자연결함수

ex)
SELECT  CONCAT('111','22222 ')
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ MOD 나머지 함수

ex)
SELECT  MOD(11111,100)
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ value 함수 - COALESCE와 동일한 기능

ex)
SELECT  VALUE(CURRENT DATE,'2004-08-16')
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ abs 함수 절대값 함수

ex)
SELECT  ABS(-51234)
  FROM SYSIBM.SYSDUMMY1
  WITH UR

 

 

@@@ lcas 함수 대문자를 소문자로

ex)
SELECT  LCASE('ABCD')
  FROM SYSIBM.SYSDUMMY1
  WITH UR;

 

 

@@@ ucase 함수 소문자를 대문자로
ex)
SELECT  LCASE('abcd')
  FROM SYSIBM.SYSDUMMY1
  WITH UR;

 

 

@@@ multiply_alt 두 수를 곱한다.
ex)
SELECT MULTIPLY_ALT(3,20)
  FROM SYSIBM.SYSDUMMY1
  WITH UR;

 

 

@@@ round
ex)
SELECT  ROUND(873.726, 2), ROUND(873.726, 1), ROUND(873.726, 0), ROUND(873.726,-1),

        ROUND(873.726,-2), ROUND(873.726,-3), ROUND(873.726,-4)
  FROM SYSIBM.SYSDUMMY1
  WITH UR;

 

 

@@@ week_iso 함수
ex)
SELECT  WEEK_ISO(CURRENT DATE)     SELECT  WEEK_ISO('1997-12-28')
  FROM SYSIBM.SYSDUMMY1              FROM SYSIBM.SYSDUMMY1         
  WITH UR;                           WITH UR;                     

 

 

@@@ dayofweek_iso 해당주에서 몇일에 해당하는지
ex)
SELECT   DAYOFWEEK_ISO(CURRENT DATE)    SELECT   DAYOFWEEK_ISO('2004-08-16')
  FROM SYSIBM.SYSDUMMY1                   FROM SYSIBM.SYSDUMMY1               
  WITH UR;                                WITH UR;                           

 

SELECT  callback_dt,                                           
        callback_tm,                                          
        COUNT(seqno),                                         
        COUNT(custname),                                      
        telno_1||'-'|| telno_2||'-'|| telno_3  AS tel_number  
  FROM callback
 GROUP BY  callback_dt,callback_tm,telno_1||'-'|| telno_2||'-'|| telno_3
FETCH FIRST 5 ROWS ONLY;


SELECT  *  FROM 
(  SELECT  ROWNUMBER() OVER() AS rownum,statement_text
   FROM  explain_statement
) AS t
WHERE t.rownum = 2
FETCH  FIRST 100  ROWS  ONLY

 

 

@@@ outer join
SELECT CASE  WHEN a.relation = '1' THEN '본인'
             WHEN a.relation = '2' THEN '
배우자'  
             WHEN a.relation = '3' THEN '
자녀'   
             WHEN a.relation = '4' THEN '
부모'  
             WHEN a.relation = '5' THEN '
형제자매'  
             WHEN a.relation = '6' THEN '
기타'  
             ELSE '
기타
        END AS kwan,a.name,a.fsocial_no  AS  fsocial_no,  
 CASE SUBSTR(a.fsocial_no,7,1)  
      WHEN '1' THEN YEAR(CURRENT DATE - DATE('19'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
      WHEN '2' THEN YEAR(CURRENT DATE - DATE('19'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
      WHEN '3' THEN YEAR(CURRENT DATE - DATE('20'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
      WHEN '4' THEN YEAR(CURRENT DATE - DATE('20'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
      WHEN '5' THEN YEAR(CURRENT DATE - DATE('19'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
      WHEN '6' THEN YEAR(CURRENT DATE - DATE('19'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
      WHEN '7' THEN YEAR(CURRENT DATE - DATE('20'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
      WHEN '8' THEN YEAR(CURRENT DATE - DATE('20'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
      WHEN '9' THEN YEAR(CURRENT DATE - DATE('18'||SUBSTR(A.fsocial_no,1,2)||'-'||SUBSTR(A.fsocial_no,3,2)||'-'||'01'))  
  ELSE YEAR(CURRENT DATE - DATE('18'||SUBSTR(a.fsocial_no,1,2)||'-'||SUBSTR(a.fsocial_no,3,2)||'-'||'01'))
         END  AS  YEARS,
  VALUE(b.company_nm,'') AS COMPANY_NM,
  VALUE(b.dept,'') AS DEPT,
  VALUE(b.duty,'') AS DUTY,  
  VALUE(b.offi_tel_1,'') || VALUE(b.offi_tel_2,'') || VALUE(b.offi_tel_3,'') AS offi_tel,  
  CASE WHEN a.live_yn = '1' THEN '
동거
       ELSE '' 
  END AS home  
 FROM cust.family_info A LEFT OUTER JOIN euc20.customer b ON ( a.fsocial_no =  b.social_no )  
WHERE a.social_no = '6611211010815' 
ORDER BY  fsocial_no

Posted by 사라링

ORDERY BY 절 사용

2012. 8. 29. 11:27

1. 사용방법

 

ORDER BY [ASC|DESC]

--QUERY문의 맨뒤에 옵니다.

EX)

SELECT * FROM EMP ORDER BY JOB

-- JOB를 오름차순(어샌딩)으로 정렬하여 보여준다.

SELECT * FROM EMP ORDER BY JOB ASC

-- JOB를 오름차순(어샌딩)으로 정렬하여 보여준다.

SELECT * FROM EMP ORDER BY JOB DESC

-- JOB를 내림차순(디샌딩)으로 정렬하여 보여준다.

select empno,ename from emp order by empno,ename;

-- sal컬럼을 기준으로 오름차순으로 정렬하고 sal컬럼의 값이 같은 레코드는 다시

-- ename컬럼을 기준으로 오름차순으로 정렬하여 출력합니다.

 

※ 디폴트는 ASC 입니다.

 

※ 참고사항

오름차순:정순정렬:ASC

내림차순:역순정렬:DESC

 

2. 데이타형에 따른 정렬

 

문자열은 ASC면 A,B,C순으로 ASC코드값이 작은것부터이며

DESC면 C,B,A순으로 ASC코드값이 큰것부터나옵니다.

 

정수나 실수는 ASC면 전체수가 작은것부터이며

DESC면 전체수가 큰것부터나옵니다.

 

날짜는 ASC면 오래된 날짜부터 정렬되며

DESC면 최근의 날짜부터 정렬됩니다.

 

한글은 ASC면 가,나,다.... 순으로 정렬되며

DESC면 ....다,나,가 순으로 정렬된다.

 

NULL값은 연산이나 비교도 안됩니다. 하지만 정렬시에는 NULL값은 ASC면

무조건 맨뒤에 나오며 DESC면 무조건 앞에 나옵니다.

 

※ 무조건 맨뒤나 앞에 나올때의 널값은 순서가 입력순서는 순서대로입니다.

 

※ ASC일때 NULL 값을 맨앞에 하는 방법이 있습니다.

ex : order by name nulls first

ex : order by nvl(name,' ')

 

※ DESC일때 NULL 값을 맨뒤에 하는 방법이 있습니다.

ex : order by name nulls last

 

3. 특별한 사용법

 

※ ORDER BY는 ASC, DESC 키워드를 입력하지 않으면 자동으로 오름차순 정렬됩니다.

※ ORDER BY 뒤에 컬럼명을 , 간격으로 2개이상을 사용가능합니다.

 

ex : select * from table_name order by a,b;

* a를 오름차순으로 정렬한상태에서 a의 값이 중복인 레코드는 다시 b를 오름차순으로 정렬한다.

* a의 값이 중복값이 없을때는 b는 오름차순하지 않는다.

 

ORDER BY 뒤에 숫자가 나오면 현재 TABLE의 컬럼위치를 말합니다.

select * from emp order by 1 DESC;면 TABLE의 첫번째 컬럼으로 내림차순으로 정렬한다는 뜻입니다.

 

※ 0은 사용할수 없다.

 

ORDER BY 뒤에 앨리어스(별명)을 두어 정렬이 됩니다.

SELECT A AA,B FROM ORDER BY AA;

 

select name from goods order by 1;
-- 제대로된 query
-- 첫번째 출력컬럼으로 오름차순 정렬
select name from goods order by 0;
-- 잘못된 query
-- 0 이란 숫자는 order by사용할수 없습니다.

 

IN 연산자에 사용한 문자열 값들을 기준으로 원하는 순서로 출력하는 방법

 

SELECT * FROM TABLE_NAME WHERE ID IN('084', '081', '079', '077', '127')

ORDER BY DECODE(ID, '084', 1, '081', 2, '079', 3, '077', 4, '127', 5);

 

※ DECODE 함수로 원하는 값에 우선순위를 줄수 있습니다.

 

조회조건없는 쿼리 실행시 출력 정렬상태

 

SELECT IDX, NAME, AGE FROM TABLE_NAME; 이라고 할때 입력순서대로 출력되며

IDX가 PK라면 IDX를 기준으로 오름차순으로 정렬됩니다.

100% 입력된 자료 순서라고 장담할수 없습니다. 
별다른 실행계획이 없이 풀스켄 했다면 
rowid 순서로 조회가 됩니다. 
보통은 입력 순서대로 되지만 삭제작업이 빈번하게 일어나는 경우 나중에 입력된 자료가 먼저 나오기도 하는 것으로 알고 있습니다.. 

'오라클' 카테고리의 다른 글

오라클 FUNCTION() 생성 예제  (0) 2012.08.30
DB2 함수 정리.  (0) 2012.08.29
최상위 코드와 그 최상위 코드를 대상으로 그룹 .  (0) 2012.08.24
오라클 백업 및 복구(Export, Import)  (0) 2012.08.10
OUT Join  (0) 2012.08.08
Posted by 사라링

AJAX에 대해서 인터넷 검색을 하다 여기저기에 있는 것들을 허락없이 재구성했습니다.ㅜㅜ
활용처는 개발자의 상상력과 응용력에 따라 무한히 확장될 수도 있겠네요.
prototype.js를 이용하여 확장한 것이 바로 아래 것들,
http://script.aculo.us/
http://openrico.org/
여기 나온 예제들도 함 봐보세요. 이런 걸 RIA라고들 하네요.
http://wiki.script.aculo.us/scriptaculous/show/Demos
http://openrico.org/rico/demos.page
몇 년전만 해도 정말 이런거 할려면 삽질에 삽질을 거듭해야 했는데...
세상 너무 좋아졌습니다...ㅜㅜ

AJAX는...
- 동일 도메인 내에서만 가능합니다.(웹 C/S라고들 하던데...)
(만약 서버투서버(자바<->ASP)로 데이터 통신을 하고 싶다면 XML-RPC를 이용해 보세요.
순수 ASP로만 짜여진 것도 존재합니다.)
- XMLHTTPRequest를 이용하여 HTTP로 통신합니다.
: XMLHTTPRequest는 utf-8로 처리합니다.
- AJAX를 이용한 MVC 모델2 패턴 개발 방법이 가능합니다.
- 자바스크립트로 서버측 데이터를 핸들링합니다.
- 자바스크립트로 OOP도 하네요...ㅡㅡ;


아래 샘플은 prototype.js 파일이 있어야 실행가능합니다.
www.prototypejs.org에서 prototype.js 파일을 다운로드 받으세요.^^

아래 샘플 이외에 Ajax.PeriodicalUpdater 함수도 함 사용해보세요.
지정한 초마다 응답 페이지에서 서버쪽 데이터를 끌어옵니다.
물론 페이지 리프레쉬없이요.
이외에도 다른 함수를 보면 여러개의 이벤트를 동시에 실행할 수 있습니다.
api 참조하세요.
http://www.prototypejs.org/api

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=euc-kr" />
<script src="lib/prototype/prototype.js" type="text/javascript"></script>
<script language="javascript">
function ajaxRequest(strResponseURL)
{
var httpObj = new Ajax.Request
(
strResponseURL,
{
// 기본값은 비동기식, 동기식으로 작성할려면, api 참조
// method:'post', // post가 기본값이므로 생략 가능
parameters:Form.serialize('frm'),
// form의 요소들을 통째로 응답페이지로 전송
onSuccess:displayJson,
// 성공했을 경우, displayJson 함수 호출
// onSuccess:displayTextHtml // 일반 텍스트나 html 형식을 경우
onFailure:displayError
// 실패했을 경우, displayError 함수 호출
}
);
}


function displayJson(responseHttpObj)
{
var returnData = responseHttpObj.responseText;
// 만약 XML 형태로 데이터를 받아온다면 responseHttpObj.responseXML

returnData = eval("(" + returnData + ")");

switch (returnData.dataType)
{
// 사실 응답 페이지가 utf-8로 저장되었다면 decodeURIComponent를 안써도 상관없지만,
// 그래도 혹시나 하여 사용
case 'one' : $("results").innerHTML = decodeURIComponent(returnData.response);
break;

case 'array' : viewResponse(returnData.response);
break;

default : $("results").innerHTML = decodeURIComponent(returnData);
}
}


function displayTextHtml(responseHttpObj)
{
var returnData = responseHttpObj.responseText;

$("results").innerHTML = decodeURIComponent(returnData);
}


function viewResponse(data)
{
var intLength = data.length;
var strHTML = "";

for(var i=0; i<intLength; i++)
{
strUserID = decodeURIComponent(data[i].userID);
strUserName = decodeURIComponent(data[i].userName);

strHTML += "ID : " + strUserID + '<br>';
strHTML += "이름 : " + strUserName + '<br>';
}

$("results").innerHTML = strHTML;
}


function displayError()
{
$("results").innerHTML = 'AJAX failed';
}

/*
// 2번과 3번은 skip해도 됩니다.

1. AJAX 응답 페이지는 utf-8로 인코딩하여 저장
: 한글 처리를 위해서 반드시 필요
: 여기서는 json.asp, json2.asp, json3.asp을 utf-8 인코딩 형식으로 저장하였음.
2. 응답 페이지에서 단일값을 전송할 경우, displayJson
{
"dataType":"one",
"response":"realsnake"
}
3. 응답 페이지에서 배열로 전송할 경우, displayJson
{
"dataType":"array",
"response":
[
{
"name":"realsnake",
"height":"???"
}
]
}
4. 응답페이지에서 text나 html을 통째로 전송할 경우 : displayTextHtml
*/
</script>
</head>
<body>
<h3>AJAX in prototype.js Example</h3>
<p>
prototype.js를 이용한 AJAX 예제
</p>
<form id="frm">
<!--
<input type="text" id="test" size="10">
form의 요소의 값들을 전송한다면, 받는 페이지에서는 strTest = Request("test")와 같이,
일반적인 get/post 방식으로 받으시면 됩니다.
//-->
<p><input type="button" value=" 확인 " onclick="ajaxRequest('json3.asp');"></p>
</form>
<div id="results">[Results Area]</div>
<!--
위 div 태그 안의 내용은 AJAX로 받아온 데이터로 변경됩니다.
//-->
</body>
</html>



<!-- 이하 파일들은 반드시 utf-8 인코딩 방식으로 저장 //-->

json3.asp
<%
' ---------------------------------
' asp 파일에서 DB 처리를 하고 결과 값을 넘겨도 잘 됩니다.
' 옵션대신 DB 처리결과를 함 넣어보세요.
' 그냥 일반 text만 써도 되고 아래처럼 html 태그를 같이 써도 됩니다.
' 아래 html코드와 텍스트가 그대로 위 div 태그에 페이지 리프레쉬없이 입력됩니다.
%>
<select name id="selectID" style="font-size:12px;">
<option value="1">옵션</option>
</select>
<%
' ---------------------------------
%>


json2.asp
<%
' 아래 예제들은 json(javascript object notation) 형태의 예제입니다.
' -------------------------------------
' 호출하는 페이지의 viewResponse 함수를 볼 것
%>
{
"dataType":"one",
"response":"응답"
}
<%
' -------------------------------------
%>


json.asp
<%
' -------------------------------------
' dataType이니 response니 하는 것들은 나름대로 데이터를 구조화하기 위하여
' 임의적으로 작성하여 사용한 것입니다.
' 호출하는 페이지의 viewResponse 함수를 볼 것
%>
{
"dataType":"array",
"response":
[
{
"userID":"realsnake",
"userName":"누구신지"
}
]
}
<%
' --------------------------------------
%>
===================================================================

출처 : Tong - hooah0401님의 JAVASCRIPT통

Posted by 사라링

GitHub 가 뭐지?

2012. 8. 24. 17:37

개발의 패러다임을 바꾼 GitHub가 주목받는 이유?

 By Editor 어설프군YB
2012. 07. 11 1417 조회수 2 댓글수
  
GitHub는 2008년 'The Crunchies 2008' 시상식에서 스타트업 분문에서 GitHub가 해당 상을 수상하면서 유명해졌다. Channy님 블로그에 따르면 GitHub는 소셜 서비스 기반의 프로그래밍 소스 코드 공유 서비스로 보면 될 것 같다. 

일종의 분산형 협업 모델을 온라인상에 구현한 것으로 Git 라는 리눅스 커널 개발에 쓰이는 분산형 패치 도구를 리누즈 토발즈가 개선해 만든 분산형 소스 콘트롤 시스템으로 볼 수 있을 것 같다. 

Github는 이 분산형 소스 콘트롤 기반에 프로젝트 관리를 주목표로 하는 기존 포지(Forge) 계통의 SourceForge나 Google Code의 기능을 내포하면서 소스코드 개발과 패치를 시행함은 물론 온라인을 통해 참여자나 관심있는 사용자에게 소스 개발 내용을 업데이트해 알려주는 기능을 구체화시킨 온라인 서비스라고 보면 될 것 같다.

기존에 소스포지에 페이스북의 업데이트 기능을 접목한 시스템이라고 하면 될까 싶다. 
 
 



GitHub 어떻게 만들어 졌나?
Github는 PJ Hyett, Tom Preston-Werner, Chris Wanstrath라는 세명의 창업자에 의해 창업된다. 물론, 대부분의 창업 스토리가 그러했던 것처럼 처음부터 이들이 친분이 있었던 것도 아니고 창업을 결심한 것도 아니었다. 

창업자중 한명인 Tom Preston-Werner가 원래 Rails 개발자 였는데, 이 사람이 Git 시스템의 열혈 팬이기도해 GitHub 창업의 단초가 되었던 모양이다. Rails 개발자였던 만큼 Rails 개발자 커뮤니티의 모임에 자주 나갔던건 당연했을터, 거기에서 Chris Wanstrath 공동 창업자를 만난 것이 이들 창업의 기초가 된다.
 
둘이 술을 먹으면서 Git 기반의 호스팅 서비스가 있으면 좋겠다는 아이디어가 나왔는데, 당시에는 Git가 시스템이나 기능적인 면에서는 쓸모가 있지만 범용적이지 못해서 아주 소수의 리눅스 커널 개발자나 관심있는 몇몇 개발자만 사용하고 있었던 것을 호스팅 서비스로 접목시켜 온라인으로 제공해 보면 어떨까하는 그런 접근이었다. 

아이디어만 나눴으면 당연히 창업 스토리는 없었을 것인데, 미국의 젊은 이들은 두려움이 없는지 자신들이 평소 느꼈거나 필요하다고 생각했던 이 프로젝트에 발벗고 나서게 되고 두 사람이 첫 시작이 되어 Git 서비스를 웹에서 이용할 수 있도록 도구화 하는 프로젝트를 시작하게 된다. 바로 이것이 GitHub의 창업 초기 이야기다. 

그들의 이야기를 쫒아보면 당연한 이야기겠지만 처음부터 시장성 같은 것은 아예 염두해 두지 않았다고 한다. Git라는 시스템 자체가 워낙 Side Ware에 가까워 이용자도 많지 않았고, 실 사용 대상자가 어느정도나 될지 예측할 수 없었던 상황에서 아이디어 조금 추가하고 웹으로 서비스를 제공한다고 단기간에 소스포지나 구글 코드 같은 서비스를 능가 할 거라고는 생각하지 않았을 것이다. 

그래서 이 프로젝트를 시작한 둘은 직장을 뛰쳐나와 창업하려는 욕심보단 가능성을 테스트해 보자는 접근법으로 출발한듯 보인다. 둘은 모두 직업이 있었기 때문에 일주일에 한번씩 만나면서 주말 혹은 밤에 작업하는 형태로 이 프로젝트를 이어오게 된다. 
 
그런데 될려고 하는 서비스는 하늘이 알아본다고, 이 서비스의 가능성을 알았기 때문인지 오랜기간 작업 끝에 오픈한 웹 사이트는 얼마 되지 않아 사람들이 몰려들기 시작하면서 단순히 직업을 가지고 두사람이 만들 수 없는 수준으로 변모해 갔다. 

이때 또 한명의 공동 창업자인 PJ Hyett를 끌어 들이게 되고 본격적인 GitHub 개발에 올인한다. 이 때 당시만해도  PJ Hyett, Chris Wanstrath는 별도의 스타트업을 시작하고 있었는데, 이 서비스의 가능성을 확인하고 바로 정리하고 GitHub에 올인해 현재에 이르게 된 것이다. 


스타트업 입장에서 GitHub가 존경스러웠던 이유?
이들은 초반에 돈을 거의 쓰지 않았다고 하는데 GitHub를 시작한지 2년간 사무실이 없었다고 할정도면 말다한거 아닌가?

사실, 스타트업을 하면서 사무실, 서버, 약간의 인건비등은 고정비로 지출된다. 아이엠데이 같은 경우도 정말 이런저런 방법을 다 동원해가며 최소화 했지만, 아이엠데이 서비스는 초반에 인기가 없었기 때문에 운영비를 별도로 충당 할 수 있는 길이 많이 없었다. 

그래서 일정한 고정비용이 지출되었는데, 이들은 사무실 없이 미국에 유명한 37Signals라는 루비온 레일즈를 개발한 회사가 만든 CampFire라는 웹베이스의 채팅 서비스를 이용해 대화하며 업무를 처리하고 서비스 운영에 필요한 호스팅은 EngineYard라는 업체로 부터 GitHub에 광고를 게재해주는 조건으로 무료로 이용했다고 한다. 

이들이 법인 설립에 들어간 총비용은 1000$달러 정도이데.. 유일하게 아이엠데이가 이들보다 적게 들어간 비용이 바로 이 법인 설립 비용이 아닐까 생각된다. 한국은 법인 설립을 중기청 차원에서 지원하고 있어서 온라인 서비스를 이용하면 주식 비용 일부를 제외하면 거의 공짜에 가까운 수준으로 법인 설립이 가능하다. 

그런점에선 한국의 인프라도 꽤 슬만하다는 생각이다. 이후 베타 버전을 만든뒤 주변의 rails 개발자를 대상으로 영업을 했는데 당시에 오픈 소스로 진행하는 프로젝트가 많았고, 실제 개발자들도 소스 포지등을 활용하고 있어서 조금 손쉽게 베타 테스터겸 고객으로 모집 할 수 있었다고 한다.

물론 친분 있는 사람들이 첫 고객이었기 때문에 열성적으로 이들이 추구하는 Git 시스템에 대해 열성적으로 홍보와 설명, 관리와 지원을 담당하며 서비스를 발전 시켰다. 당시 이런 노력 때문인지 rails 개발자들도 반응이 꽤 좋은 편이었다고 한다.

이렇게 첫 테이프를 끊고, 베타 버전을 지속하며 유저들을 끌어모아 2008년 4월 대중에게 첫 선을 보이며 GitHub의 전설을 이어간다. 위에 설명한 'The Crunchies 2008' 시상도 이런 노력때문에 이루어진 것으로 보인다. 


GitHub 조직문화중 인상적인 점
이 조직의 최대 장점은 운영지침, 부서, 직급, 업무 데드라인, 출퇴근 시간등이 없는 것이다. 거의 조직 파괴자들에 가까울 정도로 무규칙적인 기준을 가지고 있는데 너무 위험할 정도로 무모하단 생각이 들정도 였다. 

인력이 많지 않기는 한데 아이엠데이도 직급, 업무 데드라인등은 조금 탄력적으로 이용하고 있고, 좀 더 서비스가 강화되고 조직과 수익이 생기면 이런 운영폭을 넓힐 생각은 가지고 있지만, 이들과 같은 접근법을 시도 할 수 있을지는 사실 감이 잘 안잡히는게 사실이다. 

서구식 실리주의가 밑바탕에 깔려있다고해도 이정도면 조직 파괴론적 이론에 대입해 볼 수 있을정도로 파격적인 실험으로 보여진다. 아마 한국의 대기업이 이런 내용을 벤치마킹 했다면, 담당자는 사표 쓰지 않았을까 싶을 정도다. 

이들이 이런 원칙을 만들어가는 것은 GitHub에서 하는 일이 아니라 자신이 중요하다고 생각하는 것을 원하고 싶을때 진행해야 효율을 높일 수 있다는 철학에서 출발한 것 같다. 추상적인 이론이지만, 매우 기본을 잘 지킬 준비가 되어 있다면, 훌륭한 정신이라고 볼 수 있지만 실천하긴 정말 어렵다는 점에서 박수를 쳐주고 싶을 정도다.

일단 아직까지는 잘 지켜지고 있고 실제로 운영진과 직원간에도 신뢰와 존중이 밑바탕에 깔려서 그런지, 업무 처리에 있어서도 큰 문제가 없었다고 하니 대단하단 말밖에 안나오는 것 같다. 

이런 조직일수록 인력관리와 구인에 신경을 많이 써야 하는데 어떤 노하우가 있는지 심히 궁금해지는 대목이 아닐 수 없다. 
 

문화파괴자에서 트랜드 리더로..
이들 때문에 실리콘 밸리의 채용문화가 바뀌었다고 한다. 이들은 채용 대상자의 기술적 능력을 GitHub 페이지를 만들고 활용하는 내용을 보고 채용 대상자의 전문 분야와 업무 특성등을 살펴보면서 채용을 결정한다는 것이다. 

개발 전문 조직에서 쉽게 쓸 수 있을 것 같은 발상인데, 막상 한국 기업에서 이런 방식으로 개발자의 업무 패턴이나 개발 특성을 알아보고 면접만으로 사람을 뽑는다고 해보자? 아마 한국에선 100% 실패할 것 같은게, 기본적으로 학력, 경력, 나이.. 면접등을 통한 일반적인 채용 프로세스가 정형화되어 있어서 이런 파격적인 실험은 아마 벤처에서나 가능하지 않을까 싶다. 

어떤면에서는 벤처 조차도 어렵게 여겨질 정도로 파격적인 실험인데.. 이런 요소에서도 남들이 하지 않는 노력을 기울인다는 점이 주목받는 요인이 아닐까 싶다. 

또, Help 페이지 코드를 공개해 누구나 Help 페이지를 자유롭게 꾸밀  수 있게 하는가 하면, 소수의 Rails 개발자를 바탕으로 시장을 특화해 성장했다는점, 남들이 하지 않았던 Git 시스템을 기초로 시장보다 가능성에 더 많은 고민을 했다는점등은 우리에게 많은 시사점을 안겨준다는 생각이다. 

이미 만들어 졌거나 어느정도 대표 플레이어가 정해져가는 시장이 아니라 남들이 안하는 분야, 안하고 있고, 앞으로 가능성이 있는 분야를 찾는 노력, 그리고 자신들만의 철저한 철학을 바탕으로 서비스를 개발하고 발전시켜 나가야 한다는 기본적인 스타트업 성공론을 우리는 GitHub를 통해 벤치마킹해 볼 수 있지 않을까 싶다. 

Posted by 사라링

http://www.eclipse.org/downloads/ 

Hightlights 

Eclipse 4.2 is now the default platform for the Eclipse community. 

New Koneki project provides world-class Lua development tools for M2M application development. 

Equinox provides the reference implementation for the new OSGi R5 specification. 

Eclipse Virgo 3.5 will deliver the new Virgo Nano which allows developers to deploy very small and fast OSGi-based applications. 

Xtend, a new language for Java developers, will introduce support for debugging, improved IDE support and new language features, including properties, data classes and type inference for fields. 

Xtext 2.3 will now support integrated debugging of JVM-based DSLs created using Xtext and tighter integration with the Java Development Tools (JDT). 

Eclipse Code Recommenders makes developers smarter about using APIs. Based on a knowledge-based and advanced analytics of existing API usage, Code Recommenders provides intelligent API recommendations to Eclipse developers building Java applications. 

from: http://www.eclipse.org/juno


빨리 실행해 봐야지 ㅋ 

Posted by 사라링

PJax ?

 | AJAX
2012. 8. 24. 17:26

ajax 기술은 이미, JavaScript 를 이용한 대중적인 웹기술로, 많은 웹사이트에서 적용하고 있는, 이제는 새로울 것 없는 기술 중 하나입니다. 간단하게 언급해 보면, 페이지의 새로고침없이 데이터를 가져와 화면에 뿌려주는 기능을 합니다.

사실, ajax 출현 전에는 iframe 기술을 이용해서 비슷한 처리를 하곤 했습니다.  iframe 태그를 숨겨놓고, 서브밋을 날릴 때 iframe 태그에 서브밋을 날려서, 데이터를 가져온 다음에 현재의 페이지에 보여주는 방식을 이용하면 ajax 와 비슷하게, 페이지 이동없이 데이터를 가져와 뿌려줄 수 있었습니다. ajax 기술이 사용되기전에 한 때 많은 사이트에서 이용했던 방식중 하나입니다.

ajax 기술의 등장은 사실, XMLHTTPRequest() 에서 출발합니다. 누구나 ajax 방식의 처리를 구현해서 만들어 사용할 수 있지만, prototype.js 을 비롯한 jQuery 등의 등장으로 ajax 처리를 구현할 필요없이 쉽게 가져다 간단하게 API를 호출해서 사용할 수 있게 만들어 놓아, 더욱 더 많은 인기를 끌게 되었습니다. 그리고 ajax 는 웹을 빠르게, 그리고 멋지게 만들어 주었고요!

그런데 ajax 를 이용하면, 뒤로 가기 버튼을 이용할 수 없습니다. 게다가, HTML 소스를 열어봐도, ajax 에서 가져온 데이터가 어디에 있는지 찾을 길이 없습니다. 당연히, 검색사이트에서도 ajax 로 가져오는 데이터나 페이지들은 인덱싱을 해주지 못하므로, ajax 로 처리한 결과들은, 검색사이트에서 검색해도 검색결과를 얻을 수 없습니다.

그래서 이를 보완하기 위해 등장한 방식이 hashbang (hash:# 와 bang:! 의 합성) 기술인데, location.hash 를 이용해서, URL 의 #(hash) 뒤에 붙는 값을 이용해 ajax 처리를 합니다. 뒤로가기를 처리할 수는 있지만, 일종의 URL Hack 방식이어서, 정당성도 없고 논란의 중심에 있기도 한데, 검색인덱싱이 안되는 건 물론이고(구글에서는 검색을 할 수 있게 escape처리를 해놓음), JavaScript 기술에 의존하고 있기 때문에, JavaScript 오류 하나만으로도 사이트를 하나도 이용할 수 없게 되어버립니다. 즉 JavaScirpt 없이는 컨텐츠를 보여주지 못합니다.  hashbang 과 관련해서는 아웃사이더님 블로그에 잘 정리되어 있습니다.

해시뱅(#!)에 대해서… (http://blog.outsider.ne.kr/698)

마지막으로 이 모든 문제를 해결한, 비동기통신의 대안 pjax 를 소개합니다. 좀 거창하게 소개했는데 그렇게 거창할 건 없습니다. ^^; 우선 pjax 는 pushState + ajax 를 합쳐서 pjax 라 합니다. 여기서 pushState 는 HTML5 에서 추가된 메소드로 브라우저의 히스토리를 조작할 수 있고, 뒤로가기/앞으로가기 버튼을 이용할 수 있습니다. (현재는 github 과 facebook 등에서 쓰이고 있는 걸로 알고 있습니다.)

클리어보스에서 번역한 HTML5 명세의 window.history.pushState  http://html5.clearboth.org/history.html#dom-history-pushstate

pjax 는 pushState 를 이용한 ajax 처리방식으로 jQuery 라이브러리로 제공되고 있고, github 의 창립자이자, CEO 인 defunkt (Chris Wanstrath) 가 개발했습니다. pushState 를 지원하는 브라우저에서는 pushState 를 사용해서 동작하고, pushState 비지원 브라우저(IE9 이하) 에서는 지원하지 않는 대로 일반적인 페이지 이동형태로 처리가 이루어지기 때문에 URL 은 동일하게 전환하게 됩니다.

pushState + ajax = pjax https://github.com/defunkt/jquery-pjax

사용방법도 쉽습니다. 위의 링크에 몇가지 방식으로 나와 있는데 간단하게는 링크를 잡고, 그 링크를 어느 컨테이너로 처리할 지 잡아서, pjax 처리를 해주면 됩니다.

가령, js-pjax 클래스를 가진 a 링크만을 pjax 처리한다면 아래 코드와 같습니다.

<a href=’/explore’ class=’js-pjax’>Explore</a>
$(‘.js-pjax’).pjax(‘#main’)

샘플을 보면 쉽게 이해할 수 있습니다. pjax 체크박스를 체크하고 링크를 누르면, 시간이 변하지 않습니다. 즉 해당영역만 링크를 다시 가져오는 것을 볼 수 있습니다. pjax 체크박스를 풀고 링크를 누르면 페이지가 리로드되는 일반적인 방식으로 페이지를 로드합니다.  (내친김에 IE9 이하 에서도 pjax 처리는 하지 않고, 일반적인 방식으로 페이지를 로드합니다)

pjax 샘플 http://pjax.heroku.com/

흥미로운 점이 많아서, 혹시 단점은 없나 찾아봤는데, HTML5 라 IE9 이하 하위버전에서 지원하지 않는 점만 빼면, 앞으로의 비동기 통신기술의 대안이 아닐까 싶습니다.

참조링크 :
http://firejune.com/1743
https://github.com/defunkt/jquery-pjax
http://blog.outsider.ne.kr/698
http://html5.clearboth.org/history.html#dom-history-pushstate

'AJAX' 카테고리의 다른 글

Dialog  (0) 2012.06.19
Ajax 란  (0) 2012.05.24
AJax JSON 데이터 가져오기의 형식 .  (0) 2012.05.24
Posted by 사라링

[감리 산출물 작업]


select A.COMM_CD,A.COMM_NM,A.UP_COMM_CD, B.COMM_NM,A.USE_YN

from COM_STD_MGT A INNER JOIN 

(

     SELECT COMM_CD,COMM_NM,UP_COMM_CD FROM COM_STD_MGT WHERE UP_COMM_CD='300'

) B ON (A.UP_COMM_CD = B.COMM_CD)

WHERE A.USE_YN ='Y'

ORDER BY UP_COMM_CD,COMM_CD;


                                                                  ▽



select   A.UP_COMM_CD AS 최상위코드,  B.COMM_NM AS 최상위코드네임 ,A.COMM_CD AS 하위코드,A.COMM_NM AS 하위코드네임,A.USE_YN AS 사용유무
from COM_STD_MGT A INNER JOIN 
(
     SELECT COMM_CD,COMM_NM,UP_COMM_CD FROM COM_STD_MGT WHERE UP_COMM_CD='200'   -- UP_COMM_CD 의 값에 최상위 코드를 입력 할것 
) B ON (A.UP_COMM_CD = B.COMM_CD)
WHERE A.USE_YN ='Y'
ORDER BY A.UP_COMM_CD,A.COMM_CD ;
  -- 검색후 엑셀로 추출 하여 필터 처리 하여 감리 산출물에 반영 할것 BY 김훈 

'오라클' 카테고리의 다른 글

DB2 함수 정리.  (0) 2012.08.29
ORDERY BY 절 사용  (0) 2012.08.29
오라클 백업 및 복구(Export, Import)  (0) 2012.08.10
OUT Join  (0) 2012.08.08
SYS_CONNECT_BY_PATH  (0) 2012.07.26
Posted by 사라링

BLOG main image
.. by 사라링

카테고리

사라링님의 노트 (301)
JSP (31)
J-Query (41)
JAVA (24)
디자인패턴 (1)
스트러츠 (3)
안드로이드 (11)
오라클 (45)
우분투-오라클 (1)
이클립스메뉴얼 (6)
스프링3.0 (23)
자바스크립트 (10)
HTML5.0 (17)
정보처리기사 (1)
기타(컴퓨터 관련) (1)
문제점 해결 (3)
프로젝트 (2)
AJAX (4)
하이버네이트 (3)
트러스트폼 (11)
Jeus (2)
재무관리(회계) (5)
정규식 (5)
아이바티스 (8)
취미 (2)
소프트웨어 보안 관련모음 (0)
정보보안기사 (6)
C언어 베이직 및 프로그램 (3)
보안 관련 용어 정리 (2)
넥사크로 (6)
웹스퀘어_ (0)
Total :
Today : Yesterday :