Как сделать эффективное внешнее или левое соединение в 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 курсов без каких-либо предметов) скрипт работает очень медленно!

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

Обновление 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 000 мс до примерно 4 000 мс.

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


person Yahya Uddin    schedule 03.12.2015    source источник
comment
Можешь дать свои замеры? Вы используете Saxon-HE или Saxon-EE?   -  person Michael Kay    schedule 03.12.2015
comment
Для проблем сравнительного анализа с большими наборами данных вряд ли полезно предоставлять только небольшие выдержки (хорошо для самого вопроса, но вы должны предоставлять наборы данных, которые вы сравнивали с собой). Кроме того, полезно указать точную версию программного обеспечения.   -  person Jens Erat    schedule 03.12.2015
comment
Saxon 9.7 - это версия, которую я использую   -  person Yahya Uddin    schedule 04.12.2015


Ответы (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)
person Jens Erat    schedule 03.12.2015
comment
похоже, это не сильно повлияло на время выполнения скрипта. Тем не менее, я проголосовал за это, потому что это потенциально может быть проблемой для других движков XQuery. - person Yahya Uddin; 03.12.2015