SQL帮助读取XML列中的值

时间:2018-07-16 17:12:28

标签: sql xml

当前,我在表中有一列定义为文本,数据格式为XML。

示例数据为:

<Attributes><Default>0</Default><Table>0=Monday
1=Tuesday
2=Wednesday
3=Thursday
4=Friday
5=Saturday
-1=Sunday
</Table><Visibility>0</Visibility></Attributes>

此列需要连接到另一个表,该表的“工作日”列为“星期一”,值为0。我需要能够研究这个XML结构并在星期一返回。

表1结构:

CREATE TABLE [CNF_Right]
(
    [RightId] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [Attribute] [text] NULL
)

表1数据:

RightID          Attribute
-----------------------------------------------------
1                <Attributes><Default>0</Default> 
                  <Table>0=Monday
                         1=Tuesday
                         2=Wednesday
                         3=Thursday
                         4=Friday
                         5=Saturday
                         -1=Sunday
                   </Table>
                   <Visibility>0</Visibility>
                 </Attributes>

表2的结构:

CREATE TABLE [DATA_Right]
(
    [RightDataId] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [WeekDay] [int] NOT NULL
)

表2数据:

RightDataID             WeekDay
-------------------------------
1                       0
2                       -1
3                       5

当我加入这两个时,我想返回

Rightid    RightDataID    Weekday    WeekDayDescription
-------------------------------------------------------
1              1             0       Monday
1              2            -1       Sunday
1              3             5       Saturday

任何帮助将不胜感激。

TIA

1 个答案:

答案 0 :(得分:0)

正如我之前在评论中告诉您的那样,这种设计很糟糕,但这是您必须忍受的东西...

尝试一下:

USE master;
GO
CREATE DATABASE dbTest;
GO
Use dbTest;
GO
CREATE TABLE [CNF_Right]
(
    [RightId] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [Attribute] [text] NULL
);
CREATE TABLE [DATA_Right]
(
    [RightDataId] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [WeekDay] [int] NOT NULL
);
GO
INSERT INTO [CNF_Right](Attribute) VALUES
('<Attributes><Default>0</Default><Table>0=Monday
1=Tuesday
2=Wednesday
3=Thursday
4=Friday
5=Saturday
-1=Sunday
</Table><Visibility>0</Visibility></Attributes>');
INSERT INTO [DATA_Right]([WeekDay])
VALUES(0),(-1),(5);
GO

CREATE FUNCTION dbo.GetWeekdayList(@RightId INT)
RETURNS TABLE
AS
RETURN
WITH GetContent(TableContent) AS
(
    SELECT CAST(CAST(r.Attribute AS VARCHAR(MAX)) AS XML).value('(/Attributes/Table/text())[1]','nvarchar(max)')
    FROM [CNF_Right] r
    WHERE r.RightId=@RightId
)
,SplitContent(AsXml) AS
(
    SELECT CAST('<x><y>' + REPLACE(REPLACE(REPLACE(REPLACE(gc.TableContent,CHAR(13),CHAR(10)),CHAR(10)+CHAR(10),CHAR(10)),'=','</y><y>'),CHAR(10),'</y></x><x><y>') + '</y></x>' AS XML)
    FROM GetContent gc
)
SELECT x.value('y[1]','int') AS WeekdayIndex
      ,x.value('y[2]','nvarchar(max)') AS WeekdayName
FROM SplitContent sc
CROSS APPLY sc.AsXml.nodes('/x[y/text()]') A(x);
GO

SELECT *
FROM [DATA_Right] dr
INNER JOIN dbo.GetWeekdayList(1) AS wl ON wl.WeekdayIndex=dr.[WeekDay]

GO
USE master;
GO
--Careful with real data
--DROP DATABASE dbTest;
GO

一些解释

创建测试数据库后,我使用您的命令来创建两个表。

然后将XML和权限值插入这些表中。

魔术是在内联表值函数dbo.GetWeekdayList()中完成的。此函数会将TEXT强制转换为VARCHAR(MAX),并将其强制转换为XML。为了能够使用.value()方法在XML中检索值,需要这样做。

结果是值列表,它是一个带有换行符和=字符的大字符串。

第二步,使用XML技巧拆分此字符串,而最后的SELECT将返回工作日的名称和索引作为派生表。

最终查询将加入 iTVF ,就像它是一个表一样。

提示

这会很慢。
TEXT已过时,以后的版本将不再支持。
XML必须反复转换-操作非常昂贵。
必须使用大量昂贵的字符串操作,强制转换和XML操作一遍又一遍地创建派生表。
所有这些都是在可怜的小型数据库服务器上计算的。您的DBA将爱您...

我建议将这些数据写入经过适当设计的帮助程序表中,并将其加入。

从长远来看,这种设计应该完全改变...