XQuery - 优化BIG数据库的查询

时间:2012-02-13 14:24:54

标签: oracle11g query-optimization xquery marklogic

我有一个大的xml数据库(30 000个文件,1.3 Go)。此数据库中的一个文件列出了数据库中存在的所有其他文件。我的目标是“简单地”检查列出的所有文件是否都存在于数据库中。但我不能处理文件的名称,只能处理文档中的XML代码。

就是这样:

declare variable $root :=  fn:collection();

declare function local:isValid($fileCode) {

let $fileSearchedIdentCode := $root/dmodule/identity/dmCode
return 
$fileCode/@attribute1 = $fileSearchedIdentCode/@attribute1 and
$fileCode/@attribute2 = $fileSearchedIdentCode/@attribute2 and
$fileCode/@attribute3 = $fileSearchedIdentCode/@attribute3

};

<result>
{ 
for $fileCode in $root/file[identity/@fileType eq 'listOfFiles']/fileContent/fileEntry/fileCode 
return
    if (local:isValid($fileCode))
    then   <filePresent>1</filePresent>  
    else <fileNonPresent>2</fileNonPresent>

}
</result>

上面的代码是针对一个小数据库运行的,但是对于我来说,它需要大量的时间。

所以,我想知道是否有人可以帮助我改进代码以便在合理的时间内执行它;)

(我的数据库已编入索引)

感谢您的帮助!!

约翰

5 个答案:

答案 0 :(得分:3)

似乎属性索引未应用于local:isValid函数中的属性检查。您可以通过将它们重写为XPath谓词来实现这一目标:

declare variable $root :=  fn:collection();

declare function local:isValid($fileCode) {
  $root/dmodule/identity/dmCode[@attribute1 = $fileCode/@attribute1
    and @attribute2 = $fileCode/@attribute2
    and @attribute3 = $fileCode/@attribute3]
};

<result> { 
  for $fileCode in $root/file[identity/@fileType = 'listOfFiles']/fileContent/fileEntry/fileCode 
  return
    if (local:isValid($fileCode))
      then   <filePresent>1</filePresent>  
      else <fileNonPresent>2</fileNonPresent>
}</result>

完成这些更改后,BaseX中的查询信息视图告诉我使用了索引:

Compiling:
- pre-evaluating fn:collection()
- rewriting And expression to predicate(s)
- rewriting fn:boolean(@*:attribute1 = $fileCode/@attribute1)
- rewriting fn:boolean(@*:attribute2 = $fileCode/@attribute2)
- rewriting fn:boolean(@*:attribute3 = $fileCode/@attribute3)
- applying attribute index
- applying attribute index

我的测试数据的评估时间从4'500ms下降到~20ms。

答案 1 :(得分:3)

对于MarkLogic,您需要注意索引查找仅在某些表达式和函数中发生。在这种情况下,您需要更紧凑的代码。这是一个应该产生相同结果的表单,但会以简单的方式使用索引:

<result>
{
    for $fileCode in
      collection()/
      file[identity/@fileType eq "listOfFiles"]/
      fileContent/
      fileEntry/
      fileCode
    let $fc1 := $fileCode/@attribute1/string()
    let $fc2 := $fileCode/@attribute2/string()
    let $fc3 := $fileCode/@attribute3/string()
    return
      if (collection()/
          dmodule/
          identity/
          dmCode[
            @attribute1 eq $fc1][
            @attribute2 eq $fc2][
            @attribute3 eq $fc3])
      then <filePresent>1</filePresent>
      else <fileNonPresent>2</fileNonPresent>
  }
</result>

但是,该代码将根据listOfFiles条目执行一次数据库查找,这不是最佳选择。

可以进一步优化。首先,MarkLogic是一个面向文档的数据库,其中每个文档都有一个唯一的URI。因此,如果您只是将三个属性值编码到每个文档URI中,那么效率会更高。我们可能会使用string-join(($fc1, $fc2, $fc3), '/')之类的东西来构建URI。然后,您可以使用doc()调用检查每个值,这比XPath查找更有效 - 即使使用索引也是如此。一旦进行了更改,listOfFiles文档也可以存储URI而不是属性值。

其次,我认为结果格式不是很有用。它告诉你有些文件遗失了,但不是哪些文件。我会重构,以便代码只返回丢失的文档URI。我们还可以在MarkLogic中启用额外的索引:URI词典。这会自动维护所有文档URI的值索引,类似于listOfFiles文档。使用URI词典,我可以写:

<result>{
    let $uris :=
      collection()/
      file[identity/@fileType eq "listOfFiles"]/
      fileContent/
      fileEntry/
      fileCode/
      string-join(
        (@attribute1/string(),
         @attribute2/string(),
         @attribute3/string()),
        "/")
    let $uris-present := cts:uris((), "document", cts:document-query($uris))
    for $uri in $uris
    where not($uri = $uris-present)
    return <missing>{ $uri }</missing>
}</result>

这只需要一次数据库查找,并在内存中完成剩下的必要工作。它应该比原始查询或我的第一次迭代更好地扩展。如果您不同意我对结果格式的修改,并且仍希望查看每个输入fileCode的结果,则可以将...where...return...子句重构为...return...if...then...else...,就像在原始查询中一样

请务必使用https://github.com/marklogic/cq中的个人资料工具 - 它可以帮助您尝试替代方案并发现优化机会。

答案 2 :(得分:1)

您没有在测试系统列表中包含eXist-db,但如果您有兴趣对数据进行基准测试,那么有一篇很棒的文章可以优化您的查询并智能地使用索引来加速eXist中的性能 - D b。见http://exist-db.org/exist/tuning.xml。您发布的查询应该可以正常工作而无需修改,但文章中的建议肯定会帮助您提高性能。如果您需要帮助,请随时发布到现有的邮件列表。

无论你使用哪种系统,我都非常有兴趣知道你的结果 - 而不仅仅是我 - 我认为会有广泛的兴趣。

祝你好运!

答案 3 :(得分:1)

如果您在比较中使用的每个属性上定义属性范围索引,MarkLogic应该快速处理您的查询。

您可以通过MarkLogic管理界面(http:// 主机名:8001)执行此操作:

  • 在数据库
  • 下选择您的数据库
  • 选择左侧的属性范围索引
  • 选择添加以定义新的属性范围索引
  • 指定引用范围索引的元素(dmcode)和属性(attribute1,attribute2,attribute3)(如果元素位于某个命名空间中,请不要忘记指定命名空间。)
  • 单击“确定”以创建范围索引。

您使用的是什么版本的MarkLogic? 如果您使用的是MarkLogic 5,还可以使用Query Console来测试您的查询:

(HTTP:// 主机名:8000 / qconsole)

随时问您是否有任何疑问/让我知道它是怎么回事。 我来自MarkLogic,很乐意提供帮助。

答案 4 :(得分:0)

用等号(=)替换“eq”会有帮助吗?