2009-04-02 16 views
1

Ich schreibe eine benutzerdefinierte Funktion zum Extrahieren von Werten aus einer XML-Spalte in SQL Server, die ein einfaches Wörterbuch der Zeichenfolge Schlüssel-Wert-Paare darstellt. Der einzige Weg, wie ich es bisher geschafft habe, scheint übermäßig komplex zu sein. Haben Sie vereinfachte Vorschläge oder Tipps für die Funktion DictValue unten?SQL Server-XML-Abfrage-Tipp

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] 
+0

Die einzige Frage wäre: Warum speichern Sie dies in XML und nicht in einer relationalen Tabelle? Das Schlüssel/Wert-Paar scheint relativ strukturiert und ideal für relationale Speicherung geeignet ... –

Antwort

1

Ich finde den folgenden Variable-bound XQuery Ansatz leichter zu verstehen:

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 

ich es nicht überprüfen hätte, aber das ist vielleicht ein bisschen besser führen, wie es Kontext auf dem T- Schalt vermeidet SQL- und XQuery-Engines und erfordert nur ein XQuery.

Ich habe erkannt, dass Sie nicht angegeben, ob die Dict XML für eine Id mehrere Paar Elemente mit dem gleichen Schlüssel enthalten könnte:

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>') 

Wenn das der Fall ist, dann diese leicht modifizierte Funktion betrachten, die verwendet eine FLWOR XQuery, um mehrere Werte aufzulisten und zu kombinieren, falls sie existieren. Beachten Sie jedoch, dass die Funktion in diesem Fall nur dann NULL zurückgibt, wenn eine @id nicht gefunden wird, und nicht, wenn kein passendes @key-Paarelement vorhanden ist.

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 

Viel Glück!

0

Persönlich Ich habe nicht viel sehen, dass Sie tun können, ist der Code, den Sie haben in einer Art und Weise strukturiert, die sehr gut lesbar ist, und Sie die Abfrage der XML das Ergebnis gesetzt zuerst zu erhalten, dann die Heimelf Werte.

Sie können vielleicht die Verwendung der @ d-Variable umgehen, aber ich glaube, die Lesbarkeit des Codes wird stark leiden.

0
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