Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Область применения:SQL Server
База данных SQL Azure
Управляемый экземпляр SQL Azure
Azure Synapse Analytics
База данных SQL в Microsoft Fabric
SQL Server использует соединения для получения данных из нескольких таблиц на основе логических связей между ними. Соединения являются фундаментальными для операций реляционной базы данных и позволяют объединять данные из двух или нескольких таблиц в один результирующий набор.
SQL Server реализует как операции логического соединения (определяемые Transact-SQL синтаксисом), так и операции физического соединения (фактические алгоритмы, используемые для выполнения соединений). Понимание обоих аспектов помогает создавать эффективные запросы и оптимизировать производительность базы данных.
К операциям логического соединения относятся:
- Внутренние соединения
- Левые, правые и полные внешние соединения
- Перекрестные соединения
К операциям физического соединения относятся:
- Соединения методом вложенных циклов
- Объединение слиянием
- Хэш-соединения
- Адаптивные соединения (относится к: SQL Server 2017 (14.x) и более поздним версиям.
В этой статье объясняется, как работают соединения, когда используются различные типы соединения и как оптимизатор запросов выбирает наиболее эффективный алгоритм соединения на основе таких факторов, как размер таблицы, доступные индексы и распределение данных.
Note
Дополнительные сведения о синтаксисе соединения см. в предложении FROM и JOIN, APPLY, PIVOT.
Основы присоединения
С помощью соединения можно получать данные из двух или нескольких таблиц на основе логических связей между ними. Соединения указывают, как SQL Server должен использовать данные из одной таблицы для выбора строк в другой таблице.
Условие соединения определяет, каким образом две таблицы связаны в запросе:
- Указание столбца из каждой таблицы, который будет использоваться для соединения. В типичном условии соединения указывается внешний ключ из одной таблицы и связанный с ним ключ из другой таблицы;
- Указывается логический оператор (например, = или <>), используемый для сравнения значений из столбцов.
Соединения выражаются логически с помощью следующего синтаксиса Transact-SQL:
[ INNER ] JOINLEFT [ OUTER ] JOINRIGHT [ OUTER ] JOINFULL [ OUTER ] JOINCROSS JOIN
Внутренние соединения можно задавать в предложениях FROM и WHERE.
Внешние соединения и перекрестные соединения можно задавать только в предложении FROM. Условия соединения сочетаются с условиями поиска WHERE и HAVING для управления строками, выбранными из базовых таблиц, на которые ссылается предложение FROM.
Указание условий соединения в FROM предложении помогает отделять их от других условий поиска, которые могут быть указаны в WHERE предложении, и это рекомендуемый метод для указания соединений. Ниже приведен упрощенный синтаксис соединения с использованием предложения FROM стандарта ISO:
FROM first_table < join_type > second_table [ ON ( join_condition ) ]
- Join_type указывает, какой тип соединения выполняется: внутреннее, внешнее или перекрестное соединение. Описание различных типов соединений см. в предложении FROM.
- join_condition определяет предикат, который вычисляется для каждой пары соединяемых строк.
Следующий код представляет собой пример спецификации объединения условий FROM:
FROM Purchasing.ProductVendor INNER JOIN Purchasing.Vendor
ON ( ProductVendor.BusinessEntityID = Vendor.BusinessEntityID )
Следующий код представляет собой простой оператор SELECT с использованием этого соединения:
SELECT ProductID, Purchasing.Vendor.BusinessEntityID, Name
FROM Purchasing.ProductVendor INNER JOIN Purchasing.Vendor
ON (Purchasing.ProductVendor.BusinessEntityID = Purchasing.Vendor.BusinessEntityID)
WHERE StandardPrice > $10
AND Name LIKE N'F%';
GO
Инструкция SELECT возвращает наименование продукта и сведения о поставщике для всех сочетаний запчастей, поставляемых компаниями с названиями на букву F и стоимостью продукта более 10 долларов.
Если один запрос содержит ссылки на несколько таблиц, то все ссылки столбцов должны быть однозначными. В предыдущем примере как таблица ProductVendor, так и таблица Vendor содержат столбец с именем BusinessEntityID. Имена столбцов, совпадающие в двух или более таблицах, на которые ссылается запрос, должны уточняться именем таблицы. Все ссылки на столбцы Vendor в этом примере полностью определены.
Если имя столбца не дублируется в двух или более таблицах, используемых в запросе, ссылки на него не должны быть квалифицированы с именем таблицы. Это показано в предыдущем примере. Такое SELECT предложение иногда трудно понять, так как нет ничего, указывающего на таблицу, предоставившую каждый столбец. Запрос гораздо легче читать, если все столбцы указаны с именами соответствующих таблиц. Читабельность дополнительно улучшается при использовании псевдонимов таблиц, особенно когда имена самих таблиц должны указываться вместе с именами базы данных и владельцев. Следующий код представляет собой тот же пример, за исключением того, что таблицам присвоены псевдонимы, а имена столбцов уточнены с помощью псевдонимов таблиц для повышения удобочитаемости:
SELECT pv.ProductID, v.BusinessEntityID, v.Name
FROM Purchasing.ProductVendor AS pv
INNER JOIN Purchasing.Vendor AS v
ON (pv.BusinessEntityID = v.BusinessEntityID)
WHERE StandardPrice > $10
AND Name LIKE N'F%';
В предыдущем примере условие соединения задается в предложении FROM, что является рекомендуемым способом. В следующем запросе это же условие соединения указывается в предложении WHERE:
SELECT pv.ProductID, v.BusinessEntityID, v.Name
FROM Purchasing.ProductVendor AS pv, Purchasing.Vendor AS v
WHERE pv.BusinessEntityID=v.BusinessEntityID
AND StandardPrice > $10
AND Name LIKE N'F%';
Список SELECT для соединения может ссылаться на все столбцы в соединяемых таблицах или на любое подмножество этих столбцов. Список SELECT не обязательно должен содержать столбцы из каждой таблицы в объединении. Например, в соединении из трех таблиц связующим звеном между одной из таблиц и третьей таблицей может быть только одна таблица, при этом список выборки не обязательно должен ссылаться на столбцы средней таблицы. Это также называется антиполусоединением.
Хотя обычно в условиях соединения для сравнения используется оператор равенства (=), можно указать другие операторы сравнения или реляционные операторы, равно как другие предикаты. Дополнительные сведения см. в разделе "Операторы сравнения " и WHERE.
При присоединении к SQL Server оптимизатор запросов выбирает наиболее эффективный метод (из нескольких возможностей) обработки соединения. Это включает выбор наиболее эффективного типа физического соединения, порядок объединения таблиц и даже использование типов операций логического соединения, которые нельзя выразить напрямую с помощью синтаксиса Transact-SQL, например полусоединения и антиполусоединения. Физическое выполнение различных соединений может использовать множество различных оптимизаций и поэтому не может быть надежно предсказано. Дополнительные сведения о полусоединениях и анти-полусоединениях смотрите в справочнике по логическим и физическим операторам плана выполнения.
Столбцы, используемые в условии соединения, не требуют того же имени или того же типа данных. Однако если типы данных не идентичны, они должны быть совместимыми или быть типами, которые SQL Server может неявно преобразовать. Если типы данных не могут быть неявно преобразованы, условие соединения должно явно преобразовать тип данных с помощью CAST функции. Дополнительные сведения о неявных и явных преобразованиях см. в разделе "Преобразование типов данных" (ядро СУБД).
Большинство запросов, использующих соединение, можно переписать с помощью подзапроса (запроса, вложенного в другой запрос), а большинство подзапросов можно переписать как соединения. Дополнительные сведения о вложенных запросах см. в разделе Subqueries (SQL Server).
Note
Таблицы не могут быть присоединены непосредственно к столбцам ntext, текста или изображения. Однако соединить таблицы по столбцам ntext, text или image можно косвенно, с помощью SUBSTRING.
Например, SELECT * FROM t1 JOIN t2 ON SUBSTRING(t1.textcolumn, 1, 20) = SUBSTRING(t2.textcolumn, 1, 20) выполняет двухтабличное внутреннее объединение по первым 20 символам каждого текстового столбца в таблицах t1 и t2.
Другая возможность сравнения столбцов ntext и text из двух таблиц заключается в сравнении длины столбцов с предложением WHERE, например: WHERE DATALENGTH(p1.pr_info) = DATALENGTH(p2.pr_info)
Общие сведения о соединениях вложенных циклов
Если один вход соединения имеет небольшой размер (менее десяти строк), а другой вход сравнительно большой и индексирован по соединяемым столбцам, индексное соединение вложенных циклов является самой быстрой операцией соединения, так как для нее потребуется наименьшее количество операций сравнения и ввода-вывода.
Соединение вложенных циклов, называемое также вложенной итерацией, использует один ввод соединения в качестве внешней входной таблицы (на графической схеме выполнения она является верхним входом), а второй в качестве внутренней (нижней) входной таблицы. Внешний цикл обрабатывает внешнюю входную таблицу строка за строкой. Во внутреннем цикле для каждой внешней строки производится сканирование внутренней входной таблицы и вывод совпадающих строк.
В простейшем случае поиск просматривает таблицу или индекс целиком; это называется наивным соединением вложенными циклами. Если поиск использует индекс, он называется соединением вложенных циклов по индексу. Если индекс построен в рамках плана запроса (и уничтожен после завершения запроса), он называется присоединением к вложенным циклам временных индексов. Все эти варианты учитываются оптимизатором запросов.
Соединение вложенных циклов является особенно эффективным в случае, когда внешние входные данные сравнительно невелики, а внутренние входные данные велики и заранее индексированы. Во многих небольших транзакциях, например в тех, которые затрагивают лишь небольшое количество строк, соединения вложенными циклами с использованием индекса превосходят как соединения слиянием, так и хэш-соединения. Однако в больших запросах соединения вложенных циклов часто являются не лучшим вариантом.
Если для атрибута OPTIMIZED оператора соединения вложенными циклами задано значение True, это означает, что оптимизированные соединения вложенными циклами (или пакетная сортировка) используются для уменьшения количества операций ввода-вывода, когда внутренняя таблица имеет большой размер, независимо от того, выполняется ли ее параллельная обработка. Наличие этой оптимизации в данном плане может быть не очень очевидным при анализе плана выполнения, учитывая, что сама сортировка является скрытой операцией. Но если посмотреть XML плана и найти атрибут OPTIMIZED, это указывает на то, что соединение Nested Loops может попытаться изменить порядок входных строк, чтобы повысить производительность операций ввода-вывода.
Объединение слиянием
Если входы соединения не являются малыми, но отсортированы по столбцу соединения (например, если они были получены путем сканирования отсортированных индексов), слияние соединения является самой быстрой операцией. Если оба входных набора данных велики и имеют сопоставимые размеры, то соединение слиянием после предварительной сортировки и хэш-соединение обеспечивают примерно одинаковую производительность. Однако операции хэш-соединения часто выполняются быстрее, если два входа значительно отличаются по размеру.
Соединение слиянием требует сортировки обоих входных данных по столбцам слияния, которые определяются предложениями равенства (ON) предиката соединения. Оптимизатор запросов обычно сканирует индекс, если существует индекс по соответствующему набору столбцов, либо помещает оператор сортировки ниже операции слияния. В редких случаях может быть несколько условий равенства, но столбцы для слияния берутся только из некоторых доступных условий равенства.
Так как каждый набор входных данных сортируется, оператор Merge Join получает строку из каждого набора входных данных и сравнивает их. Например, для операций внутреннего соединения строки возвращаются в том случае, если они равны. Если они не равны, строка нижнего значения удаляется, а другая строка получается из этого входного значения. Этот процесс повторяется, пока не будет выполнена обработка всех строк.
Операция объединения слиянием — это обычная или многоуровневая операция. При соединении слиянием «многие ко многим» используется временная таблица для хранения строк. Если в обоих входных потоках есть повторяющиеся значения, то один из потоков должен возвращаться к началу последовательности повторов по мере обработки каждого повтора в другом входном потоке.
При наличии остаточного предиката все строки, удовлетворяющие предикату слияния, определяют остаточный предикат, и возвращаются только те строки, которые ему соответствуют.
Соединение слиянием само по себе выполняется очень быстро, но может оказаться затратным вариантом, если требуется сортировка. Однако, если объём данных велик и нужные данные можно получить в уже отсортированном виде из существующих индексов B-деревьев, соединение слиянием часто оказывается самым быстрым из доступных алгоритмов соединения.
Хэш-соединения
Хэш-соединения могут эффективно обрабатывать большие, несортированные и неиндексированные входы. Они полезны для получения промежуточных результатов в сложных запросах из-за следующего.
- Промежуточные результаты не индексируются (если только явно не сохраняются на диске, а затем индексируются) и часто не сортируются для следующей операции в плане запроса.
- Оптимизаторы запросов оценивают только размеры промежуточных результатов. Поскольку оценки для сложных запросов могут быть очень неточными, алгоритмы обработки промежуточных результатов должны быть не только эффективными, но и сохранять работоспособность, если промежуточный результат оказывается значительно больше ожидаемого.
Хэш-соединение позволяет уменьшить денормализацию. Денормализация обычно используется для получения более высокой производительности при уменьшении количества операций соединения, несмотря на издержки, вызываемые избыточностью данных, например несогласованных обновлений. Хэш-соединения снижают потребность в денормализации Хеш-соединения позволяют сделать вертикальное секционирование (размещение групп столбцов одной таблицы в отдельных файлах или индексах) реальным вариантом физического проектирования базы данных.
Хэш-соединение имеет два входа: конструктивный и пробный. Оптимизатор запросов распределяет роли таким образом, при котором меньшему входу присваивается значение «конструктивный».
Хэш-соединения используются во многих типах операций сопоставления множеств: внутреннее соединение; левое, правое и полное внешнее соединение; левое и правое полусоединение; пересечение; объединение; и разность. Кроме того, вариант хэш-соединения может выполнять удаление дубликатов и группировку, например SUM(salary) GROUP BY department. В указанной модификации используется общий вход как для конструктивной, так и для пробной ролей.
В представленных ниже разделах описываются различные типы хэш-соединений: хэш-соединения в памяти, поэтапные и рекурсивные хэш-соединения.
Хэш-соединение в памяти
Перед проведением хэш-соединения производится просмотр или вычисление входного конструктивного значения, а затем в памяти создается хэш-таблица. Каждая строка добавляется в корзину хеша в зависимости от значения хеша, вычисленного для хеш-ключа. В случае если конструктивное входное значение имеет размер, меньший объема доступной памяти, то все строки данных могут быть занесены в хэш-таблицу. За этим этапом сборки следует этап зондирования. Весь входной набор данных пробирующей стороны сканируется или вычисляется по одной строке за раз; для каждой такой строки вычисляется значение хеш-ключа, затем сканируется соответствующий хеш-бакет и формируются совпадения.
хэш-соединение Грейс
Если входные данные сборки не помещаются в память, хэш-соединение выполняется в несколько этапов. Указанный процесс называется плавным хэш-соединением. Каждый шаг включает фазу сборки и фазу зондирования. Сначала все входные данные build и probe полностью считываются и разбиваются на несколько файлов с использованием хеш-функции по хеш-ключам. Использование хэш-функции для хэш-ключей гарантирует, что любые две объединяемые записи должны находиться в одной и той же паре файлов. Таким образом, задача объединения двух больших входных наборов данных была сведена к нескольким меньшим подзадачам той же задачи. Затем хэш-соединение применяется к каждой паре разделенных файлов.
Рекурсивное хэш-соединение
Если объем входных данных для построения настолько велик, что входные данные для стандартного внешнего слияния потребовали бы нескольких уровней слияния, то потребуются несколько шагов разбиения и несколько уровней разбиения. Если большими являются только некоторые из разделов, дополнительные шаги разбиения используются только для этих конкретных разделов. Чтобы максимально ускорить проведение всех шагов разбиения, используются емкие асинхронные операции ввода-вывода, в результате чего один поток может занимать сразу несколько жестких дисков.
Note
Если объем входных данных для построения лишь немного превышает объем доступной памяти, элементы хэш-соединения в памяти и grace hash join объединяются в одном шаге, в результате чего получается гибридное хэш-соединение.
Во время оптимизации не всегда можно определить, какое хэш-соединение используется. Поэтому SQL Server сначала использует хэш-соединение в памяти, а затем, в зависимости от размера входных данных build-стороны, постепенно переходит к grace hash join и рекурсивному хэш-соединению.
Если оптимизатор запросов ошибочно определяет, какой из двух входов меньше и, следовательно, должен был быть входом сборки, роли входов сборки и проверки динамически меняются местами. Хэш-соединение обеспечивает использование меньшего файла переполнения в качестве входных данных для построения. Данная функция называется "переключением ролей". Смена ролей происходит внутри хеш-соединения после по крайней мере одной выгрузки на диск.
Note
Переключение ролей происходит независимо от указаний запроса или структуры запроса. Разворот ролей не отображается в плане запроса; Когда это происходит, это прозрачно для пользователя.
Восстановление хэша
Термин «hash bailout» иногда используется для обозначения grace hash join или рекурсивных хеш-соединений.
Note
Наличие рекурсивных хэш-соединений и аварийных остановок снижает производительность сервера. Если в трассировке видно много событий Hash Warning, обновите статистику по столбцам, участвующим в соединении.
Дополнительные сведения о прерывании хеширования см. в разделе Класс событий предупреждений хеширования.
Адаптивные соединения
Адаптивные соединения в пакетном режиме позволяют отложить выбор метода Хэш-соединение или Соединение вложенными цикламидо завершения сканирования первых входных данных. Оператор адаптивного соединения определяет порог, который используется для определения момента переключения на план Nested Loops. Таким образом, во время выполнения план запроса может динамически переключаться на более эффективную стратегию соединения без перекомпиляции.
Tip
Больше всего эта функция будет полезна для нагрузок с частыми колебаниями между малыми и большими объёмами входного сканирования для операций соединения.
Решение, принимаемое во время выполнения, основано на следующих шагах:
- Если число строк во входных данных соединения сборки настолько мало, что соединение вложенными циклами будет эффективнее хэш-соединения, план переключается на алгоритм вложенных циклов.
- Если число строк во входных данных соединения сборки превышает порог, переключение не выполняется и план продолжает использовать хэш-соединение.
Следующий запрос используется в качестве наглядного примера адаптивного соединения:
SELECT [fo].[Order Key], [si].[Lead Time Days], [fo].[Quantity]
FROM [Fact].[Order] AS [fo]
INNER JOIN [Dimension].[Stock Item] AS [si]
ON [fo].[Stock Item Key] = [si].[Stock Item Key]
WHERE [fo].[Quantity] = 360;
Этот запрос возвращает 336 строк. Если включить функцию Статистика активных запросов, отобразится следующий план:
Обратите внимание на следующие моменты в плане:
- Сканирование columnstore-индекса используется для выдачи строк на этапе построения хэш-соединения.
- Новый оператор адаптивного соединения. Он определяет пороговое значение, по которому принимается решение о переключении на план вложенного цикла. В этом примере порог равен 78 строкам. Если число строк >= 78, будет использоваться хэш-соединение. Если меньше порога, будет использоваться соединение методом вложенных циклов.
- Так как запрос возвращает 336 строк, это превысило пороговое значение, поэтому вторая ветвь представляет этап проверки стандартной операции соединения хэша. Статистика запросов в реальном времени показывает, как строки проходят через операторы; в данном случае — «672 из 672».
- И последняя ветвь — это поиск по кластеризованному индексу для использования соединением Nested Loops, если бы порог не был превышен. Отображается 0 из 336 строк (ветвь неиспользуется).
Теперь сравните план с тем же запросом, но в случае, когда значению Quantity в таблице соответствует только одна строка:
SELECT [fo].[Order Key], [si].[Lead Time Days], [fo].[Quantity]
FROM [Fact].[Order] AS [fo]
INNER JOIN [Dimension].[Stock Item] AS [si]
ON [fo].[Stock Item Key] = [si].[Stock Item Key]
WHERE [fo].[Quantity] = 361;
Запрос возвращает одну строку. Если включить функцию "Статистика активных запросов", отобразится следующий план:
Обратите внимание на следующие моменты в плане:
- После возврата одной строки через оператор Clustered Index Seek теперь проходят строки.
- И поскольку этап построения Hash Join не продолжился, строки не проходят через вторую ветвь.
Замечания по адаптивному соединению
Адаптивные соединения требуют больше памяти, чем эквивалентный план с индексированным соединением вложенными циклами. Дополнительная память запрашивается так, как если бы соединение Nested Loops было соединением Hash. Кроме того, на этапе сборки для циклической операции присутствуют издержки по сравнению с эквивалентным соединением потокового выполнения с вложенными циклами. При этом дополнительная стоимость обеспечивает гибкость в сценариях, когда количество строк изменяется в входных данных сборки.
Адаптивные соединения в пакетном режиме используются для первого выполнения инструкции. После компиляции последовательные выполнения остаются адаптивными с учетом порога скомпилированных адаптивных соединений и строк времени выполнения, передаваемых через этап сборки внешних входных данных.
Если Adaptive Join переключается на операцию Nested Loops, он использует строки, уже считанные на этапе построения Hash Join. Этот оператор не перечитывает строки внешней ссылки.
Отслеживание активности адаптивного соединения
Оператор Adaptive Join имеет следующие атрибуты оператора плана:
| Атрибут Plan | Description |
|---|---|
| AdaptiveThresholdRows | Показывает пороговое значение, используемое для переключения с хэш-соединения на соединение вложенными циклами. |
| EstimatedJoinType | Каким, вероятнее всего, будет тип соединения. |
| ActualJoinType | В фактическом плане показано, какой алгоритм соединения был в конечном итоге выбран на основе порогового значения. |
Предполагаемый план показывает форму плана адаптивного соединения, а также определенное пороговое значение адаптивного соединения и предполагаемый тип соединения.
Tip
Хранилище запросов сохраняет план адаптивного соединения, выполняемого в пакетном режиме, и может принудительно использовать его.
Допустимые инструкции адаптивного соединения
Чтобы логическое соединение стало допустимым для адаптивного соединения в пакетном режиме, должны выполняться следующие условия:
- Уровень совместимости базы данных имеет значение 140 или больше.
- Запрос является инструкцией
SELECT(инструкции для изменения данных сейчас недопустимы). - Соединение может выполняться посредством как индексированного соединения вложенными циклами, так и физического алгоритма хэш-соединения.
- Хэш-соединение использует пакетный режим, который включается благодаря наличию в запросе индекса columnstore, таблице с индексом columnstore, на которую соединение ссылается напрямую, или с помощью Пакетного режима для rowstore.
- Созданные альтернативные решения соединения вложенными циклами и хэш-соединения должны иметь одинаковый первый дочерний элемент (внешняя ссылка).
Строки адаптивного порогового значения
На диаграмме ниже показан пример точки, в которой пересекаются затраты на хеш-соединение и на альтернативное соединение вложенными циклами. В этой точке пересечения определяется пороговое значение, что, в свою очередь, определяет фактический алгоритм, используемый для операции соединения.
Отключение адаптивных соединений без изменения уровня совместимости
Адаптивные соединения можно отключить в области базы данных или инструкций, сохраняя уровень совместимости базы данных 140 и более поздних версий.
Чтобы отключить адаптивные соединения для всех запросов, поступающих из базы данных, выполните следующую команду в контексте соответствующей базы данных:
-- SQL Server 2017
ALTER DATABASE SCOPED CONFIGURATION SET DISABLE_BATCH_MODE_ADAPTIVE_JOINS = ON;
-- Azure SQL Database, SQL Server 2019 and later versions
ALTER DATABASE SCOPED CONFIGURATION SET BATCH_MODE_ADAPTIVE_JOINS = OFF;
Если этот параметр включен, он отображается как включенный в sys.database_scoped_configurations.
Чтобы снова включить адаптивные соединения для всех запросов, выполняемых из базы данных, выполните следующую команду в контексте соответствующей базы данных:
-- SQL Server 2017
ALTER DATABASE SCOPED CONFIGURATION SET DISABLE_BATCH_MODE_ADAPTIVE_JOINS = OFF;
-- Azure SQL Database, SQL Server 2019 and later versions
ALTER DATABASE SCOPED CONFIGURATION SET BATCH_MODE_ADAPTIVE_JOINS = ON;
Вы также можете отключить адаптивные соединения для определенного запроса, назначив DISABLE_BATCH_MODE_ADAPTIVE_JOINS в качестве указания запроса USE HINT. Рассмотрим пример.
SELECT s.CustomerID,
s.CustomerName,
sc.CustomerCategoryName
FROM Sales.Customers AS s
LEFT OUTER JOIN Sales.CustomerCategories AS sc
ON s.CustomerCategoryID = sc.CustomerCategoryID
OPTION (USE HINT('DISABLE_BATCH_MODE_ADAPTIVE_JOINS'));
Note
Подсказка запроса USE HINT имеет приоритет над конфигурацией с областью действия на уровне базы данных или настройкой флага трассировки.
Значения NULL и соединения
Если в столбцах таблиц, которые объединяются, есть значения NULL, то эти значения не совпадают друг с другом. Значения NULL в столбце одной из соединяемых таблиц могут быть возвращены только при использовании внешнего соединения (если только предикат WHERE не исключает значения NULL).
Ниже приведены две таблицы, в каждой из которых в столбце, который будет участвовать в объединении, содержится NULL:
table1 table2
a b c d
------- ------ ------- ------
1 one NULL two
NULL three 4 four
4 join4
Соединение, которое сравнивает значения в столбце a с столбцом c , не получает совпадение по столбцам со значениями NULL:
SELECT *
FROM table1 t1 JOIN table2 t2
ON t1.a = t2.c
ORDER BY t1.a;
GO
Возвращается только одна строка со значением 4 в столбцах a и c:
a b c d
----------- ------ ----------- ------
4 join4 4 four
(1 row(s) affected)
Значения NULL, возвращаемые из базовой таблицы, также сложно отличить от значений NULL, возвращаемых при внешнем соединении. Например, следующая инструкция SELECT выполняет левое внешнее соединение этих двух таблиц.
SELECT *
FROM table1 t1 LEFT OUTER JOIN table2 t2
ON t1.a = t2.c
ORDER BY t1.a;
GO
Вот набор результатов.
a b c d
----------- ------ ----------- ------
NULL three NULL NULL
1 one NULL NULL
4 join4 4 four
(3 row(s) affected)
Результаты не облегчают задачу различить NULL в данных от NULL, который представляет собой ошибку соединения. Когда значения NULL присутствуют в данных, которые объединяются, обычно предпочтительнее исключить их из результатов с помощью обычного соединения.