如何确保 SQL 能够读取所有 XML 标记数据

Posted

技术标签:

【中文标题】如何确保 SQL 能够读取所有 XML 标记数据【英文标题】:How to ensure the SQL is able to read all XML tag data 【发布时间】:2014-12-13 02:51:49 【问题描述】:

我在 SQL 表列中有以下 XML 数据:

<root>
    <Physicians>
        <name></name>
        <picture></picture>
        <gender></gender>
        <langAccept>English</langAccept>
        <langAccept>Spanish</langAccept> (can appear more times)
        <insAccept>Aetna</insAccept>
        <insAccept>BCBS</insAccept> (can appear more times)
        <specialty></specialty>
        <specialty2></specialty2>
        <specialty3></specialty3>
    </Physicians>
</root>

langAcceptinsAccept可以出现多次,不知道出现了多少次。

我有以下 SQL 查询,目前没有考虑到“langAccept”和“insAccept”标签:

DECLARE @strProvider varchar(200)
SET @strProvider = '' --The Provider DropDownList

DECLARE @strSpecialty varchar(200)
SET @strSpecialty = '' --The Specialty DropDownList

DECLARE @strLocation varchar(200)
SET @strLocation = '' --The Location DropDownList

DECLARE @strGender varchar(200)
SET @strGender = '' --The Gender DropDownList

DECLARE @strInsurance varchar(200)
SET @strInsurance = '' --The Insurance DropDownList

DECLARE @strLanguage varchar(200)
SET @strLanguage = '' --The Language DropDownList


SELECT
    [content_title] AS [Physician Name]
    , [content_status] AS [Status]
    , CAST([content_html] AS XML).value('(root/Physicians/picture/img/@src)[1]','varchar(255)') AS [Image]
    , dbo.usp_ClearHTMLTags(CONVERT(nvarchar(600), CAST([content_html] AS XML).query('root/Physicians/gender'))) AS [Gender]
    , CAST([content_html] AS XML).query('/root/Physicians/OfficeLocations/office1/a') AS [Office1]
    , CAST([content_html] AS XML).query('/root/Physicians/OfficeLocations/office2/a') AS [Office2]
    , CAST([content_html] AS XML).query('/root/Physicians/OfficeLocations/office3/a') AS [Office3]
    , CAST([content_html] AS XML).query('/root/Physicians/OfficeLocations/office4/a') AS [Office4]
    , CAST ([content_html] AS XML).query('/root/Physicians/specialty/a') AS [Specialty1]
    , CAST ([content_html] AS XML).query('/root/Physicians/specialty2/a') AS [Specialty2]
FROM
    [MYDB].[dbo].[content]
WHERE
    [folder_id] = '188'
    AND
    (content_html LIKE '%<gender>%'+ @strGender+'%</gender>%')
    AND
    (content_html LIKE '%'+@strSpecialty+'%')
    AND
    (content_html LIKE '%'+@strLocation+'%')
    AND
    (content_status = 'A')
ORDER BY
    [content_title]

我将使用 C# 作为代码隐藏获取该数据并写入我的 ASP.net 页面中的转发器。

如何修改我的 SQL 查询,以便它获取每个 langAcceptinsAccept 标记的值(出现多次)。

【问题讨论】:

这应该复制行或放入一行(如English, Spanish)? 如果我什至可以将它们放在 CSV 的列中,我可以在代码隐藏中操作它们。我认为添加一列并通过 CSV 插入每个事件对我来说是更好的途径。 使用逗号分隔符的列值不是一个好主意。这是一个糟糕的数据库设计的例子。相反,您应该寻求一个适当规范化的数据库,从中获取数据会容易得多。 因为这是我第一次使用它,我愿意提供建议和帮助:)。几乎这个查询将返回结果,我将在前端显示它作为结果。有时这些标签会出现 10+ 次,那么在一个表中创建 10+ 行有意义吗? 【参考方案1】:

可以处理可能重复的任意数量的节点 - 但请注意,这总是会为单个条目 &lt;Physician&gt; 创建大量行。

试试这个:

DECLARE @Content TABLE (ID INT NOT NULL, XmlDAta XML)
INSERT INTO @content VALUES(1, '<root>
    <Physicians>
        <name>Dr. Excellent</name>
        <picture></picture>
        <gender>Male</gender>
        <langAccept>English</langAccept>
        <langAccept>Spanish</langAccept> 
        <insAccept>Aetna</insAccept>
        <insAccept>BCBS</insAccept> 
        <specialty></specialty>
        <specialty2></specialty2>
        <specialty3></specialty3>
    </Physicians>
</root>')

SELECT
    ID,
    PhysicianName = XC.value('(name)[1]', 'varchar(50)'),
    Gender = XC.value('(gender)[1]', 'varchar(50)'),
    LangSpoken = XLang.value('.', 'varchar(20)'),
    InsAccepted = XIns.value('.', 'varchar(50)')
FROM
    @Content
CROSS APPLY
    XmlData.nodes('/root/Physicians') AS XT(XC)
CROSS APPLY
    XC.nodes('langAccept') AS XT2(XLang)
CROSS APPLY
    XC.nodes('insAccept') AS XT3(XIns)

通过在langAcceptinsAccept 节点内的insAccept 上使用.nodes(),您可以获得所有定义的值 - 但您最终会得到单个&lt;Physican&gt; 节点的多个关系行:

更新:要从您自己的现有表中获取数据,请使用:

SELECT
    ID,
    PhysicianName = XC.value('(name)[1]', 'varchar(50)'),
    Gender = XC.value('(gender)[1]', 'varchar(50)'),
    LangSpoken = XLang.value('.', 'varchar(20)'),
    InsAccepted = XIns.value('.', 'varchar(50)')
FROM
    [MyDB].[dbo].Content
CROSS APPLY
    CAST(content_html AS XML).nodes('/root/Physicians') AS XT(XC)
CROSS APPLY
    XC.nodes('langAccept') AS XT2(XLang)
CROSS APPLY
    XC.nodes('insAccept') AS XT3(XIns)

【讨论】:

不幸的是,XML 是从 SQL 表中读取的,我不确定 TAG 会出现多少次。我更新了我的问题,XML 已经在 SQL 表列中,而不是在文件中,抱歉。 @SearchForKnowledge:我的帖子在 SQL Server 表上工作......只需将我的 @Content 替换为 [MYDB].[dbo].[content] 就可以了 DECLARE @Content [dbo].[content]? @SearchForKnowledge: 否 - 用您自己的数据库名称替换 SELECT 语句中的 @Content .... 更新了我的回复 首先:您需要使用 your 列名(而不是 XmlData)。其次:此列必须为 type XML 才能正常工作!【参考方案2】:

你可以这样试试。 这不是您问题的确切答案,但这可能会帮助您解决问题。

        DECLARE @Data XMl = '<root><Physicians><name>sajsj</name><picture/><gender/><langAccept>English</langAccept><langAccept>Spanish</langAccept> (can appear more times)<insAccept>Aetna</insAccept><insAccept>BCBS</insAccept> (can appear more times)<specialty/><specialty2/><specialty3/></Physicians></root>';

        ;WITH CTE AS (

        SELECT Dt.value('(name/text())[1]','VARCHAR(100)') AS Name,
               Dt.query('(langAccept)') AS LangAccept,
               Dt.query('(insAccept)') AS InsAccept
        FROM 
        @Data.nodes('/root/Physicians') AS MyData(Dt)
        ),
        CteGetAllLangAccept AS 
        (

         SELECT 
         Ct.Name,
         Data.Lang.value('(.)[1]', 'VARCHAR(50)') AS [LangAcceptValue],
         NULL AS [InsAcceptDataValue]
         FROM  CTE Ct
         CROSS APPLY Ct.LangAccept.nodes('/langAccept') AS Data(Lang)

         ),
         CteGetInsAcceptData AS (
          SELECT 
         Ct.Name,
         NULL AS [LangAcceptValue],
         InsAcceptData.Ins.value('(.)[1]', 'VARCHAR(50)') AS [InsAcceptDataValue]
         FROM  CTE Ct
         CROSS APPLY Ct.InsAccept.nodes('/insAccept') AS InsAcceptData(Ins)
         )

         SELECT * FROM CteGetAllLangAccept![enter image description here][1]
         UNION 
         SELECT * FROM CteGetInsAcceptData;

【讨论】:

感谢您的回复。 XML 数据已经在 SQL 表列中,我只是从列中读取数据并删除数据。我毫不怀疑它有效,但我的不是文件而是表格列。如何更改 @Data 以指向表格列而不是硬编码字符串?我应该从列中获取值并将其存储在一个字符串中,然后将该字符串用于@Data 并使用上述查询吗?【参考方案3】:

我认为如果你想在客户端显示它,做几个查询会更容易,一个用于医生表,一个用于langAccept,一个用于insAccept

declare @temp table (data xml)

insert into @temp (data)
select '<root>
    <Physicians>
        <name>House M.D.</name>
        <picture></picture>
        <gender>Male</gender>
        <langAccept>English</langAccept>
        <langAccept>Spanish</langAccept> 
        <insAccept>Aetna</insAccept>
        <insAccept>BCBS</insAccept> 
        <specialty></specialty>
        <specialty2></specialty2>
        <specialty3></specialty3>
    </Physicians>
    <Physicians>
        <name>Paracelsus</name>
        <picture></picture>
        <gender>Male</gender>
        <langAccept>German</langAccept>
        <langAccept>Latin</langAccept> 
        <specialty></specialty>
        <specialty2></specialty2>
        <specialty3></specialty3>
    </Physicians>    
</root>'

select
    t.c.value('name[1]', 'nvarchar(max)') as name,
    t.c.value('gender[1]', 'nvarchar(max)') as gender
from @temp as a
    cross apply a.data.nodes('root/Physicians') as t(c)
    
select
    t.c.value('name[1]', 'nvarchar(max)') as name,
    l.c.value('.', 'nvarchar(max)') as langAccept
from @temp as a
    cross apply a.data.nodes('root/Physicians') as t(c)
    cross apply t.c.nodes('langAccept') as l(c)
    
select
    t.c.value('name[1]', 'nvarchar(max)') as name,
    l.c.value('.', 'nvarchar(max)') as insAccept
from @temp as a
    cross apply a.data.nodes('root/Physicians') as t(c)
    cross apply t.c.nodes('insAccept') as l(c)

【讨论】:

谢谢。 XML 数据是表中的一列,因此我必须从表中提取列然后提取

以上是关于如何确保 SQL 能够读取所有 XML 标记数据的主要内容,如果未能解决你的问题,请参考以下文章

XML入门知识

如何处理sql中xml标记之间的前导空格

C#读取并写入XML文件

如何为 sklearn 的 CountVectorizer 编写自定义标记器以将所有 XML 标记以及打开和关闭标记之间的所有文本视为标记

如何为 sql 中的 NULL/空值列生成带有打开和关闭标记的 XML?

R语言XML文件