В чем разница между этими двумя запросами LINQ?

Глядя на профилировщик, я вижу несколько отличий. Второй запрос, который использует include, фактически вернет данные из вторичной таблицы CountryCodes. Эта часть имеет смысл для меня. Однако я не понимаю, почему этот запрос имеет два соединения. Во-первых, он выполняет обычное внутреннее соединение между CountryCodes и CountryCodeTypes (по внешнему ключу), которого, как мне кажется, будет достаточно, чтобы вернуть все, что требует include. Однако затем он выполняет другое внешнее соединение. Почему?

var query = from codes in base.context.CountryCodes
            join codeTypes in base.context.CountryCodeTypes
            on codes.CountryCodeTypeId equals codeTypes.CountryCodeTypeId
            where codeTypes.CountryCodeTypeName == countryCodeType
            select codes;

var query = from codes in base.context.CountryCodes.Include("CountryCodeType")
            where codes.CountryCodeType.CountryCodeTypeName == countryCodeType
            select codes;

результирующий sql:

 FROM   [dbo].[CountryCode] AS [Extent1]
 INNER JOIN [dbo].[CountryCodeType] AS [Extent2] ON [Extent1].[CountryCodeTypeId] = [Extent2].[CountryCodeTypeId]
 LEFT OUTER JOIN [dbo].[CountryCodeType] AS [Extent3] ON [Extent1].[CountryCodeTypeId] = [Extent3].[CountryCodeTypeId]
 WHERE [Extent2].[CountryCodeTypeName] = @p__linq__0

Кроме того, справедливо ли сказать, что я должен использовать .Include только тогда, когда мне действительно нужны данные в таблице внешнего ключа, которые должны быть заполнены в моем результате, иначе используйте соединение? Другими словами, я не должен использовать .Include в качестве средства для соединения, потому что свойства навигации знают, как объединять сущности для меня на основе ключей.


person e36M3    schedule 14.10.2010    source источник
comment
в вашем вопросе не очень ясно, генерируется ли SQL первым или вторым запросом Linq...   -  person Thomas Levesque    schedule 15.10.2010


Ответы (2)


Это просто природа сгенерированного Entity Framework SQL.

INNER JOIN существует из-за вашего оператора where.

where codes.CountryCodeType.CountryCodeTypeName == countryCodeType

Единственный способ, которым EF может решить эту проблему, - это выполнить INNER JOIN, как вы правильно указали. Вы также правы, отмечая, что INNER JOIN фактически возвращает все данные, необходимые для удовлетворения Include().

Однако ВНЕШНЕЕ СОЕДИНЕНИЕ по-прежнему выполняется просто потому, что EF видит Include() и анализирует его как требующее соединения. Рассмотрим случай, когда у вас нет предложения where — тогда вам понадобится OUTER JOIN, верно? Ну, EF недостаточно умен, чтобы определить, что OUTER JOIN в этом случае не требуется; он видит Include(), а затем генерирует соответствующее ВНЕШНЕЕ СОЕДИНЕНИЕ, чтобы гарантировать, что требования к данным будут удовлетворены. Другими словами, он не рассматривает остальную часть вашего запроса, чтобы определить, требуется ли соединение, - он просто делает это независимо.

Что касается оператора Include(), вы должны использовать его только тогда, когда хотите получить эти связанные объекты обратно в свое приложение. Для этого запроса это не требуется. Самый простой запрос в этом случае будет

var query = from codes in base.context.CountryCodes
            where codes.CountryCodeType.CountryCodeTypeName == countryCodeType
            select codes;
person Kirk Broadhurst    schedule 15.10.2010
comment
Спасибо, Кирк. Итак, справедливо ли будет сказать, что запрос, который вы предложили без соединения, в остальном эквивалентен тому, который я написал с помощью JOIN. Однако ваш запрос просто использует навигационное свойство, выведенное из модели данных? Я также понимаю, что вы имеете в виду по поводу перевода Include() в автоматическое ВНЕШНЕЕ СОЕДИНЕНИЕ. Однако приходит на ум одно. В любой ситуации, когда на самом деле существует ограничение внешнего ключа между двумя таблицами, не будет ли достаточно ВНУТРЕННЕГО СОЕДИНЕНИЯ? - person e36M3; 15.10.2010
comment
1/Вы можете выполнять соединения, используя навигационные свойства, описанные в модели данных — я считаю, что это центральная функция EF. 2/Если бы вы не делали where, не было бы ВНУТРЕННЕГО СОЕДИНЕНИЯ. И если вы хотите получить связанные объекты с помощью Include, если бы EF использовал ВНУТРЕННЕЕ СОЕДИНЕНИЕ, вы бы получили только те объекты, у которых есть связанные объекты. OUTER JOIN гарантирует, что вы извлечете как те, у которых есть связанные объекты, так и те, у которых их нет (где OUTER JOIN пуст). - person Kirk Broadhurst; 15.10.2010

Левое внешнее соединение происходит в результате codes.CountryCodeType.CountryCodeTypeName == countryCodeType, тогда как внутреннее соединение позволяет ему включать поля из таблицы CountryCodeType в окончательные результаты.

Если вам не нужны данные из таблицы внешнего ключа в вашем результате, вам не нужно использовать Include или Join. Если вы не использовали «Включить», будет использоваться только левое внешнее соединение, а не внутреннее соединение.

Я предполагаю, что фреймворк просто недостаточно умен, чтобы понять, что он уже выполнил соединение с этой таблицей и может повторно использовать информацию там. Будем надеяться, что SQL Server достаточно умен, чтобы уловить это и использовать план выполнения, который позволяет избежать дублирования этих усилий.

person StriplingWarrior    schedule 14.10.2010
comment
Разве мне не понадобится соединение, чтобы выполнить условие where? - person e36M3; 15.10.2010
comment
Неа. Платформа определяет, что вы ссылаетесь на связанную таблицу, и создает необходимое соединение в SQL только на основе доступа к свойству. - person StriplingWarrior; 15.10.2010
comment
Я только что понял, что у меня есть внутреннее соединение и внешнее соединение в обратном порядке. Ответ Кирка более правильный. - person StriplingWarrior; 15.10.2010