在XQuery中查找存在于一个文档中而不是另一个文档中的元素的有效方法

时间:2015-12-03 00:54:23

标签: xml xpath xquery saxon xquery-3.0

我有以下数据:

<Subjects>
    <Subject>
        <Id>1</Id>
        <Name>Maths</Name>
    </Subject>
    <Subject>
        <Id>2</Id>
        <Name>Science</Name>
    </Subject>
    <Subject>
        <Id>2</Id>
        <Name>Advanced Science</Name>
    </Subject>
    <Subject>
        <Id>500</Id>
        <Name>XYZ</Name>
    </Subject>
    <Subject>
        <Id>1000</Id>
        <Name>ABC</Name>
    </Subject>
</Subjects>

<Courses>
    <Course>
        <SubjectId>1</SubjectId>
        <Name>Algebra I</Name>
    </Course>
    <Course>
        <SubjectId>1</SubjectId>
        <Name>Algebra II</Name>
    </Course>
    <Course>
        <SubjectId>1</SubjectId>
        <Name>Percentages</Name>
    </Course>
    <Course>
        <SubjectId>2</SubjectId>
        <Name>Physics</Name>
    </Course>
    <Course>
        <SubjectId>2</SubjectId>
        <Name>Biology</Name>
    </Course>
</Courses>

我希望能够使用subject500获取1000个元素,因为它们不会出现在第二个XML文档中。

我如何以最有效的方式做到这一点(记得我有大约750个科目,每个科目有120门课程)?

1 个答案:

答案 0 :(得分:3)

效率取决于你的优化器,但是既然你在标签中提到了Saxon,我想这就是我们可以瞄准的目标。假设您已将变量$subjects$courses分别绑定到<Subjects><Courses>元素,最简单的查询可能是

$subjects/Subject[not(Id = $courses/Course/SubjectId)]

作为第一步,我会尝试运行它,看看它是否在可接受的时间内产生了正确的结果;从那时起它的性能调整工作。对于性能调整,请确保您拥有不同大小的源文档,以便您可以衡量性能如何随文档大小而变化。

正常情况下,对于连接查询,Saxon-EE将比Saxon-HE做得更好,但是我怀疑它会在这个谓词被表示为否定时取得很大成功。所以这可能会有二次性能。

为了手动优化这个,我会建立一个索引。在XSLT中可以使用xsl:key完成,在XQuery 3.1中可以使用map完成。定义一个包含$ courses中出现的所有SubjectIds的地图:

let $courseSubjects := map:merge($courses/Course/SubjectId ! map{.: true()})

然后使用它来选择:

return $subjects/Subject[not(map:contains($courseSubjects, Id))]

POSTSCRIPT

我低估了Saxon-EE优化器。事实上,它确实生成了一个索引来支持对此连接的评估。因此,创建自己的地图可能是非常不必要的。但我还没有完成任何测量。