XMLからの選択を使用してテーブルに行を挿入しようとしています。私は近いと思います。どこがおかしいの?
declare @xmldata xml;
set @xmldata = '<Database>
<PurchaseDetails>
<PurchaseDetail>
<Upc>72594206916</Upc>
<Quantity>77</Quantity>
<PurchaseDate>9/2010</PurchaseDate>
<PurchaseCity>Dallas</PurchaseCity>
<PurchaseState>TX</PurchaseState>
</PurchaseDetail>
<PurchaseDetail>
<Upc>72594221854</Upc>
<Quantity>33</Quantity>
<PurchaseDate>12/2013</PurchaseDate>
<PurchaseCity>Nashville</PurchaseCity>
<PurchaseState>TN</PurchaseState>
</PurchaseDetail>
</PurchaseDetails>
</Database>'
insert into PurchaseDetails
(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
select
x.Rec.value('Upc','char(11)'),
x.Rec.value('Quantity','int'),
x.Rec.value('PurchaseDate','varchar(7)'),
x.Rec.value('PurchaseCity','varchar(50)'),
x.Rec.value('PurchaseState','char(2)')
from @xmlData.nodes('//Database/PurchaseDetails/PurchaseDetail') as x(Rec)
以前、同僚が同様の問題に取り組んでいた。これが私たちが思いついたものです。直感的ではありません!
insert into PurchaseDetails
(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
select
pd.value('Upc[1]','char(11)'),
pd.value('Quantity[1]','int'),
pd.value('PurchaseDate[1]','varchar(7)'),
pd.value('PurchaseCity[1]','varchar(50)'),
pd.value('PurchaseState[1]','char(2)')
from @xmlData.nodes('//Database/PurchaseDetails') as x(Rec)
cross apply @xmlData.nodes('//Database/PurchaseDetails/PurchaseDetail') as i(pd)
insert into PurchaseDetails(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
select T.X.value('(Upc/text())[1]', 'char(11)'),
T.X.value('(Quantity/text())[1]', 'int'),
T.X.value('(PurchaseDate/text())[1]', 'varchar(7)'),
T.X.value('(PurchaseCity/text())[1]', 'varchar(50)'),
T.X.value('(PurchaseState/text())[1]', 'char(2)')
from @xmlData.nodes('/Database/PurchaseDetails/PurchaseDetail') as T(X)
これを試して!
query()then value()
これをSQL Serverで実行すると、100%動作しました
最初にドット(。)を入力してから、子タグを入力します。
PurchaseDetailタグは2回存在するため、最初のタグと2番目のタグはドット(。)で置き換えられます。
ドットは、XQueryでの[1]の使用を妨げる可能性があります。
ドットは、最初と2番目のPurchaseDetailタグを表します。
INSERT INTO PurchaseDetails(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
SELECT col.query('./Upc').value('.', 'char(11)'),
col.query('./Quantity').value('.', 'int'),
col.query('./PurchaseDate').value('.', 'varchar(7)'),
col.query('./PurchaseCity').value('.', 'varchar(50)'),
col.query('./PurchaseState').value('.', 'char(2)')
FROM @xmlData.nodes('/Database/PurchaseDetails/PurchaseDetail') as ref(col)
これまでのクエリはより単純化されています。
動作するかどうかを確認する
select
x.Rec.query('./Upc').value('.','char(11)')
,x.Rec.query('./Quantity').value('.','int')
,x.Rec.query('./PurchaseDate').value('.','varchar(7)')
,x.Rec.query('./PurchaseCity').value('.','varchar(50)')
,x.Rec.query('./PurchaseState').value('.','char(2)')
from @xmlData.nodes('/Database/PurchaseDetails/PurchaseDetail') as x(Rec)
同様の問題に苦労し、XQueryで参照しているxmlにネストの追加レイヤーがある場合、@ birdusの答えが機能しないことがわかりましたあなたが持っている場合、わずかに異なるXML形状を仮定
_T.x.value('PurchasePlace/PurchaseCity[1]','varchar(50)')
_
それでもシングルトンエラーが発生します。 @birdusのソリューションはこの特定のケースで機能しますが、@ birdusのソリューションと@ Mikael-Erikssonのソリューションを組み合わせた、より一般的に適用可能なソリューションは次のとおりです。
_insert into PurchaseDetails(Upc, Quantity, PurchaseDate, PurchaseCity, PurchaseState)
select T.X.value('(Upc)[1]', 'char(11)'),
T.X.value('(Quantity)[1]', 'int'),
T.X.value('(PurchaseDate)[1]', 'varchar(7)'),
T.X.value('(PurchaseCity)[1]', 'varchar(50)'),
T.X.value('(PurchaseState)[1]', 'char(2)')
from @xmlData.nodes('/Database/PurchaseDetails/PurchaseDetail') as T(X)
_
このコンバインの/text()
の@birdusの省略は不必要ですが、要素セレクターの周りに@ Mikael-Erikssonの括弧を追加して、次のように変更した私の例のように複数の要素セレクターを許可します:
_T.x.value('(PurchasePlace/PurchaseCity)[1]','varchar(50)')
_
これについてのreasonは、@ birdusのバージョンがここで説明する例のいずれかでシングルトン以外のものを返すということではありません。しかし、それはmight。 Microsoft Docs :
コンパイラが実行時にシングルトンが保証されているかどうかを判断できない場合、シングルトンを必要とするロケーションステップ、関数パラメーター、および演算子はエラーを返します。
whyの問題に対処するには、位置述語の必要性(つまり、[1]
)XQuery文字列リテラル内で、@ pbzのように、シングルトンが必要であるため、保証する必要があります。 @pbzの回答に実質を追加するには、以下を参照してください。
Microsoftの SQL Docs ごと:
次の例では、XMLインスタンスはxml型の変数に保存されます。 value()メソッドは、XMLからProductID属性値を取得します。その後、値はint変数に割り当てられます。
DECLARE @myDoc xml DECLARE @ProdID int SET @myDoc = '<Root> <ProductDescription ProductID="1" ProductName="Road Bike"> <Features> <Warranty>1 year parts and labor</Warranty> <Maintenance>3 year parts and labor extended maintenance is available</Maintenance> </Features> </ProductDescription> </Root>' SET @ProdID = @myDoc.value('(/Root/ProductDescription/@ProductID)[1]', 'int' ) SELECT @ProdID
結果として値1が返されます。
XMLインスタンスにはProductID属性が1つしかありませんが、静的型付けルールでは、パス式がシングルトンを返すことを明示的に指定する必要があります 。したがって、追加の
[1]
はパス式の最後に指定されます。静的型付けの詳細については、 XQuery and Static Typing を参照してください。
そのリンクをたどると、次のようにつながります。
前に述べたように、型推論は頻繁に渡されたデータの型についてユーザーが知っているよりも広い型を推論します。これらの場合、ユーザーはクエリを書き換える必要があります。いくつかの典型的なケースは次のとおりです。
...
- タイプは、データに実際に含まれているものよりも高いカーディナリティーをもたらします。 xmlデータ型
複数の最上位要素を含む。XMLスキーマコレクションではこれを制限できません。静的型を減らすために
渡される値が実際に最大1つであることを保証します、あなたは
位置述語を使用する必要があります[1]
。たとえば、トップレベルaの下の要素bの属性cの値に1を追加するには
要素、(/a/b/@c)[1]+1
。さらに、ドキュメント
キーワードは、XMLスキーマコレクションと一緒に使用できます。