DB2 LUW 10.1 を使用。XQUERY 変換を使用してマージされた XML 列を持つ 2 つのテーブルで MERGE を機能させるのに問題があります。
次のようなテーブルがあります。
create table foo (
id int not null primary key,
data xml not null
)
このテーブルにデータを取得するには、(LOAD を使用して) 次のようなステージング テーブルにデータを読み込みます。
create table foo_incoming (
id int not null,
data xml not null
)
XML 列のデータは、XQUERY 変換を使用してマージされます。その背後にはいくつかのロジックがあるため、簡単ではありませんが、過度に複雑でもありません。手動の UPDATE を使用して変換をテストしたので、動作することがわかりました。
次に、次のように 2 つのテーブルをマージしようとします。
merge into foo f
using (select * from foo_incoming) i
on (f.id = i.id)
when matched then
update set data = xmlquery('
transform
copy $out := $old
modify ( ... )
return $out'
passing f.data as "old", i.data as "new")
when not matched then
insert (id, data) values (i.id, i.data)
これは、foo にデータがある場合に完全に機能します。XML 列は、希望どおりにマージされます。しかし、foo が空の場合、次のエラーが発生します。
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
マージが一致しなかったにもかかわらず、DB2 が XQUERY を評価しようとしているようです。したがって、f.data は NULL であり、変換のコピー式は空のシーケンスを取得します。「一致した場合」句全体を削除すると、ステートメントは機能します。
私は何を間違っていますか?それとも、これは DB2 の MERGE ステートメントの制限ですか?
単純な「一致した場合」を「一致した場合 (f.data is not null)」のようなものに変更することでこれを回避しようとしましたが、効果はありません。また、XQUERY 式を次のように変更してみました。
if($old)
then
transform
copy $out := $old
...
else ()
それも役に立ちませんでした。私が見つけた唯一の回避策は、MERGE を 2 つに分割することです。まず、foo と foo_incoming の両方にある行を更新するために、次のようにします。
merge into foo f
using
(
select q1.id, q1.data
from foo_incoming q1 inner join foo q2
on (q1.id = q2.id)
) i
on (f.id = i.id)
when matched then udpate ...;
次に、foo_incoming ではあるが foo ではない行を挿入するには、次のようにします。
merge into foo f
using
(
select q1.id, q1.data
from foo_incoming q1 left outer join foo q2
on (q1.id = q2.id)
where q2.id is null
) i
on (f.id = i.id)
when not matched then insert ...;
これは機能します。ただし、パフォーマンスはひどいものになると思います。そして、これは私が通常、私の思考に根本的な誤りがあるという警告サインとして解釈する一種のハッキングです.