使用 SQL OPENXML 查找特定子元素

Posted

技术标签:

【中文标题】使用 SQL OPENXML 查找特定子元素【英文标题】:Finding Specific Child Element Using SQL OPENXML 【发布时间】:2021-12-05 15:49:35 【问题描述】:

鉴于此 XML:

<ArrayOfCandidate>
    <Candidate>
        <Candidate_Serial>a4wwj48pypxg</Candidate_Serial>
        <Job_Serial>a2wwj48c92qp</Job_Serial>
        <Job_Name>Janitor</Job_Name>
        <First_Name>Phillip</First_Name>
        <Last_Name>Fry</Last_Name>
        <Email>phillip_fry@yahoo.com</Email>
        <Address_1>123 Main St</Address_1>
        <Questionnaires>
            <Questionnaire>
                <Questionnaire_Name>Questionnaire 1</Questionnaire_Name>
                <Questionnaire_Serial>a7wwj48rbcwx</Questionnaire_Serial>
                <Submit_Date>04/29/2020 10:55 AM</Submit_Date>
                <Submit_Date_Timestamp>1588175742</Submit_Date_Timestamp>
                <Questions>
                    <QuestionObject>
                        <Question>Salary Requirements</Question>
                        <Value>36.00 per hour</Value>
                    </QuestionObject>
                    <QuestionObject>
                        <Question>Are you eligible to work in the US?</Question>
                        <Value>Yes</Value>
                    </QuestionObject>
                </Questions>
            </Questionnaire>
            <Questionnaire>
                <Questionnaire_Name>New Employee Information Sheet</Questionnaire_Name>
                <Questionnaire_Serial>a7wwj488ril8</Questionnaire_Serial>
                <Submit_Date>05/18/2020 11:52 AM</Submit_Date>
                <Submit_Date_Timestamp>1589820723</Submit_Date_Timestamp>
                <Questions>
                    <QuestionObject>
                        <Question>Zip Code</Question>
                        <Value>86327</Value>
                    </QuestionObject>
                    <QuestionObject>
                        <Question>Phone Number</Question>
                        <Value>252-915-1623</Value>
                    </QuestionObject>
                    <QuestionObject>
                        <Question>Social Security Number</Question>
                        <Value>414-62-7741</Value>
                    </QuestionObject>
                </Questions>
            </Questionnaire>
        </Questionnaires>
    </Candidate>
</ArrayOfCandidate>

我已经能够使用&lt;Questionnaire&gt;&lt;QuestionObject&gt; 的索引来查找值,但我不能保证索引始终相同。

exec sp_xml_preparedocument @idoc OUTPUT, @XMLData  
            
select *
from    openxml(@idoc,'/ArrayOfCandidate/Candidate', 1)
with (
      Candidate_Serial      nvarchar(max)   'Candidate_Serial'
    , First_Name            nvarchar(max)   'First_Name'
    , Last_Name             nvarchar(max)   'Last_Name'
    , Email                 nvarchar(max)   'Email'
    , Address_1             nvarchar(max)   'Address_1'
    , SSN                   nvarchar(max)   'Questionnaires/Questionnaire[2]/Questions/QuestionObject[3]/Value'
) c;

是否可以用New Employee Information Sheet 的文本重写&lt;Questionnaire_Name&gt; 元素和&lt;Question&gt; 元素与Social Security Number 文本的查询匹配

【问题讨论】:

最好使用 XQuery 方法 .nodes() 和 .value() 而不是专有的 OPENXML。 @YitzhakKhabinsky 我尽量避免使用 XQuery,因为它对于需要处理大量元素的查询要慢得多,这里就是这种情况。 【参考方案1】:

请尝试以下解决方案。

无论其顺序位置如何,它都会找到 SSN。

从 SQL Server 2005 开始,最好使用基于 w3c 标准的 XQuery 语言,同时处理 XML 数据类型。

保留 Microsoft 专有的 OPENXML 及其伙伴 sp_xml_preparedocumentsp_xml_removedocument 只是为了与过时的 SQL 向后兼容 Server 2000。它们的使用减少到极少数的边缘情况。 强烈建议重新编写 SQL 并将其切换到 XQuery。

SQL

DECLARE @xml XML =
N'<ArrayOfCandidate>
    <Candidate>
        <Candidate_Serial>a4wwj48pypxg</Candidate_Serial>
        <Job_Serial>a2wwj48c92qp</Job_Serial>
        <Job_Name>Janitor</Job_Name>
        <First_Name>Phillip</First_Name>
        <Last_Name>Fry</Last_Name>
        <Email>phillip_fry@yahoo.com</Email>
        <Address_1>123 Main St</Address_1>
        <Questionnaires>
            <Questionnaire>
                <Questionnaire_Name>Questionnaire 1</Questionnaire_Name>
                <Questionnaire_Serial>a7wwj48rbcwx</Questionnaire_Serial>
                <Submit_Date>04/29/2020 10:55 AM</Submit_Date>
                <Submit_Date_Timestamp>1588175742</Submit_Date_Timestamp>
                <Questions>
                    <QuestionObject>
                        <Question>Salary Requirements</Question>
                        <Value>36.00 per hour</Value>
                    </QuestionObject>
                    <QuestionObject>
                        <Question>Are you eligible to work in the US?</Question>
                        <Value>Yes</Value>
                    </QuestionObject>
                </Questions>
            </Questionnaire>
            <Questionnaire>
                <Questionnaire_Name>New Employee Information Sheet</Questionnaire_Name>
                <Questionnaire_Serial>a7wwj488ril8</Questionnaire_Serial>
                <Submit_Date>05/18/2020 11:52 AM</Submit_Date>
                <Submit_Date_Timestamp>1589820723</Submit_Date_Timestamp>
                <Questions>
                    <QuestionObject>
                        <Question>Zip Code</Question>
                        <Value>86327</Value>
                    </QuestionObject>
                    <QuestionObject>
                        <Question>Phone Number</Question>
                        <Value>252-915-1623</Value>
                    </QuestionObject>
                    <QuestionObject>
                        <Question>Social Security Number</Question>
                        <Value>414-62-7741</Value>
                    </QuestionObject>
                </Questions>
            </Questionnaire>
        </Questionnaires>
    </Candidate>
</ArrayOfCandidate>';

SELECT c.value('(Candidate_Serial/text())[1]', 'varchar(50)') as Candidate_Serial
    , c.value('(First_Name/text())[1]', 'varchar(50)') as First_Name
    , c.value('(Last_Name/text())[1]', 'varchar(50)') as Last_Name
    , c.value('(Email/text())[1]', 'varchar(500)') as Email
    , c.value('(Address_1/text())[1]', 'varchar(500)') as Address_1
    , c.value('(Questionnaires/Questionnaire/Questions/QuestionObject[Question/text()="Social Security Number"]/Value/text())[1]', 'CHAR(11)') as SSN
FROM @xml.nodes('/ArrayOfCandidate/Candidate') AS t(c);

输出

+------------------+------------+-----------+-----------------------+-------------+-------------+
| Candidate_Serial | First_Name | Last_Name |         Email         |  Address_1  |     SSN     |
+------------------+------------+-----------+-----------------------+-------------+-------------+
| a4wwj48pypxg     | Phillip    | Fry       | phillip_fry@yahoo.com | 123 Main St | 414-62-7741 |
+------------------+------------+-----------+-----------------------+-------------+-------------+

【讨论】:

以上是关于使用 SQL OPENXML 查找特定子元素的主要内容,如果未能解决你的问题,请参考以下文章

仅在openxml中获取指定节点的子节点

如何使用 jQuery 从子元素中获取数据以在 xml api 中查找其他子元素?

查找父纯javascript的子元素

使用 jQuery 查找 HTML 表单元素的子类型

Selenium XPath 合并跨越文本并查找包含子字符串的元素

如何按值命名父元素的子元素? SQL 服务器