我有一个与此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: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>
这递归地定义了一个<Question>
个元素,这些元素可以包含<SubQuestion>
个元素,但都属于QuestionType
类型。
使用SQL,我想查询一次文档以获得包含所有问题和子问题的单个结果集。我目前有两个独立的查询来实现这一点(请注意我只使用NVarChar(1000)
进行测试 - 它们在生产中的尺寸会更合适,@X
是一个与上面的模式匹配的XML变量):
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
FROM @X.nodes('//Question') X(C);
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
FROM @X.nodes('//SubQuestion') X(C);
我希望这可以通过递归CTE来解决,但是我把它放在一起很麻烦。
答案 0 :(得分:1)
到目前为止,我正在这样做,虽然我仍然希望稍微压缩查询:
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;
这是重要的一点,因为它将获得第一个非空ParentId
值中的任何一个:
COALESCE(
C.query('..').value('(Question/questionId)[1]', 'NVarChar(1000)'),
C.query('..').value('(SubQuestion/questionId)[1]', 'NVarChar(1000)')
) ParentId
答案 1 :(得分:1)
鉴于您已使用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查询
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);
答案 2 :(得分:0)
您可以使用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