 |  |
|
난이도 : 중급 Matthias Nicola, Senior Software Engineer, IBM Silicon Valley Laboratory Uttam Jain, Advisory Software Engineer, IBM
2008 년 2 월 19 일 IBM® DB2® 9.5 for Linux, Unix and Windows의 가장 특징적인 신기능들 중 하나는 XML 업데이트 기능입니다. 이전 버전인 DB2 9은 XML 데이터를 저장 및 색인하고, SQL/XML과 XQuery 언어를 사용하여 쿼리 하는데 pureXML™ 지원을 도입했습니다. XML 문서에 대한 수정 작업이 데이터베이스 서버 밖에서 수행되었으며, 뒤이어 DB2의 전체 문서에 대한 업데이트가 수행되었습니다. 새로운 DB2 9.5는 XQuery 표준 확장인 XQuery Update Facility를 도입하여, 여러분이 XML 문서 내의 개별 엘리먼트와 애트리뷰트를 수정, 삽입, 삭제할 수 있도록 했습니다. 이는 XML 데이터의 업데이트를 더욱 수월하게 하고, 고성능을 제공합니다. 이 글에서는 새로운 XML 업데이트 기능을 소개하고, 전형적인 XML 업데이트 연산 예제를 제공하며, 일반적인 실수를 피하는 방법을 설명합니다.
머리말과 배경
여러 가지 측면에서 볼 때, DB2 9 pureXML은 XML에 대한 데이터베이스 관리 지원의 관점에서 업계를 앞서가고 있다. 실제로, IBM이 pureXML 기술을 개발하고 있을 때, XML과 XQuery 표준을 정의 및 관리하는 기구인 World Wide Web Consortium (W3C)은 XML을 위한 표준 업데이트 메커니즘을 정의하기 시작했다. XML 업데이트 표준의 부재로, DB2 9은 아래 예제에서처럼 SQL UPDATE 문을 통해 전체 문서 대체를 지원한다:
create table xmlcustomer (cid bigint, info XML);
update xmlcustomer
set info = '<customerinfo><name>John Doe</name>...</customerinfo>'
where cid = 1783;
|
UPDATE 문의 set 구문은 해당 행의 기존 문서를 새로운 문서로 대체한다. 여기에서 문서는 리터럴 값으로 제공되지만, 매개변수 마커나 호스트 변수를 통해서 데이터베이스로 전달될 수도 있다. DB2 9에서, XML 문서의 개별 엘리먼트나 애트리뷰트를 수정할 필요가 있는 애플리케이션은 애플리케이션 자체에서 이를 수행하곤 한다. 이는 다음과 같은 프로세싱 시퀀스를 만들어 낸다. (그림 1)
- 애플리케이션은 SQL 또는 XQuery 문을 제출하여 XML 문서를 읽는다.
- 문서는 데이터베이스에서 검색되어 텍스트 포맷으로 직렬화 된다.
- 문서는 클라이언트에 전송된다.
- 애플리케이션은 XML 문서를 파싱하는데, 주로 문서 객체 모델(DOM)을 사용한다.
- 애플리케이션은 DOM API를 사용하여 문서를 수정한다.
- 문서는 클라이언트 애플리케이션에서 재 직렬화 된다.
- 애플리케이션은 SQL UPDATE 문을 제출하고, 업데이트 된 문서를 데이터베이스 서버로 전송한다.
- 데이터베이스 서버는 업데이트 된 XML 문서를 파싱하고, 오래된 문서를 대체한다.
그림 1. DB2 9에서 XML 문서 업데이트 하기
XML 문서의 가져오기, 파싱, 수정 프로세스는 데이터베이스 서버에서 실행되는 저장 프로시저에 캡슐화 된다. 프로세싱 단계는 그림 1과 같지만, 문서는 클라이언트로 전달되지 않는다.
DB2 9.5의 새로운 업데이트 기능은 이러한 과정을 한 단계로 줄였다. 애플리케이션은 임베디드 XQuery transform 식과 함께 SQL UPDATE 문을 DB2 서버로 보낸다. (그림 2) 이 식은 XML 데이터를 업데이트 하는 새로운 XQuery 표준의 일부이다.
그림 2. DB2 9.5에서 XML 문서 업데이트 하기
DB2 9.5가 UPDATE 문을 실행할 때, 관련 문서들을 배치시키고 지정된 엘리먼트나 애트리뷰트를 수정한다. 이는 DB2 스토리지 레이어 안에서 발생하는데, 다시 말해서, 이 문서는 파싱이나 직렬화 없이 모든 시간을 DB2의 내부 계층 XML 포맷으로 머물러 있다. 동시성 컨트롤과 로깅은 문서 전체에 발생한다. 결국, 이 새로운 업데이트 프로세스는 그림 1의 프로세스 보다 2배에서 4배 빠르다.
새로운 업데이트 기능으로, XML 문서에서 노드 레벨 수정을 수행할 수 있다. 대부분의 경우, 여러분이 수정하는 노드는 엘리먼트 또는 애트리뷰트다. 다음과 같이 할 수 있다:
- 노드의 값을 바꾼다.
- 노드를 새로운 노드로 대체한다.
- 새로운 노드를 삽입한다. (주어진 노드의 앞 또는 뒤 같이 특정 위치도 가능하다.)
- 노드를 삭제한다.
- 노드에 새로운 이름을 짓는다.
- 단일 UPDATE 문에서 문서에 있는 다중 노드들을 수정한다.
- 단일 UPDATE 문에서 다중 문서들을 업데이트 한다.
이제부터는 XQuery transform 식으로 이 같은 XML 업데이트를 수행하는 방법을 설명하겠다. UPDATE 문에 transform을 삽입하여 디스크 상의 데이터를 영구 수정하고, 쿼리에서 "즉각적으로" XML 데이터를 수정하는 방법을 설명하겠다. 후자는 애플리케이션이 데이터베이스에 있는 것과 다른 XML 포맷을 받아야 할 경우 유용하다.
샘플 데이터
새로운 DB2 9.5 XML 업데이트 기능을 설명하기 위해, 다음과 같은 CREATE TABLE과 INSERT 문에 의해서 정의된 샘플 테이블과 데이터가 사용된다. xmlcustomer 테이블은 두 개의 컬럼 유형인 bigint와 XML을 갖고 있고, 두 개의 데이터 행을 갖고 있다. 이 행은 cid 값 1000과 1001로 구분된다. 이 글에 사용된 샘플 데이터와 모든 업데이트는 텍스트 파일로서 다운로드 가능하다. (다운로드)
create table xmlcustomer (cid bigint, info XML); |
|
insert into xmlcustomer values (1000, '
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo>'); |
|
insert into xmlcustomer values (1001, '
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo>'); |
|
샘플 문서의 계층 구조는 그림 3과 같다. XML 쿼리나 업데이트를 설계할 때 이러한 트리 구조를 염두 해 두는 것이 좋다.
그림 3. 샘플 문서의 트리 구조
XML 데이터 업데이트 -- 간단한 예제
XML 업데이트의 간단한 예제로서 Update 1로 시작한다. 특정 고객의 전화 번호를 수정한다.
고급 레벨에서, SQL UPDATE 문의 "update… set…. where…" 이라는 친숙한 신택스를 보게 된다. 또한, "set" 구문이 새로운 값을 XML 컬럼 "info"에 할당하고, 이 새로운 값은 XMLQUERY 함수의 결과라는 것도 알 수 있다.
여러분은 아마도 SQL/XML을 사용한 XML 데이터 쿼리에서 XMLQUERY 함수를 기억할 것이다. XMLQUERY는 SQL 쿼리의 SELECT 문에 사용되어 결과 세트의 행에 있는 XML 문서에 기반하여 XML 값을 추출 또는 구현한다. 이를 위해, XMLQUERY 함수를 사용하여 SQL 문에 XQuery 식을 삽입할 수 있다. 이 예제에서, XMLQUERY 함수는 주어진 XML 문서를 수정하는데 사용된다. Update 1의 XQuery 식은 transform 식이다. transform 식은 XQuery를 확장하여 XML 문서를 수정하는 새로운 표준인 XQuery Update Facility의 핵심에 있다.
transform 식은 옵션 "transform" 키워드로 시작하여 그 뒤를 COPY, MODIFY, RETURN 문이 잇는다. transform 식의 개념은 COPY 문이 XML 컬럼(info)의 인풋 문서를 변수(이 경우, $new)에 할당하고, MODIFY 문이 한 개 이상의 업데이트 연산을 그 변수에 적용하며, 마지막으로 RETURN 구문이 transform 식의 결과를 만들어 낸다.
Update 1
update xmlcustomer
set info = xmlquery( 'transform
copy $new := $INFO
modify do replace value of $new/customerinfo/phone with "905-477-9011"
return $new')
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country=“Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country=“Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>90111</zipcode>
</addr>
<phone type="work">905-477-9011</phone>
</customerinfo> |
|
MODIFY 문은 실제 발생하는 업데이트 연산을 정의하기 때문에 특별히 흥미롭다. 이 글의 다양한 MODIFY 예제를 참조하라. 여기에서, "phone" 엘리먼트의 값은 새로운 값(905-477-9011)으로 대체된다. Update 1과 다른 많은 전형적인 유스 케이스에서, RETURN 문은 수정된 문서를 보유하고 있는 변수를 리턴한다. 하지만, 리턴은 훨씬 더 복잡한 식이고, 엘리먼트 구조와 FLWOR 식을 포함하고 있다. 마찬가지로, COPY 문의 오른쪽 부분은 인풋 문서를 보유하고 있는 변수이고, 우리 $INFO의 경우는 그것 보다 약간 더 복잡하다. COPY 문의 오른쪽은 하나의 노드로 계산되어야 하는데, 빈 시퀀스 또는 한 개 이상의 아이템의 시퀀스가 되어서는 안된다는 의미이다. 이 단일 노드는 자손들을 갖고 있는데, 이것이 전체 XML 문서의 루트가 될 수 있다는 의미이다. (그리고 종종 루트가 된다.)
"transform" 키워드가 옵션이기 때문에, 지금부터는 생략하기로 한다.
SQL 쿼리에서 문서 업데이트
모순처럼 들리겠지만 매우 쉽다. transform 식은 쿼리에 사용되어, 데이터베이스에서 읽고 애플리케이션으로 전달하면서 XML 문서를 수정한다. Update 2는 Update 1과 같은 XMLQUERY 함수, transform 식, WHERE 문을 가진 SQL SELECT 문이다. Update 1이 디스크 상의 문서에 영구적이고 기록된 수정을 하는 반면, Update 2는 원래의 문서는 테이블에 그대로 남겨두고, 쿼리 프로세싱의 일부로서 "임시로" 이를 수정하기만 한다. 이 업데이트는 전화 번호의 지역 번호를 검게 하는데 사용된다.
Update 2
select xmlquery('copy $new := $INFO
modify do replace value of $new/customerinfo/phone with "905-xxx-xxxx"
return $new ')
from xmlcustomer
where cid = 1000;
|
이 글에 사용된 예제 대부분이 SQL UPDATE 문의 transform 식을 보여준다. 하지만, 이것은 Update 2에 나타난 것처럼 쿼리에도 나타난다. 이 글 후반 예제들은 쿼리의 TRANSFROM 식이 문서에서 특정 엘리먼트나 애트리뷰트를 제거하는 데에도 유용하고, 다른 이름 아래에 엘리먼트를 노출하는데도 유용하다는 것을 증명한다. 또한, 새로운 업데이트 기능을 발견하여 테스트 하려면, UPDATE 보다는 SELECT 문에 transform과 함께 XMLQUERY 함수를 삽입하는 것이 더 편리하다. 이러한 방식으로 업데이트의 결과를 보고 확인할 수 있고, 업데이트가 기대하지 않은 일을 수행할 경우 테이블에 있는 데이터를 손상시키지 않아도 된다.
영구 마커를 사용한 업데이트
Update 1과 Update 2에서, "phone" 엘리먼트의 새로운 값은 문장 텍스트에 하드 코딩 되었다. 가끔 여러분은 UPDATE 문을 단 한번 준비하고 컴파일 한 다음, 업데이트를 실행할 때 마다 새로운 값으로 전달해야 한다. 이는 매 실행 때마다 데이터베이스 서버에서 문을 재 컴파일 하지 않도록 해준다. Update 3은 XMLQUERY 함수에 SQL 스타일 매개변수 마커("?")를 사용하여 수행하는 방법이다. 이 매개변수 마커는 변수 ($z)로서 XQuery 식에 전달된다. Update 3 역시 WHERE 문에 두 번째 매개변수 마커를 사용하여 업데이트 된 행을 선택한다.
Update 3
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do replace value of $new/customerinfo/phone with $z
return $new ' passing cast(? as varchar(15)) as "z")
where cid = ?
|
기존 값을 대체하기 위해 여러분이 사용하는 새로운 값 역시도 계산된다. 예를 들어, 기존 전화 번호에 확장을 추가하려면, modify 문은 다음과 같다: modify do replace value of $new/customerinfo/phone with concat($new/customerinfo/phone, "x301"). 또는, 고객이 balance 엘리먼트를 갖고 있을 경우, 다음과 같이 밸런스를 늘릴 수 있다: modify do replace value of $new/customerinfo/balance with $new/customerinfo/balance + 1000.
스키마 밸리데이션을 이용한 업데이트
XML 문서를 수정할 때, 이 문서가 주어진 XML 스키마에 순응하는지 여부를 확인하고 싶을 때가 있다. Update 4는 XMLVALIDATE 함수를 사용하여, 업데이트 된 문서가 "cust.custschema" 구분자를 갖고 있고 XML 스키마에 따라 유효한지를 검사한다. UPDATE 문은 수정된 문서가 유효하지 않을 경우 작동하지 않는다.
Update 4
update xmlcustomer
set info = xmlvalidate(xmlquery('
copy $new := $INFO
modify do replace value of $new/customerinfo/phone
with "905-477-9011"
return $new ')
according to xmlschema id "cust.custschema")
where cid = 1000; |
XML 업데이트 예제
여러분이 지금까지 봐온 예제들은 엘리먼트 노드의 값을 수정하기 위해서 "replace value of" 연산을 사용했다. 다음 예제에서, 여러분은 XML 문서의 엘리먼트와 애트리뷰트 노드를 삭제, 삽입, 재명명 하는 것 같은 연산들을 수행하는 방법을 보게 될 것이다.
노드 삭제
Update 5는 MODIFY 문에서 "delete" 연산을 사용하여 XML 문서에서 "phone" 엘리먼트를 삭제하는 방법을 보여주고 있다. 문서에서 제거하고자 하는 노드에 대한 경로를 지정한다. MODIFY 문을 제외한 모든 것이 이전 업데이트와 동일하다. 실제로, 다음의 예제들에서도, 다른 종류의 업데이트를 수식하기 위해 MODIFY 문만 수정된다.
Update 5
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do delete $new/customerinfo/phone
return $new')
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
</customerinfo> |
|
모든 노드-레벨 업데이트는 애트리뷰트에도 적용될 수 있다. Update 6는 "phone" 엘리먼트에서 "type" 애트리뷰트 같은 애트리뷰트를 삭제하는 예제이다.
Update 6
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do delete $new/customerinfo/phone/@type
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone>963-289-4136</phone>
</customerinfo> |
|
노드 재명명 하기
"rename" 연산을 사용하여 엘리먼트, 애트리뷰트, 또는 프로세싱 명령 노드에 다른 이름을 줄 수 있다. 텍스트 노드 또는 XML 주석 같은 다른 노드는 노드 이름을 갖고 있지 않으므로, 재명명 될 수 없다. 재명명 예제는 Update 7에 나타나 있는데, "addr" 엘리먼트를 "address"로 재명명 한다. 이 업데이트는 SQL WHERE 문을 사용하여 특정 행을 선택하지 않는다. 따라서, 테이블의 모든 행들(문서들)이 수정된다. 확실히, 이는 하나의 문서를 업데이트 하는 것 보다 더 많은 시간이 든다.
Update 7
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do rename $new/customerinfo/addr as "address"
return $new' ) ; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<address country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</address>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
<customerinfo>
<name>David Patterson</name>
<address country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</address>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
노드 바꾸기
Update 8은 "replace" 연산을 사용하여 기존 phone 엘리먼트를 새로운 엘리먼트로 대체하는 방법을 보여주고 있다. "replace" 연산은 "replace value of" 연산과는 다르게 작동한다. 전자는 전체 노드(구 노드는 삭제된다.)를 대체하는 반면, 후자는 노드의 콘텐트만 대체한다. Update 8에서, 전체 새로운 "phone" 엘리먼트가 업데이트의 MODIFY 문에 제공된다.
Update 8
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do replace $new/customerinfo/phone with
<phone type="home">416-123-4567</phone>
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="home">416-123-4567</phone>
</customerinfo> |
|
다중 노드의 값을 대체하기
하나의 update 문으로 같은 문서에 여러 수정을 할 수 있다. Update 9에서 보듯, MODIFY 문에는 다중 update 연산을 포함하고 있다. 여기에는 괄호가 쳐있고, 콤마로 구분된다. 이것으로, 같거나 다른 종류로 두 개 이상의 update 연산을 포함시킬 수 있다. Update 9에서, 두 개의 update 연산들은 "replace value of" 유형이다. 이들은 phone 엘리먼트의 값과 @type 애트리뷰트의 값을 대체한다. 재미있는 점은, 이 두 개의 연산 결과가 Update 8과 같다는 점이다.
Update 9
update xmlcustomer
set info = xmlquery('
copy $new := $INFO
modify (do replace value of $new/customerinfo/phone with "416-123-4567",
do replace value of $new/customerinfo/phone/@type with "home")
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="home">416-123-4567</phone>
</customerinfo> |
|
노드 삽입하기
"insert" 연산으로 엘리먼트나 애트리뷰트 같은 새로운 노드들을 XML 문서에 추가할 수 있다. 문서에 새로운 노드의 위치를 지정할 수 있는 다섯 가지 방법이 있다. 하나의 새로운 엘리먼트를 문서에 삽입한다고 가장해 보자. 다음과 같은 insert 옵션이 있다:
-
insert
element1
into
element2
element1이 element2의 자식이 될 것이라는 것을 의미한다. element2의 기존 자식들 사이에서 element1의 위치는 정해지지 않는다.
-
insert
element1
as last into
element2
element1이 element2의 마지막 자식이 될 것이다.
-
insert
element1
as first into
element2
element1이 element2의 첫 번째 자식이 될 것이다.
-
insert
element1
after
element2
element1이 element2의 형제가 될 것이고, element2 다음에 바로 나타날 것이다.
-
insert
element1
before
element2
element1이 element2의 형제가 될 것이고, element2 앞에 바로 나타날 것이다.
만일 element1 대신 새로운 애트리뷰트를 삽입하면, "into
element2 ", "as last into
element2 ", "as first into
element2 " 연산 모두 같은 결과를 갖게 되고, 이 애트리뷰트는 element2 에 추가된다. XML 데이터 모델은 엘리먼트의 애트리뷰트들 사이에 위치 순서를 정의하지 않는다. 따라서, last, first, before, after는 애트리뷰트의 순서 선정에 영향을 미치지 않는다. before 또는 after
element2 에 애트리뷰트를 삽입하면, 이 애트리뷰트는 element2의 부모에 추가될 것이다.
위에 열거된 옵션에서, element1은 노드의 순서를 계산하는 식도 될 수 있다. 이 경우, 모든 노드들은 지정된 위치에 삽입된다. 또한, element1 은 전체 프라그먼트를 삽입하고자 할 경우, 중첩된 엘리먼트를 가진 XML 프라그먼트의 루트가 될 수 있다.
몇 가지 예제를 보자. Update 10은 두 번째 전화 번호를 샘플 문서에 삽입한다. "customerinfo" 엘리먼트는 "into $new/customerinfo"라고 명령함으로써 새로운 "phone" 엘리먼트의 부모로서 명확히 지정된다.
Update 10
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do insert <phone type="cell">777-555-3333</phone>
into $new/customerinfo
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">416-123-4567</phone>
<phone type="cell">777-555-3333</phone>
</customerinfo> |
|
Update 10의 결과에서, 새로운 phone "엘리먼트"는 "customerinfo"의 마지막 자식으로 판명되었다. 하지만, 위치는 Update 11에서처럼, "as last into"라고 명확히 설정할 경우에만 보장된다.
Update 11
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do insert<phone type="cell">777-555-3333</phone>
as last into $new/customerinfo
return $new' )
where cid = 1000; |
|
새로운 휴대폰 번호를 문서 내 phone 엘리먼트의 첫 번째 엘리먼트로 만들고 싶다면, 이것이 기존 phone 엘리먼트가 처음 나타나기 바로 전에 삽입되도록 요청할 수 있다. Update 12를 참조하라.
Update 12
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do insert <phone type="cell">777-555-3333</phone>
before $new/customerinfo/phone[1]
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="cell">777-555-3333</phone>
<phone type="work">416-123-4567</phone>
</customerinfo> |
|
마찬가지로, 고객 이름 다음에 새로운 phone 엘리먼트를 삽입할 수 있다. (Update 13)
Update 13
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do insert <phone type="cell">777-555-3333</phone>
after $new/customerinfo/name
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<phone type="cell">777-555-3333</phone>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">416-123-4567</phone>
</customerinfo> |
|
Update 14는 transform 식에 관계형 컬럼 값 "CID"를 사용하여 루트 엘리먼트 "customerinfo" 아래 첫 번째 엘리먼트로서 새로운 엘리먼트 "customerid"를 만들어 낸다.
Update 14
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do insert <customerid>{ $CID } </customerid>
as first into $new/customerinfo
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<customerid>1000</customerid>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">416-123-4567</phone>
</customerinfo> |
|
"customerid"가 "customerinfo" 엘리먼트의 애트리뷰트가 되게 하고 싶다면, insert 식에 computed attribute constructor를 포함시켜야 한다. 이것은 attribute 키워드 다음에 원하는 애트리뷰트 이름, 그리고 애트리뷰트 값에 대한 식으로 구성된다. Update 15를 참조하라.
Update 15
update xmlcustomer
set info = xmlquery('
copy $new := $INFO
modify do insert
attribute customerid { $CID }
into $new/customerinfo
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo customerid="1000">
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">416-123-4567</phone>
</customerinfo> |
|
일반적인 함정과 해결책
앞서 언급했던 업데이트 예제들은 단순하고 매력적이다. 여러분이 보다 복잡한 업데이트를 수행할 때면 실수를 저지르거나, DB2가 업데이트 식을 거부하는 상황도 만날 수 있다. 다음 섹션에서는 일반적인 함정과 간단한 해결책을 제시한다.
엘리먼트를 문서 노드에 삽입하기
XML 데이터 모델은 파싱된, 훌륭한 구조의 XML 문서가 문서의 루트 엘리먼트(customerinfo)의 부모인 한 개의 문서 노드를 갖도록 하고 있다. 이 문서 노드는 XML 문서의 텍스트(직렬화) 표현에서는 보이지 않는다.
저지르기 쉬운 실수라고 한다면, 새로운 엘리먼트를 루트 엘리먼트 대신 문서 노드에 삽입하는 것이다. Update 16은 insert가 "into $new/customerinfo" 대신 "into $new"로 지정되었다는 것을 제외하고는 Update 10과 거의 같다. 다시 말해서, Update 16은 새로운 phone 엘리먼트를 "customerinfo" 엘리먼트의 자식으로서가 아닌 형제로서 삽입한다. 결과는 구성이 엉성한 두 개의 엘리먼트(customerinfo와 phone)의 XML 시퀀스이다. DB2의 XML 컬럼에는 구성이 잘된 XML 문서만 포함되므로, 업데이트는 실패한다.
Update 16
update xmlcustomer
set info = xmlquery( '
copy $new := $INFO
modify do insert <phone type="cell">777-555-3333</phone> into $new
return $new' )
where cid = 1000; |
|
Error Message:
SQL20345N The XML value is not a well-formed document with a single root element.
SQLSTATE=2200L
|
| | 원래의 XML 문서: | 거부당한 XML 값: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo>
<phone type="cell">777-555-3333</phone> |
|
반복 노드 수정하기
하나의 XPath 식이 하나의 문서에서 다중 노드들을 구분한다면, 이를 반복 노드라고 한다. 예를 들어, 샘플 테이블의 두 번째 문서에 두 개의 "phone" 엘리먼트가 포함되어 있다. 따라서, /customerinfo/phone 식은 두 엘리먼트의 시퀀스를 나타낸다. "delete" 연산은 노드의 시퀀스에 대해 실행될 수 있는 유일한 연산이다 — 이것은 모든 시퀀스를 지운다. Update 17을 참조하라.
Update 17
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do delete $new/customerinfo/phone
return $new' )
where cid = 1001; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
</customerinfo> |
|
phone 엘리먼트에서 단 하나만 삭제하려면,
modify do delete $new/customerinfo/phone[@type = "work"] 같이 MODIFY 문에서 술어를 사용할 수 있다.
Update 18은 삭제하는 대신 phone 엘리먼트의 값을 대체하려고 하고 있다. 하지만, 런타임 시 DB2는 한 개 이상의 phone 엘리먼트가 있다는 것을 탐지하고, SQL16085N 에러를 리턴한다. DB2 명령어 프롬프트에 ?
SQL16085N을 타이핑 하면, 에러 사유를 볼 수 있다. 에러 메시지에 나타난 사유 코드 "XUTY0008"은 "replace 식의 대상 노드가 단일 노드가 아니다"라는 사실을 나타내고 있다.
Update 18
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do replace value of $new/customerinfo/phone with "444-444-4444"
return $new')
where cid = 1001; |
|
Error message:
SQL16085N The target node of an XQuery "replace value of" expression is not valid.
Error QName=err:XUTY0008. SQLSTATE=10703 |
|
이러한 에러 때문에, 같은 번호를 가진 모든 phone 엘리먼트를 업데이트 할 수 없고, 이는 불합리한 일이다. 같은 방식으로 다중 노드들을 업데이트 해야 한다면, XQuery "for" 식을 사용하여 노드들을 반복한다. (Update 19)
Update 19
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify for $j in $new/customerinfo/phone return
do replace value of $j with "444-444-4444"
return $new' )
where cid = 1001; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">444-444-4444</telephone>
<phone type="cell">444-444-4444</telephone>
</customerinfo> |
|
하지만, 많은 경우, 다중 노드들 중 하나만 수정해야 하는 것이 대부분이다. Update 20은 MODIFY 문에서 술어를 사용하여 휴대폰 번호만 수정한다.
Update 20
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify do replace value of $new/customerinfo/phone[@type = "cell"]
with "444-444-4444"
return $new' )
where cid = 1001; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">444-444-4444</phone>
</customerinfo> |
|
존재하지 않는 노드 수정하기
이전 섹션에서, 오직 delete 연산만이 반복 엘리먼트 같은 노드 시퀀스에 적용될 수 있다고 배웠다. rename과 replace 같은 다른 연산들은 영향이 미칠 노드에 반복하기 위해 "for" 식을 필요로 한다. (Update 19) update 연산의 대상이 비어 있을 경우에도 마찬가지이다. 예를 들어, Update 21은 @type = "home"인 상황에서 phone 엘리먼트의 값을 대체하려고 한다. 하지만, 이 샘플 문서에는 집 전화 번호가 없고, $new/customerinfo/phone[@type = "home"] 식은 빈 결과만 만들어 낼 뿐이다. 따라서, Update 21은 실패한다. 존재 하지 않는 집 전화 번호를 제거하려고 시도하는 "delete" 연산은 성공하지만, 문서에는 어떤 영향도 미치지 않는다.
Update 21
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify do replace value of $new/customerinfo/phone[@type = "home"]
with "777-777-7777"
return $new')
where cid = 1001; |
|
SQL16085N The target node of an XQuery "replace value of" expression is not
valid. Error QName=err:XUTY0008. SQLSTATE=10703 |
|
사유 코드 "XUTY0008"은 "replace 식의 대상 노드가 단일 노드가 아니다."라고 말해주고 있다. Update 18의 경우, "단일 노드가 아님(not a single node)"이 의미하는 것은 한 개 이상의 노드가 있다는 의미이다. Update 21의 경우, "단일 노드가 아님(not a single node)"이 의미하는 것은 한 개 미만의 노드가 있다는 의미, 즉 아무 것도 없다는 의미이다. "for" 반복이 이 문제를 해결할 수 있다. Update 22에서, 성공하지만, 문서에는 어떤 결과도 미치지 않는다.
Update 22
update xmlcustomer
set info = xmlquery( '
copy $new := $INFO
modify for $j in $new/customerinfo/phone[@type = "home"] return
do replace value of $j with "777-777-7777"
return $new' )
where cid = 1001; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서 (이전과 동일) |
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
대안으로, XQuery if-then-else 식을 사용하여 집 전화 번호를 조건적으로 업데이트 하거나, 아직 존재하지 않을 경우 새 번호를 삽입할 수 있다. Update 23에 설명되어 있다.
Update 23
update xmlcustomer
set info = xmlquery('
copy $new := $INFO
modify if ($new/customerinfo/phone[@type="home"]) then
do replace value of $new/customerinfo/phone[@type="home"]
with "777-777-7777"
else do insert <phone type="home">777-777-7777</phone>
as last into $new/customerinfo
return $new' )
where cid = 1001; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
<phone type="home">777-777-7777</phone>
</customerinfo> |
|
같은 노드를 여러 번 수정하기
Update 9에서, 단일 update 문의 MODIFY 문에서 같은 문서에 여러 update 연산을 포함시킬 수 있다는 것을 알았다. 하지만, 같은 노드의 값을 한 번 이상 재명명, 대체, 수정할 수 없다. 일반적으로, 이는 말이 되지 않는다. 그 예가 Update 24에 나타나 있다. 이 업데이트는 DB2가 두 개의 "rename" 연산들이 같은 phone 엘리먼트를 위해 존재한다는 것을 탐지하면 런타임 시 실패한다.
Update 24
update xmlcustomer
set info = xmlquery('copy $new := $INFO
modify(
do rename $new/customerinfo/phone[@type="work"] as "telephone",
do rename $new/customerinfo/phone[. ="222-222-2222"] as "telephone" )
return $new')
where cid = 1001; |
|
Error Message:
SQL16083N Incompatible "rename" expressions exist in the modify clause of a
Transform expression. Error QName=err:XUDY0015. SQLSTATE=10704 |
|
update 연산의 술어들이 뚜렷한 노드들을 선택하도록 해야 한다. Update 25에서 보듯, $new/customerinfo/phone에 대해 두 개의 "replace value of" 식을 갖고 있다. 이들 중 하나는 직장 전화 번호에, 다른 하나는 집 전화 번호에 적용되며, SQL16083N 에러를 방지한다. "for" 반복 역시 Update 22와 비슷하게 사용되어, 직장 전화 번호나 집 전화 번호가 존재하지 않을 경우에도 이 문이 실패하지 않도록 한다. 이 패턴은 옵션 엘리먼트와 스키마 다양성을 다룰 때 유용하다.
Update 25
update xmlcustomer
set info = xmlquery('
copy $new := $INFO
modify (for $x in $new/customerinfo/phone[@type="work"]
return do replace value of $x with "444-444-4444" ,
for $y in $new/customerinfo/phone[@type="home"]
return do replace value of $y with "555-555-5555")
return $new')
where cid = 1001; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">222-222-2222</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
<customerinfo>
<name>David Patterson</name>
<addr country="Canada">
<street>Fifth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">444-444-4444</phone>
<phone type="cell">333-333-3333</phone>
</customerinfo> |
|
copy 문에서 노드 선택하기
지금까지, 이 글의 모든 업데이트 예제는 같은 "copy" 문인 copy $new := $INFO를 사용했다. 이것이 결코 변하지 않는데도 이 문법을 실행해야 하는지가 궁금할 것이다. copy 문의 유연성이 필요한 상황이 있다. 애플리케이션이 특정 고객에 대한 정보를 가져와야 하지만, 프라이버시를 지키기 위해서, 고객의 전화 번호를 배제시켜야 할 경우를 생각해 보자. Update 26과 Update 27이 바로 이 일을 수행한다. Update 26은 SQL 술어를 사용하여 문서를 선택하지만, Update 27은 XQuery 술어를 사용하고 SQL의 사용을 전적으로 배제한다. 이 쿼리들은 쿼리 프로세싱 동안 문서를 "임시로" 변형하고 테이블의 문서는 바꾸지 않는다.
Update 26
xquery
copy $new := db2-fn:sqlquery("select info from xmlcustomer where cid = 1000")
modify do delete $new/customerinfo/phone
return $new; |
| | 쿼리 결과: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
</customerinfo> |
|
Update 27
xquery
copy $new := db2-fn:xmlcolumn("XMLCUSTOMER.INFO")[/customerinfo/name="John Smith"]
modify do delete $new/customerinfo/phone
return $new; |
| | 쿼리 결과: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
</customerinfo> |
|
COPY 문의 오른쪽은 한 개의 문서 같이 정확히 한 아이템을 만들어 내야 한다. "John Smith"라는 이름을 가진 고객이 많거나 없다면, DB2는 다음과 같은 에러를 만들어 낸다:
SQL16084N An assigned value in the copy clause of a transform expression is not a
sequence with exactly one item that is a node. Error QName=err:XUTY0013. SQLSTATE=10705 |
고객들을 반복함으로써 이와 같은 에러를 피할 수 있다. (Update 28):
Update 28
xquery
for $i in db2-fn:xmlcolumn("XMLCUSTOMER.INFO")[/customerinfo/name="John Smith"]
return
copy $new := $i
modify do delete $new/customerinfo/phone
return $new; |
| | 쿼리 결과: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
</customerinfo> |
|
또 다른 예제인, Update 29는 COPY 문을 사용하여 고객의 주소만 가져오고, RETURN 문을 사용하여 수정된 주소를 새로운 "sendto" 엘리먼트에 삽입한다.
Update 29
xquery
for $i in db2-fn:xmlcolumn("XMLCUSTOMER.INFO")/customerinfo[name="John Smith"]
return
copy $new := $i/addr
modify do rename $new/zipcode as "postalcode"
return <sendto>{$new}</sendto>; |
| | 원래의 XML 문서: | 쿼리 결과: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<sendto>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<postalcode>M1T 2A9</postalcode>
</addr>
</sendto> |
|
SQL update 문에서 transform 식을 사용할 때, COPY 문의 필터링 술어 보다는 SQL WHERE 문의 술어를 갖게 된다.
다중 update 연산 결합하기
새로운 XQuery 업데이트 표준은 MODIFY 문의 모든 update 연산들이 서로 독립적으로 원래의 문서에 적용되도록 하고 있다. 서로의 결과는 볼 수 없다. 이것을 "snapshot semantics"라고 하는데, 각 update 연산이 논리적으로 원래 문서의 개별 스냅샷에 적용된다는 의미이다. 이러한 의미론은 Update 30에 묘사되어 있는데, 여기에는 두 개의 update 연산이 포함된다. 첫 번째 연산은 추가 "phone" 엘리먼트를 삽입한다. 두 번째 연산은 모든 "phone" 엘리먼트 이름을 "telephone"으로 바꾼다. 하지만, 재명명은 원래 문서의 phone 엘리먼트에만 적용되고, 같은 update 문에 추가된 새로운 phone 엘리먼트에는 적용되지 않는다. MODIFY 문의 update 연산 순서는 관련이 없다.
Update 30
update xmlcustomer
set info = xmlquery( 'copy $new := $INFO
modify ( do insert <phone type="cell">777-555-3333</phone>
after $new/customerinfo/addr ,
for $j in $new/customerinfo/phone
return do rename $j as "telephone" )
return $new' )
where cid = 1000; |
| | 업데이트 전의 XML 문서: | 업데이트 후의 XML 문서: |
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="work">963-289-4136</phone>
</customerinfo> |
|
<customerinfo>
<name>John Smith</name>
<addr country="Canada">
<street>Fourth</street>
<city>Calgary</city>
<state>Alberta</state>
<zipcode>M1T 2A9</zipcode>
</addr>
<phone type="cell">777-555-3333</phone>
<telephone type="work">963-289-4136</telephone>
</customerinfo> |
|
또 다른 예로, "addr" 엘리먼트를 탐지하고, 새로운 엘리먼트 "POBox"를 /customerinfo/addr에 삽입하는 update 문을 생각해 보자. 새로운 엘리먼트 "POBox"는 업데이트 된 문서에 나타나지 않는데, 부모 엘리먼트 "addr"이 삭제되었기 때문이다.
결과 XML 구조가 기본적인 XML 규칙들을 위반하면 업데이트는 거부된다. 예를 들어, XML 엘리먼트는 같은 이름을 가진 두 개의 애트리뷰트를 가질 수 없다. 애트리뷰트를 엘리먼트에 추가했을 때, 같은 이름을 가진 또 다른 애트리뷰트가 존재한다면, DB2는 업데이트를 거부한다.
요약
DB2 9.5는 XQuery Update Facility를 도입하여, XML 문서 내에서 개별 엘리먼트와 애트리뷰트의 값을 재명명, 삽입, 삭제, 대체, 수정할 수 있도록 하고 있다. 이는 XML 데이터의 업데이트를 쉽고 효율적으로 만든다. 이 업데이트 기능을 사용하여 XML 파싱 또는 문서를 애플리케이션을 읽어 들이지 않고도 문서를 수정할 수 있다. 예제를 통해 업데이트 기능을 소개했으니, 여러분이 직접 XML 업데이트를 작성해 보도록 하자.
감사의 말
이 글을 검토해 준 Don Chamberlin, Cindy Saracco, Henrik Loeser, Susanne Englert, Mel Kiyama에게 감사의 말을 전한다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| Script for queries in this article | sample.sql | 15KB | HTTP |
|---|
참고자료 교육
- "DB2 Universal Database의 네이티브 XML 지원": 본 기술자료에 관한 중요한 토픽.
- "SQL을 이용한 DB2 XML 데이터 쿼리 (한글)"
(developerWorks, 2006년 12월): SQL과 SQL/XML을 사용하여 XML 칼럼에 저장된 데이터를 쿼리하는 방법.
- "XQuery를 이용한 DB2 XML 데이터 쿼리 (한글)"
(developerWorks, 2007년 1월): XQuery를 사용하여 XML 칼럼에 저장된 데이터를 쿼리하는 방법.
- "네임스페이스로 XML 데이터 쿼리하기" (developerWorks, 2006년 11월)
- "DB2 9의 pureXML: XML 데이터를 쿼리하는 방법은?" (developerWorks, 2006년 6월): MDD 프로젝트에 AspectJ 기반 라이브러리를 만드는 방법.
-
"XMLTABLE 예제, Part 1: 관계형 포맷으로 XML 데이터 가져오기 (한글)"
(developerWorks, 2007년 10월): XMLTABLE을 사용하여 관계형 포맷에서 XML 데이터를 검색하는 방법을 배운다. 반복되거나 소실된 XML 엘리먼트를 관리하는 방법과, XMLTABLE 함수에서 네임스페이스를 핸들하는 방법을 배운다
- "DB2 9의 pureXML 성능을 위한 15 개의 베스트 프랙티스" (developerWorks, 2006년 10월): DB2 pureXML 성능에 관한 가이드라인.
- "DB2 8.x에서 DB2 Viper로 XML 애플리케이션 마이그레이션, Part 1: DB2 Viper의 XML 문서의 부분 업데이트" (developerworks, 2006년 5월)
-
DB2 pureXML Enablement Wiki
-
XQuery Update Facility
-
XQuery 튜토리얼
-
developerWorks Information Management 존 (영문)
-
developerWorks
기술 이벤트와 웹캐스트.
제품 및 기술 얻기
토론
필자소개  | 
|  | Nicola 박사는 IBM Silicon Valley Lab의 XML 데이터베이스 성능 분야 기술 리더이다. XQuery, SQL/XML을 포함하여 DB2에서의 XML 성능을 연구하고 있다. DB2 XML 개발 팀은 물론, XML을 사용하는 고객과 비즈니스 파트너들과 긴밀히 협력하고 있다. IBM에 입사하기 전에, Informix Software에서 데이터 웨어하우징 성능 분야에서 일했다. 분산 및 복제 데이터베이스 관련 프로젝트도 수행했다. 1999년, 독일 Technical University of Aachen에서 컴퓨터 공학 박사 학위를 받았다. |
 | 
|  | Uttam Jain은 DB2 pureXML 스토리지 분야의 소프트웨어 엔지니어이자 기술 리더이다. 2001년 IBM Silicon Valley Laboratory에 입사했으며, DB2 pureXML 팀에 합류하기 전 DB2에 객체 관계형 구조를 구현하는 작업을 했다. 2000년 University of Florida에서 전기 및 컴퓨터 공학 석사 학위를 받았다. 현재 관심 분야는 XQuery, SQL/XML, 분산 XML 데이터베이스이다. |
기사에 대한 평가
|  |