نصيحة SQL خادم XML سؤال
-
22-08-2019 - |
سؤال
وأنا أكتب دالة معرفة من قبل المستخدم لاستخراج القيم من عمود XML في SQL Server الذي يمثل القاموس بسيط من سلسلة أزواج قيمة المفتاح. الطريقة الوحيدة لقد جعلت من العمل حتى الآن يبدو معقدا للغاية. هل لديك أي اقتراحات أو نصائح تبسيط لوظيفة 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]
المحلول
وأجد نهج كسكيري متغير ملزمة التالية أسهل للفهم:
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 ومحركات كسكيري ويتطلب سوى كسكيري واحد.
وأنا فقط أدركت أنك لم يحدد ما إذا كان 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 كسكيري تعداد والجمع بين قيم متعددة، إذا كانت موجودة. ملاحظة، على الرغم من أنه في هذه الحالة فإن وظيفة يعود فقط NULL عندما لم يتم العثور علىid، وليس عندما لم يكن هناك مطابقةkey عنصر زوج.
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
وحظا سعيدا!
نصائح أخرى
وأنا شخصيا لا أرى الكثير الذي يمكنك القيام به، التعليمات البرمجية التي قمت منظم بطريقة غير مقروء جدا، وكنت الاستعلام عن 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