Другие довольно подробно объяснили вопрос, я хотел бы добавить примечание, что это неопределенное поведение в соответствии со стандартом.
C ++ 11 3.6.1 / 3 Основная функция
Функция main не должна использоваться в программе. Связь (3.5) с main определяется реализацией. Программа, которая определяет main как удаленный или объявляет main как встроенный, статический или constexpr, неверна. Имя main не является зарезервированным. [Пример: функции-члены, классы и перечисления могут называться main, как и сущности в других пространствах имен. - конец примера]
Концептуально вот что происходит, когда вы объединяете три таблицы вместе.
WHERE
) к первой таблице, которая не включает никаких других таблиц. Он выбирает столбцы, упомянутые в условиях JOIN
, списке SELECT
или списке ORDER BY
. Назовите этот результат A ORDER BY
Это происходит концептуально. Фактически, на этом пути есть много возможных оптимизаций. Преимущество реляционной модели состоит в том, что надежная математическая основа делает возможными различные преобразования плана, не изменяя при этом его правильность.
Например, действительно нет необходимости генерировать полные наборы результатов в процессе. Вместо этого ORDER BY
может выполняться путем доступа к данным, в первую очередь, с использованием индекса. Также можно выполнить множество типов соединений.
ORDER BY
может выполняться путем доступа к данным, в первую очередь, с использованием индекса. Также можно выполнить множество типов соединений. на самом деле нет необходимости генерировать полные наборы результатов по пути. Вместо этого ORDER BY
может выполняться путем доступа к данным, в первую очередь, с использованием индекса. Также можно выполнить множество типов соединений. Мы знаем, что данные из B
будут отфильтрованы (внутренним) соединением с A
(данные в A
также фильтруются). Итак, если мы (внутреннее) присоединяемся от B
к C
, то набор C
будет также , отфильтрованным отношением к A
. Также обратите внимание, что любые дубликаты из соединения будут включены .
Однако; в каком порядке это происходит, зависит от оптимизатора; он может решить сначала выполнить соединение B
/ C
, затем ввести A
или любую другую последовательность (возможно, на основе предполагаемого количества строк из каждого соединения и соответствующие индексы).
ОДНАКО; в вашем более позднем примере вы используете соединение LEFT OUTER
; поэтому Аккаунт
не фильтруется вообще , и вполне может быть дублирован, если какая-либо из других таблиц имеет несколько совпадений.
Есть ли дубликаты (для каждой учетной записи) в BalanceToken
?
Я часто считаю, что помогает просмотреть фактический план выполнения. В Query Analyzer / Management Studio вы можете включить это для запросов из меню Query или использовать Ctrl + M. После выполнения запроса план, который был выполнен, отображается на другой вкладке результатов. Из этого вы увидите, что сначала соединяются C и B, а затем результат соединяется с A. План может варьироваться в зависимости от информации, имеющейся в СУБД, потому что оба соединения являются внутренними, что делает его A-and-B-and-C . Я имею в виду, что результат будет одним и тем же независимо от того, какое соединение будет выполнено первым, но время, необходимое для этого, может сильно отличаться, и именно здесь в игру вступают оптимизатор и подсказки.
Соединения могут быть непростыми, и большая часть поведения, конечно, продиктована тем, как данные хранятся в реальных таблицах.
Не видя таблиц, трудно дать четкий ответ в ваш конкретный случай, но я думаю, что основная проблема заключается в том, что вы суммируете несколько наборов результатов, которые объединяются в один.
Возможно, вместо нескольких объединений вы должны создать в своем запросе две отдельные временные таблицы, одну с идентификатором учетной записи, датой и суммой начальных балансов, вторую с идентификатором учетной записи, датой и суммой конечных балансов, а затем объединить эти две таблицы по идентификатору учетной записи. и дату.
Чтобы точно узнать, что происходит с объединениями, в том числе и в вашем конкретном случае, я бы сделал следующее:
Изменил начальную часть
SELECT accountID Accountbalancedate, sum (...) как начальный баланс, sum (...) как закрывающий баланс FROM
до простого
«SELECT * FROM»
Изучите полученную таблицу, и вы увидите, какие именно данные дублируются. Удалите объединения одно за другим и посмотрите, что произойдет. Это должно дать вам представление о том, что именно в ваших данных вызывает дублирование.
Если вы открываете запрос в SQL Server Management Studio (существует бесплатная версия), вы можете редактировать запрос в дизайнере. Визуальное представление того, как соединяются таблицы, также может помочь вам понять, что происходит.