Sql 使用节点()和值()获取父值
我有一个与此XSD匹配的XML文档片段:Sql 使用节点()和值()获取父值,sql,sql-server,xml,sql-server-2008,xquery,Sql,Sql Server,Xml,Sql Server 2008,Xquery,我有一个与此XSD匹配的XML文档片段: <xs:complexType name="QuestionType"> <xs:sequence> <xs:element name="questionId" type="xs:string" minOccurs="1" /> <xs:element name="questionDescription" type="xs:string" minOccurs="1" /> <
<xs:complexType name="QuestionType">
<xs:sequence>
<xs:element name="questionId" type="xs:string" minOccurs="1" />
<xs:element name="questionDescription" type="xs:string" minOccurs="1" />
<xs:element name="questionHeader" type="xs:string" minOccurs="0" />
<xs:element name="questionLabel" type="xs:string" minOccurs="0" />
<xs:element name="version" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="SubQuestion" type="QuestionType"
minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
我希望可以使用递归CTE来解决这个问题,但我很难将其组合在一起。您可以使用CTE:
WITH TopLevel (ID, ParentID, Description, Header, Label,Level)
AS
(
SELECT -- Top-level questions...
C.value('questionId[1]', 'NVarChar(1000)') Id,
NULL ParentId,
C.value('questionDescription[1]', 'NVarChar(1000)') Description,
NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
C.value('version[1]', 'NVarChar(1000)') Version,
0 AS Level
FROM @X.nodes('//Question') X(C)
UNION ALL
SELECT -- Sub-questions...
C.value('questionId[1]', 'NVarChar(1000)') Id,
C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)') ParentId,
C.value('questionDescription[1]', 'NVarChar(1000)') Description,
NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
C.value('version[1]', 'NVarChar(1000)') Version
,Level + 1 AS Level
FROM @X.nodes('//SubQuestion') X(C)
JOIN TopLevel AS t ON C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)') = t.id )
SELECT * FROM TopLevel
参考资料:到目前为止,我正在这样做,尽管我仍然希望将查询压缩一点:
WITH Q AS (
SELECT
C.value('questionId[1]', 'NVarChar(1000)') Id,
NULL ParentId,
C.value('questionDescription[1]', 'NVarChar(1000)') Description,
NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
C.value('version[1]', 'NVarChar(1000)') Version
FROM @X.nodes('//Question') X(C)
UNION ALL
SELECT
C.value('questionId[1]', 'NVarChar(1000)') Id,
COALESCE(
C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)'),
C.query('..').value('(SubQuestion/questionId)[1]', 'NVarChar(1000)')
) ParentId,
C.value('questionDescription[1]', 'NVarChar(1000)') Description,
NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
C.value('version[1]', 'NVarChar(1000)') Version
FROM @X.nodes('//SubQuestion') X(C)
)
SELECT Q.Id, Q.ParentId, Q.Description, Q.Header, Q.Label, Q.Version
FROM Q;
这是一个重要的位,因为它将获得第一个非null的值:
COALESCE(
C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)'),
C.query('..').value('(SubQuestion/questionId)[1]', 'NVarChar(1000)')
) ParentId
考虑到您已经用sql-server-2008标记了这个问题,并且IMHO sql server 2008支持XQuery,我想提出一个不同的“角度”:使用XPath表达式选择您感兴趣的节点
.//*[local-name(.) = 'Question' or local-name(.) = 'SubQuestion']
请注意,我正在使用XPath函数local-name(),以防实际XML数据具有名称空间声明
我创建了一个示例XML文件来测试上述表达式:
<?xml version="1.0" encoding="UTF-8"?>
<Questions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.acme.com" xsi:schemaLocation="sample.xsd">
<Question>
<questionId>1</questionId>
<questionDescription>Question 1</questionDescription>
<version>1</version>
<SubQuestion>
<questionId>1.1</questionId>
<questionDescription>Question 1.1</questionDescription>
<version>1</version>
<SubQuestion>
<questionId>1.1.1</questionId>
<questionDescription>Question 1.1.1</questionDescription>
<version>1</version>
<SubQuestion>
<questionId>1.1.1.1</questionId>
<questionDescription>Question 1.1.1.1</questionDescription>
<version>1</version>
</SubQuestion>
<SubQuestion>
<questionId>1.1.1.2</questionId>
<questionDescription>Question 1.1.1.2</questionDescription>
<version>1</version>
</SubQuestion>
</SubQuestion>
<SubQuestion>
<questionId>1.2</questionId>
<questionDescription>Question 1.2</questionDescription>
</SubQuestion>
</SubQuestion>
</Question>
<Question>
<questionId>2</questionId>
<questionDescription>Question 2</questionDescription>
<version>1</version>
</Question>
<Question>
<questionId>3</questionId>
<questionDescription>Question 3</questionDescription>
<version>1</version>
<SubQuestion>
<questionId>3.1</questionId>
<questionDescription>Question 3.1</questionDescription>
<version>1</version>
</SubQuestion>
</Question>
</Questions>
边栏:如果您的文档非常大,那么XQuery非常慢,您最好使用它OpenXml@kd7:好的,谢谢你指出这一点。我知道XQuery的速度有多慢,但对于我的文档来说应该足够了。@Yuck:我有两个问题:1)您是否可以发布至少一个XML片段(或示例),而不仅仅是XSD?2) 您是在寻找SQL查询还是XQuery查询适合您?文档结构的问题是,
的父级可能有
或
。本CTE仅考虑了前者。我在递归查询“TopLevel”的“ParentID”列中得到了锚和递归部分之间的错误类型不匹配。
检查答案,我只是添加了一个级别字段来显示问题/子问题的深度。您始终可以在CTE之后的SELECT中将TopLevel连接回自身,以获得附加的子问题关系。+1使用单个查询的解决方案的关键是在回答开始时使用XQuery选择器。我正在编辑我的最终查询,目的是让它在某个地方。
<?xml version="1.0" encoding="UTF-8"?>
<Questions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.acme.com" xsi:schemaLocation="sample.xsd">
<Question>
<questionId>1</questionId>
<questionDescription>Question 1</questionDescription>
<version>1</version>
<SubQuestion>
<questionId>1.1</questionId>
<questionDescription>Question 1.1</questionDescription>
<version>1</version>
<SubQuestion>
<questionId>1.1.1</questionId>
<questionDescription>Question 1.1.1</questionDescription>
<version>1</version>
<SubQuestion>
<questionId>1.1.1.1</questionId>
<questionDescription>Question 1.1.1.1</questionDescription>
<version>1</version>
</SubQuestion>
<SubQuestion>
<questionId>1.1.1.2</questionId>
<questionDescription>Question 1.1.1.2</questionDescription>
<version>1</version>
</SubQuestion>
</SubQuestion>
<SubQuestion>
<questionId>1.2</questionId>
<questionDescription>Question 1.2</questionDescription>
</SubQuestion>
</SubQuestion>
</Question>
<Question>
<questionId>2</questionId>
<questionDescription>Question 2</questionDescription>
<version>1</version>
</Question>
<Question>
<questionId>3</questionId>
<questionDescription>Question 3</questionDescription>
<version>1</version>
<SubQuestion>
<questionId>3.1</questionId>
<questionDescription>Question 3.1</questionDescription>
<version>1</version>
</SubQuestion>
</Question>
</Questions>
declare namespace acme = "http://www.acme.com";
<AllQuestions>
{
for $question in .//*[local-name(.) = 'Question' or local-name(.) = 'SubQuestion']
return
<Question>
<questionId>{ data($question/acme:questionId) }</questionId>
<questionDescription>{ data($question/acme:questionDescription) }</questionDescription>
</Question>
}
</AllQuestions>
<?xml version="1.0" encoding="UTF-8"?>
<AllQuestions>
<Question>
<questionId>1</questionId>
<questionDescription>Question 1</questionDescription>
</Question>
<Question>
<questionId>1.1</questionId>
<questionDescription>Question 1.1</questionDescription>
</Question>
<Question>
<questionId>1.1.1</questionId>
<questionDescription>Question 1.1.1</questionDescription>
</Question>
<Question>
<questionId>1.1.1.1</questionId>
<questionDescription>Question 1.1.1.1</questionDescription>
</Question>
<Question>
<questionId>1.1.1.2</questionId>
<questionDescription>Question 1.1.1.2</questionDescription>
</Question>
<Question>
<questionId>1.2</questionId>
<questionDescription>Question 1.2</questionDescription>
</Question>
<Question>
<questionId>2</questionId>
<questionDescription>Question 2</questionDescription>
</Question>
<Question>
<questionId>3</questionId>
<questionDescription>Question 3</questionDescription>
</Question>
<Question>
<questionId>3.1</questionId>
<questionDescription>Question 3.1</questionDescription>
</Question>
</AllQuestions>
SELECT
C.value('questionId[1]', 'NVarChar(1000)') Id,
COALESCE(
C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)'),
C.query('..').value('(SubQuestion/questionId)[1]', 'NVarChar(1000)')
) ParentId,
C.value('questionDescription[1]', 'NVarChar(1000)') Description,
NULLIF(C.value('questionHeader[1]', 'NVarChar(1000)'), '') Header,
NULLIF(C.value('questionLabel[1]', 'NVarChar(1000)'), '') Label,
C.value('version[1]', 'NVarChar(1000)') Version
FROM
@X.nodes('.//*[local-name(.)="Question" or local-name(.)="SubQuestion"]') X(C);