2007년 12월 29일 토요일

JAVA - ARGUMENT BINDING OF PREPARESTATMENT



오라클에서 데이터 억세스 모듈을만들때, 바인딩 변수를 사용하면,
DB에 부하를 덜수있는 잇점이 있죠. 머 제가 DBA도 아니므로 내부적인
매커니즘은 잘 모르더라도, 가끔 오라클사에서 와서, 시스템 튜닝을
할때 개발자 교육을 하는데 거기서 항상 강조하는게, 리터럴 변수를
쓰지 말고 바인딩 변수를 쓰라고 그럽니다...

리터럴과 바인딩에대해 간단히 설명하자면,

리터럴은
select * from table where id='설사댄스' 처럼 값을 직접 쿼리에 적용시켜
대입시키는 방법입니다.

바인딩은 리터럴과는 좀 틀리게 작동합니다.

바인딩은
select * from table where id=:input_id 처럼 작성되어지며,
프롬프트가 :input_id 를 입력을 기다립니다.

일반적으로 쿼리가 실행되면, 파싱 및 컴파일이 이루어진 후 실행이되어집니다.
dbms는 이전에 사용되어진 쿼리에대한 정보를 가지고 있는데, 이 정보에 기록된
쿼리와 동일한 쿼리를 사용하게되면, 컴파일단계를 생략합니다.

바인딩을 이용하면, 바인딩변수에 해당하는 부분에 대입되는 값만 틀리지,
한번 컴파일되면, 언제든지 재사용되어집니다. 하지만 리터럴의 경우는 쿼리문
자체를 틀리게 인식하므로, 리터럴변수 위치에 있는 값이 틀리면 매번 컴파일이
이루어지므로, 발생 빈도수가높은(사용율이 높은 쿼리) 는 바인딩처리를 해주면
DB의 부하를 상당히 덜어줄수 있습니다..

예를들어 회원이 1000명인 사이트에 1000명의 유저에대해 로그인을 해주는
쿼리를 생각해볼때,
select count(1) from user_tb where user_id = '설사댄스' and passwd = '변비'
처럼 리터럴형 쿼리를 만들어준다면, 1000명이 각각 접속을할때 각각에대해
컴파일이 일어납니다. 서로 다른 쿼리로 인식되어지는것이죠.
하지만, 바인딩을 이용하면
select count(1) from user_tb where user_id = :user_id and passwd = :passwd
처럼 작성되어진다면, 한번만 컴파일이 일어나고, 1000명의 유저가 각각의
로그인에대해서 같은 쿼리를 이용하게되므로 컴파일에 걸리는 부하를 1/1000 로
줄일수 있는 기대효과를 볼수 있습니다..

바인딩은 어떻게 이용할까요..
간단합니다. 요새 언어들은 대부분 prepareStatement 라는 api 를 제공합니다.

select count(1) from user_tb where user_id = ? and passwd = ?
처럼 ? 기호로 바인딩변수가 위치하는곳을 알려주고, 멤버함수로 해당위치에
값을 셋팅해주면, 알아서 값을 대입해주고 결과값을 돌려줍니다....

이정도면, 왜 바인딩을 이용해야하는지 공감하시리라 생각되어지구요.

오늘 이 글을 쓰게된 이유는, 동적인쿼리에서 바인딩을 이용하는 방법을 연구해보고저
쓰게되었습니다.

select * from table where id = ? ...
우리는 이런 쿼리를 정적쿼리라고 합니다... 파라미터의 갯수가 정해진 것이죠.

select * from table where id in (select .... from table_b)
이처럼 가변적인 형태의 파라미터를 받아들이는 형태를 동적쿼리라고 합니다.

동적쿼리의 장점은 한번의 커넥션으로 다중적인 결과를 도출할수 있습니다.

예를들어 회원테이블에서 유저의 정보를 가져올려구할때 우리는
select * from member_tb where user_id = ? 처럼 쿼리를 던질수 있습니다.
대부분의 경우라면 위의 쿼리는 unique 속성을 갖는 user_id 에대해 정보를
얻을수 있습니다.

그런데, 여러명의 데이터도 불러올수 있죠...이럴때 쓰는 게 in 키워드입니다.
select * from member_tb where user_id in ('날나리개발자', '설사댄스')
이처럼 쿼리를 던지면, '날나리개발자' 와 '설사댄스' 라는 아이디를 갖는
유저의 정보를 가져올것입니다...

그런데 문제가 있습니다.....저런경우는 프로그램적으로 어떻게 바인딩할것인지
.........

리터럴을 쓰면 오히려 편합니다.
select * from member_tb where user_id in ($user_list)
처럼 쿼리를 만든다음에 $user_list = "'날나리개발자', '설사댄스'";
처럼 처리해주면 간단하죠.
.....................................................................
그런데 이 글의 주제가 바인딩이므로 저렇게 쓰면 매우 슬퍼지죠 ㅠ,.-;;;


=== 바인딩기법1(저렙용) ===
어떤 언어든 for , while 등의 루프를 제공합니다.
parameter 로 $user_list[] = {"날나리개발자", "설사댄스"};
이렇게 값을 가져온다면
$qry = "select * from user_tb ";
$qry.= "where user_id in (";
for($i=0; $i<$user_list.length; $i++) { //$user_list.length ??? 맞는문법인지 원..php 한지 오래되서... // 어쨌든 주제는 그게 아니니 걸러들으시길 -o-;;;; if($i>0) $qry.=" , ";
$qry .= "?";
}
$qry.=" ) ";

for($i=0; $i<$user_list.length; $i++) { // 여기에 바인딩변수를 setting 하는 메서드 호출; } 대충 이런 로직을 생각해낼수 있겠죠......머 좀 복잡하긴하지만, 일단 바인딩이라는 주제아래 충분히 가치있는 로직을 만들어냈습니다. 그런데 문제가 있습니다... 배열의 갯수가 틀리면, 바인딩을 쓴 효과가 줄어든다는 것이죠... 그렇다면,,이를 해결할 해결책은 무엇일까요? 그건 바로 배열형태로 파라미터를 가져오지말고, 구분자를 갖는 문자열로 가져오는 겁니다... $user_list = "설사댄스,날나리개발자"; 처럼요.. 그럼 이걸 바인딩하는 기술을 알아보도록 하겠습니다..실은 오늘 주제가 이겁니다. ^^;;; 그 전에 먼저 준비운동을 하겠습니다... 문자열을 구분자 (예:콤마",")를 기준으로 row 로 나누는 방법을 연구해봅시다. 먼저 pivot table을 만들어봅시다. CREATE TABLE PIVOT ( NO NUMBER NOT NULL, NAME VARCHAR2(256 BYTE) ); CREATE UNIQUE INDEX PKX_M_PIVOT ON PIVOT (NO); insert into pivot select rownum, to_char(rownum) from dual connect by level <= 1000; commit; 이렇게해서 no 컬럼의 값이 1 부터 1000 까지의 값을 갖는 pivot 테이블을 만들었습니다. 이렇게 pivot 을 만들면 상당히 유용하게 쓸수있습니다. select "설사댄스,날나리개발자" as col1 from dual; result>>
-----
col1
-----
설사댄스,날나리개발자
-----

, 를 기준으로 row 로 나눈다면 2개의 로우가 필요하겠죠.
몇개의 row가 필요한지 알아내는 방법은

select length(a.col1)-length(replace(a.col1,',',''))+1 as len
from (select "설사댄스,날나리개발자" as col1 from dual) a;

처럼하면 2 가 나옵니다.

그럼 다음단계로 가서 피봇과 크로스조인을 해봅시다.

select a.len, a.col1, b.no from (
select length(a.col1)-length(replace(a.col1,',',''))+1 as len,
a.col1
from (select '설사댄스,날나리개발자' as col1 from dual) a
) a cross join pivot b where b.no <>>
--------+----------------------+----------+
len col1 no
--------+----------------------+----------+
2 설사댄스,날나리개발자 1
2 설사댄스,날나리개발자 2
-------------------------------------------

데이터 복제에 성공햇습니다.... 만약 "설사댄스,날나리개발자,행복한고니"
를 입력하면,
result>>
--------+----------------------+----------+
len col1 no
--------+----------------------+----------+
3 설사댄스,날나리개발자,행복한고니 1
3 설사댄스,날나리개발자,행복한고니 2
3 설사댄스,날나리개발자,행복한고니 3
-------------------------------------------
처럼 결과가 나오겠죠.


자 이제 저걸
---------
1 설사댄스
2 날나리개발자
3 행복한고니
----------
의 형태로 만들려면 어떻게 해야할까요...

네.. instr() 과 substr() 을 이용하면됩니다...

select
trim(
SUBSTR
(
','col1',' ,
INSTR(','col1',',',',1, NO)+1 ,
(INSTR(','col1',',',', 1,NO+1)-INSTR(','col1',',',', 1,NO))-1
)
) as col2
from (
select a.len, a.col1, b.no from (
select length(a.col1)-length(replace(a.col1,',',''))+1 as len ,
a.col1
from (select '설사댄스,날나리개발자,행복한고니' as col1 from dual) a
) a cross join pivot b where b.no <= a.len ) 결과>>
------+--------------+
no col2
------+--------------+
1 설사댄스
2 날나리개발자
3 행복한고니
------+--------------+

이쯤되면 눈치채셨죠...이제 이사람의 정보를 어떻게 가져와야할것인지...

=== 바인딩기법(고렙용) : 구분자를 가진 문자열을 바인딩으로 받아서
파싱하여 동적쿼리 만들기 ===

select * from member_tb where user_id in (
select
trim(
SUBSTR
(
','col1',' ,
INSTR(','col1',',',',1, NO)+1 ,
(INSTR(','col1',',',', 1,NO+1)-INSTR(','col1',',',', 1,NO))-1
)
) as col2
from (
select a.len, a.col1, b.no from (
select length(a.col1)-length(replace(a.col1,',',''))+1 as len ,
a.col1
from (select ? as col1 from dual) a
) a cross join pivot b where b.no <= a.len ) ) 단 하나의 바인딩변수를 이용함으로서 루프를 이용한 바인딩을 셋팅하는 부담을 줄였습니다.... 이걸 이용하면 어떤 장점이 있을까요... update, insert, select 모든 쿼리에 다양하게 이용되어질수 있습니다. 파라미터를 구분자로 연결된 형태로 넘기는건 그리 어려운 작업이 아니니깐요.


출처 : http://www.phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=46546&sca=&sfl=mb_id%7C%7Csubject&stx=VALENNY&sop=and

HTML - DIV POSiTION TIP

특정 object를 기준으로 삼고 싶을때
<div id=target position='position:relative'>타겟이 되는 div</div>

<div id=sticker style="top:expression(target.offsetTop)">달라 붙는 div</div>

하지만 타겟이 되는 div의 position이 relative가 아니면 위치를 알 수 없다.

LINUX - CRONTAP

일정 시간에 자동으로 프로그램(배치) 을 실행시킬 수 있다.

1. 크론텝 목록보기 : corontab -l

2. 크론텝 편집 : corontab -e

ex) 0 0 * * * /test.sh

분 시 일 월 요일 / 실행 프로세스명

ex) 55 10 1 8 /usr/local/TEST/test.sh

55분 10시 1일 8월 화요일 /test.sh를 실행

/etc/rc.d/init.d/crond restart : 크론텝 리스타트

ORACLE - 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된다.

LINUX - SYNC SYSTEM TIMER

uptime 으로 현제 시간 확인 후

rdate -s 203.248.240.103

time.bora.net 과 동기화 시키면 된다.

admin 계정권한으로.


*관련 명령어

env, nslookup(time.bora.net)

LINUX - CRONTAP

일정 시간에 자동으로 프로그램(배치) 을 실행시킬 수 있다.
1. 크론텝 목록보기 : corontab -l

2. 크론텝 편집 : corontab -e
ex) 0 0 * * * /test.sh
분 시 일 월 요일 / 실행 프로세스명

ex) 55 10 1 8 /usr/local/TEST/test.sh
55분 10시 1일 8월 화요일 /test.sh를 실행

/etc/rc.d/init.d/crond restart : 크론텝 리스타트

2007년 12월 28일 금요일

HTML - SPECIAL CHARACTER


<HEAD>와 같은 글자를 표현하기가 쉽지 않을 것이다.
이는 HTML 문장에서 <HEAD>를 기술하면 HEAD의 시작태그로 간주하기 때문에 그렇게 표현되지는 않기 때문이다.
이를 표현하기 위해서는 이것이 태그의 시작이 아니고 그렇게 표현하는 것이라는 지시가 필요한데 이때 사용되는 글자가 특수문자이다.



&lt;HEAD&gt;로 표시하여야 <HEAD>로 표현된다.


특수문자는 일종의 기호문자이며 다음 두가지 기능을 위하여 사용된다.




  • 위와 같이 특수문자를 표현하기 위하여


    왼쪽 꺽쇠 (<)는 태그의 시작으로, 오른쪽 꺽쇠 (>)는 태그의 종료로 그리고 앰퍼샌드 (&)는 특수문자의 시작을 알리는 등 HTML 문서에서 특수한 의미를 지니고 있으므로, 일반 문서 내부에서 표현을 위하여는 사용 할 수 없다. 이것을 표현하기 위하여 특수문자를 사용한다.

  • 일반적으로 문서 편집기에 없는 문자를 표현하기 위하여

    예를 들어π, σ δ ∑ 등의 표현


이중 따옴표(")는 &quot;로 사용하여도 되며, 고급의 복잡한 문서에서 꼭 &quot;로 사용하여야 될 경우도 있다.



이들 문자들을 HTML 문서에서 사용하려면, 그 문자들의 문자기호(escape 기능)
사용해야 한다.





  • &lt; 혹은 &#60; : (<)의 문자기호
  • &gt; 혹은 &#62; : (>)의 문자기호
  • &amp; 혹은 &#38; : (&)의 문자기호

  • &nbsp; 혹은 &#160; : ( ) 공간의 문자기호
  • &copy; 혹은 &#169; : (©) 저작권 의 문자기호



  • &int; 혹은 &#8747; : () 인테그랄

  • &sum; 혹은 &#8721; : () 합계 (Netscape 않됨)

  • &Aring; 혹은 &#197; : (Å) 라틴 대문자 A 위 원 표시 (Netscape 않됨)




특수 문자에 대 한 자세한 내용은 HTML 4의 글자 엔티티(entity) 참조하라.



일반적으로 대소문자를 구별하지 않는 HTML의 다른 부분과는 달리, 특수문자나 문자 기호들은 대소문자의 구별한다. 예를 들어서 &Aring;(Å)와 &aring;(å) 는 다르고(Netscape 않됨), &AElig;(Æ:대문자)와 &aelig;(æ:소문자)는 다르다.



JAVA SCRIPT - POPUP MOVE TO CENTER


function center(){
var x,y;
if (self.innerHeight) { // IE 외 모든 브라우저
x = (screen.Width - self.innerWidth) / 2;
y = (screen.Height - self.innerHeight) / 2;
}else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict 모드
x = (screen.Width - document.documentElement.clientWidth) / 2;
y = (screen.Height - document.documentElement.clientHeight) / 2;
}else if (document.body) { // 다른 IE 브라우저( IE < 6)
x = (screen.Width - document.body.clientWidth) / 2;
y = (screen.Height - document.body.clientHeight) / 2;
}
window.moveTo(x,y);
}


이 funtion을 팝업 하단에서 호출

<script type="text/javascript">
center()
</script>