반응형
- 아마도 스팸봇들 등으로 부터 이메일을 캣치 당하는것을 방지해주는 함수인듯?

PHP hide_email()

1. What is it?

A PHP function to protect the E-mail address you publish on your website against bots or spiders that index or harvest E-mail addresses for sending you spam. It uses a substitution cipher with a different key for every page load. Look at the generated XHTML in the example while pressing the browsers "reload" button to see this in effect.

2. How does it work?

PHP encrypts your E-mail address and generates the javascript that decrypts it. Most bots and spiders can't execute javascript and that is what makes this work. A visitor of your web page will not notice that you used this script as long as he/she has javascript enabled. The visitor will see "[javascript protected email address]" in stead of the E-mail address if he/she has javascript disabled.

3. Example

<?php echo hide_email('test@test.com'); ?>

This is the PHP code you write where you want the E-mail address on your web page.

test@test.com

This is what the E-mail address will look like for the visitor of your web page.

<SPAN id=e776129440>[javascript protected email address]</SPAN>
<SCRIPT type=text/javascript>/*<![CDATA[*/eval("var a=\"PM37xKSakA+8N4D@2T0ijrGlWZeudn1fOv_VtoLEBJ.gm-RIyChws6bXzpYF9cUq5QH\";var b=a.split(\"\").sort().join(\"\");var c=\"9-F949-F93gX6\";var d=\"\";for(var e=0;e<c.length;e++)d+=b.charAt(a.indexOf(c.charAt(e)));document.getElementById(\"e776129440\").innerHTML=\"<a href=\\\"mailto:\"+d+\"\\\">\"+d+\"</a>\"")/*]]>*/</SCRIPT>

This is the generated XHTML that the bot or spider will see instead of your E-mail address.

4. The code

The "hide_email()" PHP function is only 9 lines of code:

function hide_email($email) { $character_set = '+-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'; $key = str_shuffle($character_set); $cipher_text = ''; $id = 'e'.rand(1,999999999); for ($i=0;$i<strlen($email);$i+=1) $cipher_text.= $key[strpos($character_set,$email[$i])]; $script = 'var a="'.$key.'";var b=a.split("").sort().join("");var c="'.$cipher_text.'";var d="";'; $script.= 'for(var e=0;e<c.length;e++)d+=b.charAt(a.indexOf(c.charAt(e)));'; $script.= 'document.getElementById("'.$id.'").innerHTML="<a href=\\"mailto:"+d+"\\">"+d+"</a>"'; $script = "eval(\"".str_replace(array("\\",'"'),array("\\\\",'\"'), $script)."\")"; $script = '<script type="text/javascript">/*<![CDATA[*/'.$script.'/*]]>*/</script>'; return '<span id="'.$id.'">[javascript protected email address]</span>'.$script; }

License: Public domain.

5. XHTML generator

You can use this generator if you have no PHP support on your web server. Change the E-mail address into your own E-mail address and press "Generate". Cut and paste the generated XHTML into your own web page.

E-mail address

Generated XHTML

Because the generator uses Javascript instead of PHP you can save this page to disk as "Web Page, complete" and use it offline.

6. Credits

The idea of javascript E-mail address obfuscation is not mine. It seems that Tim Williams came up with the idea first. Andrew Moulden improved it by adding a generated key. Ross Killen wrote a PHP version that generates a different key every page load. My implementation is much like that of Ross Killen, but I implemented a slightly different encryption algorithm, minified and obfuscated the javascript and made the script valid for javascript strict and XHTML 1.0 strict parsing.

  1. HTML generator by Tim Williams (University of Arizona)
  2. Improved HTML generator by Andrew Moulden (Site Engineering Ltd.)
  3. PHP version by Ross Killen (Celtic Productions Ltd.)

7. Considerations

  • Users must have javascript enabled to see your E-mail address.
  • This does not protect you against bots and spiders that can execute javascript.
  • The position of the key and the cipher text in the javascript are constant.
  • If this script gets very popular bots and spiders might get taught decoding it.
  • Line 7 of the PHP code complicates decoding (due to "eval"), but can be left out.
  • The main reason for not adding much more complexity is wanting few lines of code.
  • I chose the "span" tag over the semantically more correct "noscript" tag;
    the XHTML 1.0 strict schema says the "noscript" tag may only contain "Block" elements.

8. Interesting links

Send me your ideas and comments on this subject!

Maurits van der Schee - maurits@vdschee.nl - August 2008 - Amsterdam - Valid XHTML 1.0 Strict


반응형

'Web > PHP' 카테고리의 다른 글

참조연산자 (&)  (0) 2010.04.29
php 4.x 버전과 php 5.x 버전 동시에 사용하기[리눅스 설정]  (0) 2010.04.01
cURL  (0) 2010.01.20
간단한 달력 소스  (0) 2009.12.23
함수가 선언됐는지 여부를 알아보는 PHP 내장함수  (0) 2009.12.18
반응형

차례
1. cURL이란?
2. cURL의 장점
3. cURL의 함수 설명




1. cURL이란?

- cURL = Client URL
- cURL은 아래와 같은 프로토콜 등에 의해 전송되는 파일들을 위한 command line tool이다.
   FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS, FILE
-  SSL, HTTP POST, HTTP PUT, FTP uploading, HTTP form기반의 upload, proxies, cookies, user+password인증도 제공한다.
- cURL은 무료이며, 많은 운영체제에서 컴파일되고 동작하는 open software이다.
- command line tool을 이용하거나
- libCURL을 이용할 수 있다.

아래는 원문이다.
 curl is a command line tool for transferring files with URL syntax, supporting FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS and FILE. curl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, kerberos...), file transfer resume, proxy tunneling and a busload of other useful tricks.

Curl is free and open software that compiles and runs under a wide variety of operating systems.

 Use the cURL command line tool or use libcurl from within your own programs.

[what can curl do]의 내용을 참조하기 바란다.(http://curl.haxx.se/docs/features.html)


2. cURL의 장점
- cURL의 PHP설정의 allow_url_fopen옵션과 상관 없이 작동한다.
- Proxy, Cookie, Header등을 손쉽게 세팅할 수 있다.
- 다양한 프로토콜의 사용이 가능하여 개발의 범위가 넓다.
- 핸들러 하나로 여러개의 접속을 할 수 있기 때문에 코딩을 단순화 할 수 있다.

3. cURL의 함수 설명
 
-curl_close —URL handle  을 닫습니다.
-curl_copy_handlecURL handle 을 속성채로 복사합니다.
-curl_errno에러번호를 리턴합니다.
-curl_error  에러메세지를 리턴합니다.
-curl_exec  설정된옵션으로 실행합니다.
-curl_getinfo통신의 정보를 표시합니다.
-curl_initcURL handle 을 생성합니다.
-curl_multi_add_handle  handle  배열에 cURL handle  을 추가합니다
-curl_multi_closecURL handle 배열 전체를 닫습니다.
-curl_multi_exec  여러 개의 설정된옵션으로 실행합니다.
-curl_multi_getcontent  통신결과 컨텐츠를 표시합니다.
-curl_multi_info_read통신 정보들을 표시합니다.
-curl_multi_init여러 개의 handle 을 생성합니다.
-curl_multi_remove_handle여러 개의 handle  을 삭제합니다.
-curl_multi_select  여러 개의 handle  중 사용할 handle  을 선택합니다.
-curl_setopt_array옵션을 배열로 한번에 설정합니다.
-curl_setopt한가지의 옵션을 설정합니다.
-curl_version  cURL의 버젼정보를 가져옵니다.


4. 기초사용법

<?php
//핸들을 생성한다.
$ch = curl_init();


//URL을 설정한다.
curl_setopt($ch, CURLOPT_URL, http://www.naver.com/);

//실행한다.
$html = curl_exec($ch);

//핸들을 닫는다.
curl_close($ch);
?>


5. 옵션 설명

옵션 설명을 시작하기 전에...

sample.php
<?
if($a != "x")
header("Location:sample.php?a=x");
else{
?>
<pre>
webTech입니다.
<? print_r($_SERVER); ?>
<? print_r($_COOKIE); ?>
</pre>
<?
}
?>


출력 결과(IE)
webTech입니다.
Array
(
[HTTP_ACCEPT] => image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
[HTTP_ACCEPT_LANGUAGE] => ko
[HTTP_ACCEPT_ENCODING] => gzip, deflate
[HTTP_USER_AGENT] => Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; GTB6; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; InfoPath.2; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
[HTTP_HOST] => 192.168.0.43
[HTTP_CONNECTION] => Keep-Alive
[PATH] => /usr/local/bin:/usr/bin:/bin
[SERVER_SIGNATURE] => Apache/2.2.11 (Ubuntu) PHP/5.2.6-3ubuntu4.2 with Suhosin-Patch Server at 192.168.0.43 Port 80

[SERVER_SOFTWARE] => Apache/2.2.11 (Ubuntu) PHP/5.2.6-3ubuntu4.2 with Suhosin-Patch
[SERVER_NAME] => 192.168.0.43
[SERVER_ADDR] => 192.168.0.43
[SERVER_PORT] => 80
[REMOTE_ADDR] => 192.168.0.59
[DOCUMENT_ROOT] => /var/www/web_index
[SERVER_ADMIN] => webmaster@localhost
[SCRIPT_FILENAME] => /home/bridge/public_html/sample.php
[REMOTE_PORT] => 5576
[GATEWAY_INTERFACE] => CGI/1.1
[SERVER_PROTOCOL] => HTTP/1.1
[REQUEST_METHOD] => GET
[QUERY_STRING] => a=x
[REQUEST_URI] => /~bridge/sample.php?a=x
[SCRIPT_NAME] => /~bridge/sample.php
[PHP_SELF] => /~bridge/sample.php
[REQUEST_TIME] => 1257858849
[argv] => Array
(
[0] => a=x
)

[argc] => 1
)
Array
(
)
5.1 CURLOPT_FOLLOWLOCATION
 
CURLOPT_FOLLOWLOCATION의 값을 TRUE로 설정하면 서버가 HTTP 헤더로 보내는 "Location :"헤더의 내용을 추한다. (이것은 반복적으로 수행됩니다. CURLOPT_MAXREDIRS가 지정되지 않은 경우, 보내는 "Location : "헤더의 내용을 계속 추적 함에 주의해야 한다.)

sample2.php
echo("기본형");
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL,
http://localhost/sample.php);
$html = curl_exec($ch);


echo("return " . $html);

curl_close($ch);

결과
기본형
return 1

위의 소스는 sample.php의 소스에서 [sample.php?a=x]로 이동하라고 되어 있지만, 이동하지 않는 다는 것을 알 수 있다.

그럼 CURLOPT_FOLLOWLOCATION옵션을 TRUE로 설정 해보면...

sample3.php
echo("CURLOPT_FOLLOWLOCATION 옵션 예제");
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, http://localhost/sample.php);
curl_seropt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
$html = curl_exec($ch);


echo("return " . $html);

curl_close($ch);

결과
CURLOPT_FOLLOWLOCATION 옵션 예제
webTech입니다.
Array
(
[HTTP_ACCEPT] => image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
[HTTP_ACCEPT_LANGUAGE] => ko
[HTTP_ACCEPT_ENCODING] => gzip, deflate
[HTTP_USER_AGENT] => Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; GTB6; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; InfoPath.2; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
[HTTP_HOST] => 192.168.0.43
[HTTP_CONNECTION] => Keep-Alive
[PATH] => /usr/local/bin:/usr/bin:/bin
[SERVER_SIGNATURE] => Apache/2.2.11 (Ubuntu) PHP/5.2.6-3ubuntu4.2 with Suhosin-Patch Server at 192.168.0.43 Port 80

[SERVER_SOFTWARE] => Apache/2.2.11 (Ubuntu) PHP/5.2.6-3ubuntu4.2 with Suhosin-Patch
[SERVER_NAME] => 192.168.0.43
[SERVER_ADDR] => 192.168.0.43
[SERVER_PORT] => 80
[REMOTE_ADDR] => 192.168.0.59
[DOCUMENT_ROOT] => /var/www/web_index
[SERVER_ADMIN] => webmaster@localhost
[SCRIPT_FILENAME] => /home/bridge/public_html/sample.php
[REMOTE_PORT] => 5576
[GATEWAY_INTERFACE] => CGI/1.1
[SERVER_PROTOCOL] => HTTP/1.1
[REQUEST_METHOD] => GET
[QUERY_STRING] => a=x
[REQUEST_URI] => /~bridge/sample.php?a=x
[SCRIPT_NAME] => /~bridge/sample.php
[PHP_SELF] => /~bridge/sample.php
[REQUEST_TIME] => 1257858849
[argv] => Array
(
[0] => a=x
)

[argc] => 1
)
Array
(
)



return 1

위에 보는 것 처럼 [sample.php?a=x]로 이동한 것을 알 수 있다.
반응형
반응형

여기서 말하는 계층형 게시판이란 게시글의 답글, 답글의 답글 등 계층적인 구조를 가진 게시판을 말하고, 구현하는 로직이 복잡하여 현재 대부분의 포털 사이트에서 사용되는 게시판에서는 지원되지 않고 있다.

 


계층적 질의  


관계 형 데이터베이스는 2차원 테이블 구조에 의해 모든 데이터를 저장한다. 하지만 실무에서는 기업의 조직도나 게시판 구조와 같은 계층적인 데이터 구조를 많이 사용하고 있다. 따라서 평면적인 구조를 가지는 테이블에서도 계층적인 데이터를 조회할 수 있는 방법이 필요하다.

특히, 게시판 구조에서 특정 게시물의 답글이나 답글의 답글 등을 구현하고자 할 때, 복잡한 연산을 필요로 한다. 하지만 CUBRID나 Oracle, MS-SQL에서 지원하는 계층적 질의를 사용하면 매우 간단하게 구현할 수 있다.


다음 질의에서 article 테이블에 삽입될 데이터들의 구조는 아래 [그림 1]과 같다.

[그림 1]

구문
SELECT column_list
FROM table_joins | tables
[WHERE join_conditions and/or filtering_conditions]
[START WITH condition]
CONNECT BY [NOCYCLE] condition


아래는 게시물의 정보를 담을 수 있는 post 테이블을 생성하고, 관련된 post 정보를 입력하는 예제이다.

-- 게시글 테이블 생성
CREATE TABLE "article"(
     "mgrid" integer,
     "id" INTEGER AUTO_INCREMENT NOT NULL,
     "subject" character varying(50) NOT NULL,
     "writer" character varying(10) NOT NULL,
     "date" DATE NOT NULL,
     "count" INTEGER DEFAULT 0 NOT NULL
);

-- LEVEL_1 게시글 추가
INSERT INTO "article" ("subject", "writer", "date") VALUES ('1번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("subject", "writer", "date") VALUES ('2번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("subject", "writer", "date") VALUES ('3번 글', '문학청년', SYSDATE); 
     
-- LEVEL_2 게시글 추가
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (1, '1-1번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (2, '2-1번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (2, '2-2번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (2, '2-3번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (3, '3-1번 글', '문학청년', SYSDATE); 
     
-- LEVEL_3 게시글 추가
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (6, '2-2-1번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (7, '2-3-1번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (8, '3-1-1번 글', '문학청년', SYSDATE); 
     
-- LEVEL_4 게시글 추가
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (9, '2-2-1-1번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (9, '2-2-1-2번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (9, '2-2-1-3번 글', '문학청년', SYSDATE);
INSERT INTO "article" ("mgrid", "subject", "writer", "date") VALUES (9, '2-2-1-4번 글', '문학청년', SYSDATE);


[그림 1]을 다시 한번 더 살펴 보면 이해가 쉬울 것이다. 그렇다면 지금 데이터들을 계층적 질의를 사용하지 않고 조회해 본 뒤, 결과 값을 확인하고 계층적 질의를 사용하여 조회해보자.

-- 계층적 질의를 사용하지 않음
SELECT * FROM article;

-- 계층적 질의를 사용함
SELECT * FROM article
START WITH mgrid IS NULL
CONNECT BY PRIOR id=mgrid
ORDER SIBLINGS BY id DESC;


결과 테이블
NO
mgrid
id
subject
writer
date
count
1
(NULL)
1
1번 글
문학청년
2009-12-22
0
2
(NULL)
2
2번 글
문학청년
2009-12-22
0
3
(NULL)
3
3번 글
문학청년
2009-12-22
0
4
1
4
1-1번 글
문학청년
2009-12-22
0
5
2
5
2-1번 글
문학청년
2009-12-22
0
6
2
6
2-2번 글
문학청년
2009-12-22
0
7
2
7
2-3번 글
문학청년
2009-12-22
0
8
3
8
3-1번 글
문학청년
2009-12-22
0
9
6
9
2-2-1번 글
문학청년
2009-12-22
0
10
7
10
2-3-1번 글
문학청년
2009-12-22
0
11
8
11
3-1-1번 글
문학청년
2009-12-22
0
12
9
12
2-2-1-1번 글
문학청년
2009-12-22
0
13
9
13
2-2-1-2번 글
문학청년
2009-12-22
0
14
9
14
2-2-1-3번 글
문학청년
2009-12-22
0
15
9
15
2-2-1-4번 글
문학청년
2009-12-22
0
[계층적 질의를 사용하지 않았을 때]

NO
mgrid
id
subject
writer
date
count
1
(NULL)
3
3번 글
문학청년
2009-12-22
0
2
3
8
3-1번 글
문학청년
2009-12-22
0
3
8
11
3-1-1번 글
문학청년
2009-12-22
0
4
(NULL)
2
2번 글
문학청년
2009-12-22
0
5
2
7
2-3번 글
문학청년
2009-12-22
0
6
7
10
2-3-1번 글
문학청년
2009-12-22
0
7
2
6
2-2번 글
문학청년
2009-12-22
0
8
6
9
2-2-1번 글
문학청년
2009-12-22
0
9
9
15
2-2-1-4번 글
문학청년
2009-12-22
0
10
9
14
2-2-1-3번 글
문학청년
2009-12-22
0
11
9
13
2-2-1-2번 글
문학청년
2009-12-22
0
12
9
12
2-2-1-1번 글
문학청년
2009-12-22
0
13
2
5
2-1번 글
문학청년
2009-12-22
0
14
(NULL)
1
1번 글
문학청년
2009-12-22
0
15
1
4
1-1번 글
문학청년
2009-12-22
0
[계층적 질의를 사용했을 때]

결과 테이블에서 각 행들의 mgrid 컬럼을 보면 자신의 상위 post의 id가 저장되어 있다. 만약에 post 자신이 가장 최상위 post라면 mgrid 컬럼은 null 값을 갖는다. 단순히 질의 결과만을 보면 이해가 어려울 수도 있으니 아래 [그림 2]와 같이 실제로 게시판에 적용된 모습과 출력된 질의 결과물을 비교해보자.

[그림 2]

위의 예제는 LEVEL_4 까지만 보여지지만, 그 이상의 레벨도 쉽게 구현할 수가 있다. 이러한 기능을 구현하고자 할 때, 계층적 질의를 사용하지 않는다면 쿼리가 매우 복잡해지거나 어플리케이션 상에서 별도로 로직 처리를 해줘야 할 것이다.

그렇다면 지금부터 각 구문에 대해 좀 더 자세히 알아보도록 하자.


(1) START WITH … CONNECT BY 절

START WITH 절
START WITH 절은 계층 관계가 시작되는 루트 행(root row)을 지정하기 위한 것으로, START WITH 절 다음에 계층 관계를 검색하기 위한 조건 식을 포함한다. 만약, START WITH 절 다음에 위치하는 조건 식이 생략되면 대상 테이블 내에 존재하는 모든 행을 루트 행으로 간주한다.

CONNECT BY [NOCYCLE] PRIOR 절
  PRIOR
    CONNECT BY 조건 식은 한 쌍의 행에 대한 상-하 계층 관계(부모-자식 관계)를 정의 하기 위한 것으로, 조건 식 내에서 하나는 부모(parent)로 지정되고, 다른 하나는 자식(child)으로 지정된다. 이처럼 행 간의 부모-자식 간 계층 관계를 정의하기 위하여 CONNECT BY 조건식 내에 PRIOR 연산자를 이용하여 부모 행의 컬럼 값을 지정한다. 즉, 부모 행의 컬럼 값과 같은 컬럼 값을 가지는 모든 행은 자식 행이 된다.

  NOCYCLE
    CONNECT BY 절의 조건 식에 따른 계층 질의 결과는 루프를 포함할 수 있으며, 이것은 계층 트리를 생성할 때 무한 루프를 발생시키는 원인이 될 수 있다. 따라서, CUBRID는 루프를 발견하면 기본적으로 오류를 반환하고, 특수 연산자인 NOCYCLE이 CONNECT BY 절에 명시된 경우에는 오류를 발생시키지 않고 해당 루프에 의해 검색된 결과를 출력한다.


(2) ORDER SIBILINGS BY 절

ORDER SIBLINGS BY 절은 계층 질의 결과 값들의 계층 정보를 유지하면서 특정 컬럼을 기준으로 오름차순 또는 내림차순으로 데이터를 정렬하기 위해 사용할 수 있는 절로서, 동일한 부모를 가진 자식 행들을 정렬할 수 있다. 이처럼 계층적 질의 문에서 데이터의 계층적 순서를 파악하기 위해 사용되는 ORDER BY 절의 구문은 다음과 같다.

구문
ORDER SIBLINGS BY col_1 [ASC|DESC] [, col_2 [ASC |DESC] […[, col_n [ASC|DESC]]…]]


설명
계층 질의 결과는 기본적으로 ORDER BY 절에 명시된 컬럼 리스트를 따라 정렬된 부모와 그 부모의 자식 노드들이 연속적으로 출력되며, 동일 부모를 가지는 형제는 명시된 정렬 순서에 따라 정렬되어 출력된다.


계층 질의 문에서 사용 가능한 표현 칼럼  

LEVEL
설명
LEVEL은 계층 질의 결과 행의 깊이 레벨(depth)을 나타내는 표현 컬럼이다. 루트 노드의 LEVEL은 1이며, 하위 자식 노드의 LEVEL은 2가 된다.
LEVEL 표현 컬럼은 SELECT문 내의 WHERE절, ORDER BY절, GROUP BY… HAVING절에서 사용 가능하며, 집계 함수를 이용하는 구문에서도 사용 가능하다.

SQL 문

-- LEVEL이 2인 데이터를 조회하는 질의 문

SELECT "mgrid", "id", "subject", "writer", "date", "count", LEVEL FROM article
WHERE LEVEL=2
START WITH mgrid IS NULL
CONNECT BY PRIOR id=mgrid
ORDER SIBLINGS BY id DESC;



결과 테이블
NO
mgrid
id
subject
writer
date
count
level
1
3
8
3-1번 글
문학청년
2009-12-22
0
2
2
2
7
2-3번 글
문학청년
2009-12-22
0
2
3
2
6
2-2번 글
문학청년
2009-12-22
0
2
4
2
5
2-1번 글
문학청년
2009-12-22
0
2
5
1
4
1-1번 글
문학청년
2009-12-22
0
2

결과 테이블에 나온 LEVEL 칼럼을 [그림 1]의 LEVEL과 비교해서 살펴보자.


CONNECT_BY_ISLEAF
설명
CONNECT_BY_ISLEAF는 계층 질의 결과 행이 잎 노드(Leaf node)인지 가르키는 표현 컬럼이다. 계층 구조에서 현재 행이 잎 노드이면 1을 반환하고, 그렇지 않으면 0을 반환한다.

SQL 문
-- 현재 행이 잎 노드인 것만 조회하는 질의 문
SELECT "mgrid", "id", "subject", "writer", "date", "count", CONNECT_BY_ISLEAF FROM article
WHERE CONNECT_BY_ISLEAF=1
START WITH mgrid IS NULL
CONNECT BY PRIOR id=mgrid
ORDER SIBLINGS BY id DESC;


결과 테이블
NO
mgrid
id
subject
writer
date
count
connect_by_isleaf
1
8
11
3-1-1번 글
문학청년
2009-12-22
0
1
2
7
10
2-3-1번 글
문학청년
2009-12-22
0
1
3
9
15
2-2-1-4번 글
문학청년
2009-12-22
0
1
4
9
14
2-2-1-3번 글
문학청년
2009-12-22
0
1
5
9
13
2-2-1-2번 글
문학청년
2009-12-22
0
1
6
9
12
2-2-1-1번 글
문학청년
2009-12-22
0
1
7
2
5
2-1번 글
문학청년
2009-12-22
0
1
8
1
4
1-1번 글
문학청년
2009-12-22
0
1

그 밖에 활용 방법
네이버나 다음에서 사용되는 게시판은 삭제 하고자 하는 게시글에 답글이 있을 경우, 삭제가 되지 않는다. 만약에 자신이 구현한 게시판에서도 그러한 기능을 구현하고자 한다면, CONNECT_BY_ISLEAF 표현 컬럼의 결과 값이 1일 경우에만 삭제가 되도록 어플리케이션 상에서 구현하면 된다.



계층 질의 문에서 사용 가능한 연산자  

계층 질의 문에서는 컬럼 연산을 위해 아래와 같은 특수 연산자를 사용할 수 있으며, 이러한 특수 연산자를 이용하여 해당 컬럼의 부모 행 값 또는 루트 행의 값을 반환할 수 있다.

CONNECT_BY_ROOT 연산자
설명
CONNECT_BY_ROOT은 컬럼 값으로 루트 행의 값을 반환한다.
이 연산자는 SELECT문 내의 WHERE절 및 ORDER BY절에서 사용할 수 있다.

SQL 문
-- id가 2번인 부모 글의 자식 글들을 조회하라.
SELECT "mgrid", "id", "subject", "writer", "date", "count", CONNECT_BY_ROOT id AS parent FROM article
WHERE CONNECT_BY_ROOT id = 2
START WITH mgrid IS NULL
CONNECT BY PRIOR id=mgrid
ORDER SIBLINGS BY id;


결과 테이블
NO
mgrid
id
subject
writer
date
count
parent
1
(NULL)
2
2번 글
문학청년
2009-12-22
0
2
2
2
5
2-1번 글
문학청년
2009-12-22
0
2
3
2
6
2-2번 글
문학청년
2009-12-22
0
2
4
6
9
2-2-1번 글
문학청년
2009-12-22
0
2
5
9
12
2-2-1-1번 글
문학청년
2009-12-22
0
2
6
9
13
2-2-1-2번 글
문학청년
2009-12-22
0
2
7
9
14
2-2-1-3번 글
문학청년
2009-12-22
0
2
8
9
15
2-2-1-4번 글
문학청년
2009-12-22
0
2
9
2
7
2-3번 글
문학청년
2009-12-22
0
2
10
7
10
2-3-1번 글
문학청년
2009-12-22
0
2


PRIOR 연산자
설명
PRIOR 연산자는 컬럼 값으로 부모 행의 값을 반환한다.
하지만, 루트 행에 대해서는 컬럼 값으로 NULL 값을 반환하며, SELECT문 내의 WHERE절. ORDER BY절, 및 CONNECT BY 절에서 사용할 수 있다.

SQL 문
-- id값이 14인 게시글의 상위 게시글에 대한 정보를 조회하라.
SELECT PRIOR "id", PRIOR "subject", PRIOR "writer", PRIOR "date", PRIOR "count" FROM "article"
WHERE id=14
START WITH mgrid IS NULL
CONNECT BY PRIOR id=mgrid
ORDER SIBLINGS BY id;


결과 테이블
NO
prior id
prior subject
prior writer
prior date
prior count
1
9
2-2-1번 글
문학청년
2009-12-22
0



계층 질의 문에서 사용 가능한 함수  

CUBRID는 계층 구조를 가지는 데이터를 출력하기 위하여 다음의 함수를 지원한다.

SYS_CONNECT_BY_PATH 함수
구문
SYS_CONNECT_BY_PATH (column_name, separator_char)


설명
SYS_CONNECT_BY_PATH 함수는 루트 행으로부터 해당 행까지의 상-하 관계의 PATH를 문자열로 반환하는 함수이다. 이 때, 함수의 인자로 지정되는 COLUMN과 구분자는 문자형 타입이어야 하며, 각 PATH는 지정된 구분자에 의해 구분되어 연쇄적으로 출력된다.
이 함수는 SELECT문 내의 WHERE절과 ORDER BY절에서 사용할 수 있다.

SQL 문
-- 구분자를 이용하여 루트 행으로부터 해당 행까지 PATH를 확인하기
SELECT "mgrid", "id", "subject", SYS_CONNECT_BY_PATH(subject, '/') AS hierarchy FROM article
     START WITH mgrid IS NULL
     CONNECT BY PRIOR id=mgrid
     ORDER SIBLINGS BY id;


결과 테이블
NO
mgrid
id
subject
hierarchy
1
(NULL)
1
1번 글
/1번 글
2
1
4
1-1번 글
/1번 글/1-1번 글
3
(NULL)
2
2번 글
/2번 글
4
2
5
2-1번 글
/2번 글/2-1번 글
5
2
6
2-2번 글
/2번 글/2-2번 글
6
6
9
2-2-1번 글
/2번 글/2-2번 글/2-2-1번 글
7
9
12
2-2-1-1번 글
/2번 글/2-2번 글/2-2-1번 글/2-2-1-1번 글
8
9
13
2-2-1-2번 글
/2번 글/2-2번 글/2-2-1번 글/2-2-1-2번 글
9
9
14
2-2-1-3번 글
/2번 글/2-2번 글/2-2-1번 글/2-2-1-3번 글
10
9
15
2-2-1-4번 글
/2번 글/2-2번 글/2-2-1번 글/2-2-1-4번 글
11
2
7
2-3번 글
/2번 글/2-3번 글
12
7
10
2-3-1번 글
/2번 글/2-3번 글/2-3-1번 글
13
(NULL)
3
3번 글
/3번 글
14
3
8
3-1번 글
/3번 글/3-1번 글
15
8
11
3-1-1번 글
/3번 글/3-1번 글/3-1-1번 글



계층 질의 문 실전 응용  

계층 질의 문은 단순히 하나의 테이블에서의 계층적 구조가 아닌 다른 테이블과 조인하여 구현할 수도 있다. 예를 들어 특정 게시글에 포함된 덧글의 계층적 구조도 매우 간단하게 표현할 수 있다.

그럼 지금, 덧글의 정보를 담는 comment 테이블을 생성하고, 2번 게시글에 포함된 덧글들을 삽입하고, 조회하는 질의 문을 작성해보자.


[그림 3]은 2번 글에 포함된 덧글의 계층 구조이다.

[그림 3]

테이블 생성 및 데이터 삽입 SQL문

-- comment 테이블 생성
CREATE TABLE "comment"(
     "artid" INTEGER NOT NULL,
     "mgrid" integer,
     "id" INTEGER AUTO_INCREMENT NOT NULL,
     "content" character varying(200) NOT NULL,
     "writer" character varying(20) NOT NULL,
     "date" DATE NOT NULL
);

-- LEVEL_1 덧글 추가
INSERT INTO "comment" ("artid", "content", "writer", "date")

VALUES (2, '1번 덧글', '문학청년', sysdate);


INSERT INTO "comment" ("artid", "content", "writer", "date")

VALUES (2, '2번 덧글', '문학청년', sysdate);


INSERT INTO "comment" ("artid", "content", "writer", "date")

VALUES (2, '3번 덧글', '문학청년', sysdate);


INSERT INTO "comment" ("artid", "content", "writer", "date")

VALUES (2, '4번 덧글', '문학청년', sysdate); 
     


-- LEVEL_2 덧글 추가
INSERT INTO "comment" ("artid", "mgrid", "content", "writer", "date")

VALUES (2, 2, '2-1번 덧글', '문학청년', sysdate);


INSERT INTO "comment" ("artid", "mgrid", "content", "writer", "date")

VALUES (2, 3, '3-1번 덧글', '문학청년', sysdate);


INSERT INTO "comment" ("artid", "mgrid", "content", "writer", "date")

VALUES (2, 4, '4-1번 덧글', '문학청년', sysdate); 
     


-- LEVEL_3 덧글 추가
INSERT INTO "comment" ("artid", "mgrid", "content", "writer", "date")

VALUES (2, 5, '2-1-1번 덧글', '문학청년', sysdate);


INSERT INTO "comment" ("artid", "mgrid", "content", "writer", "date")

VALUES (2, 5, '2-1-2번 덧글', '문학청년', sysdate);


INSERT INTO "comment" ("artid", "mgrid", "content", "writer", "date")

VALUES (2, 5, '2-1-3번 덧글', '문학청년', sysdate);


INSERT INTO "comment" ("artid", "mgrid", "content", "writer", "date")

VALUES (2, 5, '2-1-4번 덧글', '문학청년', sysdate); 
     


-- LEVEL_4 덧글 추가
INSERT INTO "comment" ("artid", "mgrid", "content", "writer", "date")

VALUES (2, 10, '2-1-3-1번 덧글', '문학청년', sysdate); 
     
-- 2번 글에 포함된 덧글 조회
SELECT com."mgrid", com."id", com."content", com."writer", com."date" 
FROM comment com, article art 
WHERE art.id = com.artid AND art.id = 2 
START WITH com.mgrid IS NULL 
CONNECT BY PRIOR com.id=com.mgrid 
ORDER SIBLINGS BY com.id DESC;



결과 테이블
NO
mgrid
id
content
wrtier
date
1
(NULL)
4
4번 덧글
문학청년
2009-12-23
2
4
7
4-1번 덧글
문학청년
2009-12-23
3
(NULL)
3
3번 덧글
문학청년
2009-12-23
4
3
6
3-1번 덧글
문학청년
2009-12-23
5
(NULL)
2
2번 덧글
문학청년
2009-12-23
6
2
5
2-1번 덧글
문학청년
2009-12-23
7
5
11
2-1-4번 덧글
문학청년
2009-12-23
8
5
10
2-1-3번 덧글
문학청년
2009-12-23
9
10
12
2-1-3-1번 덧글
문학청년
2009-12-23
10
5
9
2-1-2번 덧글
문학청년
2009-12-23
11
5
8
2-1-1번 덧글
문학청년
2009-12-23
12
(NULL)
1
1번 덧글
문학청년
2009-12-23

설명
위의 예제는 article 테이블과 comment 테이블을 조인한 것 말고는 일반적인 계층적 질의와 문법상 차이가 없다.


계층 질의 문의 성능  

비록, CONNECT BY 절을 이용한 계층 질의 문이 짧고 간편하기는 하지만 질의 처리 속도 측면에서는 한계를 가지고 있음을 명심해야 한다. 만약 질의 문 수행 결과가 대상 테이블의 모든 행을 출력하는 경우라면, CONNECT BY 절을 이용한 계층 질의 문은 루프 감지, 표현 컬럼의 예약 등의 내부적인 처리로 인해 오히려 일반적인 질의 문보다 성능이 낮을 것이다. 반대로 대상 테이블에 대해 일부 행만 출력하는 경우라면 CONNECT BY 절을 이용한 계층 질의 문의 성능이 높을 것이다. 예를 들어, 2만개의 레코드를 가지는 테이블에 대하여 약 1000개의 레코드를 포함하는 서브 트리를 검색하는 경우라면, CONNECT BY 절을 포함한 SELECT 문은 UNION ALL을 결합한 SELECT문보다 약 30%의 성능 향상을 기대할 수 있다.

 

 

반응형

'DBMS > oracle' 카테고리의 다른 글

오라클 포트변경  (0) 2010.07.07
oracle for eclipse 셋팅 과정  (1) 2010.07.07

+ Recent posts