SQL ServerのXMLクエリのアドバイス
-
22-08-2019 - |
質問
私は、文字列のキーと値のペアの簡単な辞書を表し、SQL ServerのXML列から値を抽出するためのユーザー定義関数を書いています。私はそれがうまく行った唯一の方法は、これまで過度に複雑なようです。あなたは以下のDictValue
機能のための任意の簡素化提案やヒントを持っていますか?
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[DictValue]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[DictValue]
go
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TableWithXmlColumn]') AND type in (N'U'))
DROP TABLE [dbo].[TableWithXmlColumn]
go
create table TableWithXmlColumn (
Id int identity primary key
,Dict xml
)
go
create function DictValue(
@id int
,@key nvarchar(max)
) returns nvarchar(max) as begin
declare @d xml -- string Dictionary
select @d = Dict from TableWithXmlColumn where Id = @id
declare @value xml
select
@value = d.Pair.value('data(.)', 'nvarchar(max)')
from
@d.nodes('/StringDictionary/Pair') as d(Pair)
where
@key = d.Pair.value('./@Key', 'nvarchar(max)')
return convert(nvarchar(max), @value)
end
go
declare @xmlId int
insert TableWithXmlColumn (Dict) values (
N'<?xml version="1.0" encoding="utf-16"?>
<StringDictionary>
<Pair Key="color">red</Pair>
<Pair Key="count">123</Pair>
</StringDictionary>')
set @xmlId = scope_identity()
select
dbo.DictValue(@xmlId, 'color') as color
,dbo.DictValue(@xmlId, 'count') as [count]
解決
私が理解しやすい次の変数結合のXQueryアプローチを見つけます
create function DictValue(
@id int,
@key nvarchar(max)
)
returns nvarchar(max) as
begin
declare @value nvarchar(max)
select
@value = Dict.value(
'(StringDictionary/Pair[@Key=sql:variable("@key")])[1]',
'nvarchar(max)'
)
from TableWithXmlColumn
where Id = @id
return @value
end
私はそれを検証しませんでしたが、それはT-SQLとXQueryエンジンでコンテキストスイッチを回避し、一つだけXQueryを必要とするため、これは、あまりにも、少し良く実行することがあります。
私はちょうどあなたが1つのIDのためのdict XMLは、同じキーを持つ複数のペア要素が含まれている可能性があるかどうかを指定していないことに気づきます:
insert TableWithXmlColumn (Dict) values (
N'<?xml version="1.0" encoding="utf-16"?>
<StringDictionary>
<Pair Key="color">red</Pair>
<Pair Key="color">blue</Pair>
<Pair Key="count">123</Pair>
</StringDictionary>')
その場合は、、彼らが存在する場合、複数の値を列挙して結合するFLWOR XQueryを使用して、このわずかに変更された機能を、考えます。 @idが見つからない場合、この場合には機能だけでペア要素@key一致がないではない場合は、NULLを返すこと、しかし、注意します。
create function DictValue(
@id int,
@key nvarchar(max)
)
returns nvarchar(max) as
begin
declare @value nvarchar(max)
select
@value = Cast(
Dict.query('
for $i in StringDictionary/Pair[@Key=sql:variable("@key")]
return string($i)
') as nvarchar(max))
from TableWithXmlColumn
where Id = @id
return @value
end
グッドラック!
他のヒント
個人的に私はあなたが、結果が第1セットを取得するためにあなたは非常に読みやすい方法で構成されている、とあなたはXMLを照会しているコードは、その後、値をつかん行うことができます多くは表示されません。
あなたは@d変数の使用を回避することができるかもしれません、しかし、私は、コードの可読性が大幅に苦しむことになると信じています。
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[XMLTable](@x XML)
RETURNS TABLE
AS RETURN
WITH cte AS (
SELECT
1 AS lvl,
x.value('local-name(.)','NVARCHAR(MAX)') AS Name,
CAST(NULL AS NVARCHAR(MAX)) AS ParentName,
CAST(1 AS INT) AS ParentPosition,
CAST(N'Element' AS NVARCHAR(20)) AS NodeType,
x.value('local-name(.)','NVARCHAR(MAX)') AS FullPath,
x.value('local-name(.)','NVARCHAR(MAX)')
+ N'['
+ CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS NVARCHAR)
+ N']' AS XPath,
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS Position,
x.value('local-name(.)','NVARCHAR(MAX)') AS Tree,
x.value('text()[1]','NVARCHAR(MAX)') AS Value,
x.query('.') AS this,
x.query('*') AS t,
CAST(CAST(1 AS VARBINARY(4)) AS VARBINARY(MAX)) AS Sort,
CAST(1 AS INT) AS ID
FROM @x.nodes('/*') a(x)
UNION ALL
SELECT
p.lvl + 1 AS lvl,
c.value('local-name(.)','NVARCHAR(MAX)') AS Name,
CAST(p.Name AS NVARCHAR(MAX)) AS ParentName,
CAST(p.Position AS INT) AS ParentPosition,
CAST(N'Element' AS NVARCHAR(20)) AS NodeType,
CAST(p.FullPath + N'/' + c.value('local-name(.)','NVARCHAR(MAX)') AS NVARCHAR(MAX)) AS FullPath,
CAST(p.XPath + N'/'+ c.value('local-name(.)','NVARCHAR(MAX)')+ N'['+ CAST(ROW_NUMBER() OVER(PARTITION BY c.value('local-name(.)','NVARCHAR(MAX)')
ORDER BY (SELECT 1)) AS NVARCHAR)+ N']' AS NVARCHAR(MAX)) AS XPath,
ROW_NUMBER() OVER(PARTITION BY c.value('local-name(.)','NVARCHAR(MAX)')
ORDER BY (SELECT 1)) AS Position,
CAST( SPACE(2 * p.lvl - 1) + N'|' + REPLICATE(N'-', 1) + c.value('local-name(.)','NVARCHAR(MAX)') AS NVARCHAR(MAX)) AS Tree,
CAST( c.value('text()[1]','NVARCHAR(MAX)') AS NVARCHAR(MAX) ) AS Value, c.query('.') AS this,
c.query('*') AS t,
CAST(p.Sort + CAST( (lvl + 1) * 1024 + (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) * 2) AS VARBINARY(4)) AS VARBINARY(MAX) ) AS Sort,
CAST((lvl + 1) * 1024 + (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) * 2) AS INT)
FROM cte p
CROSS APPLY p.t.nodes('*') b(c)), cte2 AS (
SELECT
lvl AS Depth,
Name AS NodeName,
ParentName,
ParentPosition,
NodeType,
FullPath,
XPath,
Position,
Tree AS TreeView,
Value,
this AS XMLData,
Sort, ID
FROM cte
UNION ALL
SELECT
p.lvl,
x.value('local-name(.)','NVARCHAR(MAX)'),
p.Name,
p.Position,
CAST(N'Attribute' AS NVARCHAR(20)),
p.FullPath + N'/@' + x.value('local-name(.)','NVARCHAR(MAX)'),
p.XPath + N'/@' + x.value('local-name(.)','NVARCHAR(MAX)'),
1,
SPACE(2 * p.lvl - 1) + N'|' + REPLICATE('-', 1)
+ N'@' + x.value('local-name(.)','NVARCHAR(MAX)'),
x.value('.','NVARCHAR(MAX)'),
NULL,
p.Sort,
p.ID + 1
FROM cte p
CROSS APPLY this.nodes('/*/@*') a(x)
)
SELECT
ROW_NUMBER() OVER(ORDER BY Sort, ID) AS ID,
ParentName, ParentPosition,Depth, NodeName, Position,
NodeType, FullPath, XPath, TreeView, Value, XMLData
FROM cte2
所属していません StackOverflow