2015-12-03 3 views
0

У меня есть следующие данные:Как сделать эффективное внешнее или левое соединение в XQuery?

<!-- subjects.xml --> 
<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>3</Id> 
     <Name>History</Name> 
    </Subject> 
</Subjects> 

, который должен быть присоединен к:

<!-- courses.xml--> 
<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> 

Я хотел бы сделать левый присоединиться к первой таблицы для второй таблицы таким образом, чтобы получить следующее выход:

<Results> 
    <Result> 
     <Table1> 
      <Subject> 
       <Id>1</Id> 
       <Name>Maths</Name> 
      </Subject> 
     </Table1> 
     <Table2> 
      <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> 
     </Table2> 
    </Result> 
    <Result> 
     <Table1> 
      <!-- Notice there are 2 subjects here, as they both have the same ID--> 
      <Subject> 
       <Id>2</Id> 
       <Name>Science</Name> 
      </Subject> 
      <Subject> 
       <Id>2</Id> 
       <Name>Advanced Science</Name> 
      </Subject> 
     </Table1> 
     <Table2> 
      <Course> 
       <SubjectId>2</SubjectId> 
       <Name>Physics</Name> 
      </Course> 
      <Course> 
       <SubjectId>2</SubjectId> 
       <Name>Biology</Name> 
      </Course> 
     </Table2> 
    </Result> 
    <Result> 
     <Table1> 
      <Subject> 
       <Id>3</Id> 
       <Name>History</Name> 
      </Subject> 
     </Table1> 
     <Table2> 
      <!-- Notice this section is empty --> 
     </Table2> 
    </Result> 
</Results> 

у меня есть следующий код, чтобы сделать это:

<Results> 
    { 
     (: For each element in courses, where it's 'SubjectId' exists in "subjects.xml":) 
     for $e2 in doc("courses.xml")/Courses/Course 
     let $foriegnId := $e2/SubjectId 
     group by $foriegnId 
     let $e1 := doc("subjects.xml")/Subjects/Subject[Id = $foriegnId] 
     where $e1 

     return 
      <Result> 
       <Table1> 
        {$e1} 
       </Table1> 
       <Table2> 
        {$e2} 
       </Table2> 
      </Result> 
    } 

    { 
    (: PART2 :) 
    (:Show the remaining elements in courses that have not yet been outputted:) 
     for $e1 in doc('subjects.xml')/Subjects/Subject 
     let $idVal := $e1/Id 
     group by $idVal 
     where not(doc('courses.xml')/Courses/Course/SubjectId = $idVal) 
     return 
      <Result> 
       <Table1> 
        {$e1} 
       </Table1> 
       <Table2/> 
      </Result> 
    } 
</Results> 

Обратите внимание, что код работает нормально и выполняет эту работу. Тем не менее, я обнаружил, что при выполнении кода для больших входов (750 предметов, каждый из которых содержит 120 курсов, а также 100 предметов без курсов и 100 курсов без каких-либо предметов), скрипт работает очень медленно!

Что я могу сделать, чтобы сделать мой скрипт быстрее? Есть ли лучший способ сделать это? Какова временная сложность?

Update 2

Оказывается, я сильно не распознали проблему. Проблема на самом деле была очень мало связана с частью 2 кода, а скорее с частью 1 кода.

Что я сделал:

for $e2 in doc("courses.xml")/Courses/Course 
let $foriegnId := $e2/SubjectId 
let $e1 := doc("subjects.xml")/Subjects/Subject[Id = $foriegnId] 
group by $foriegnId 

когда то, что я должен был сделать было:

for $e2 in doc("courses.xml")/Courses/Course 
let $foriegnId := $e2/SubjectId 
group by $foriegnId 
let $e1 := doc("subjects.xml")/Subjects/Subject[Id = $foriegnId] 

Это уменьшило время кода от 30,000ms около 4,000ms.

Дальнейшие улучшения производительности приветствуются.

+0

Можете ли вы дать ваши измерения? Вы используете Saxon-HE или Saxon-EE? –

+0

Для проблем с бенчмаркингом с большими наборами данных вряд ли полезно предоставлять только небольшие выдержки (отлично подходит для самого вопроса, но вы должны предоставить набор данных, который вы сравниваете с самим собой). Также полезно дать точную версию программного обеспечения. –

+0

Saxon 9.7 - это evrsion. Я использую –

ответ

1

В зависимости от того, как оптимизирован запрос, список идентификаторов может быть объединен снова и снова, один раз для каждого объекта. Выбирайте список заблаговременно и впоследствии проверяйте это.

let $subjectIds := doc('courses.xml')/Courses/Course/SubjectId 
    for $e1 in doc('subjects.xml')/Subjects/Subject 
    let $idVal := $e1/Id 
    group by $idVal 
    where not($subjectIds = $idVal) 
    return 
     <Result> 
      <Table1> 
       {$e1} 
      </Table1> 
      <Table2/> 
     </Result> 

Дальнейшая оптимизация может быть подрезать список идентификаторов частично избыточными при условии последовательности их различных значений до:

let $subjectIds := distinct-values(doc('courses.xml')/Courses/Course/SubjectId) 
+0

, это не оказало большого влияния на время выполнения скрипта. Тем не менее, я сделал это, потому что это может быть проблемой для других движков XQuery. –