Я работаю над проектом с SQL Сервером и EF Core v3.
У меня есть 4 таблицы, связанные друг с другом. Вот мои схемы таблиц:
Я написал 2 запроса Linq к этим таблицам - один из них использовал соединение следующим образом:
var result = (from emailTemplate in _context.EmailTemplates.Include(et => et.EmailTemplateContents)
join priorityLookup in _context.Lookups on new { GroupKey = "PRIORITIES", DetailKey = emailTemplate.Priority, emailTemplate.CompanyId } equals new { priorityLookup.GroupKey, priorityLookup.DetailKey, priorityLookup.CompanyId }
join statusLookup in _context.Lookups on new { GroupKey = "STATUSES", DetailKey = emailTemplate.StatusCode, emailTemplate.CompanyId } equals new { statusLookup.GroupKey, statusLookup.DetailKey, statusLookup.CompanyId }
join priorityLookupLabel in _context.LookupLabels on new { Locale = 1033, priorityLookup.DetailKey, priorityLookup.CompanyId } equals new { priorityLookupLabel.Locale, priorityLookupLabel.DetailKey, priorityLookupLabel.CompanyId }
join statusLookupLabel in _context.LookupLabels on new { Locale = 1033, statusLookup.DetailKey, statusLookup.CompanyId } equals new { statusLookupLabel.Locale, statusLookupLabel.DetailKey, statusLookupLabel.CompanyId }
where emailTemplate.CompanyId == 3
select new EmailTemplateModel
{
Code = emailTemplate.Code,
TemplateName = emailTemplate.TemplateName,
FromEmail = emailTemplate.FromEmail,
BccEmail = emailTemplate.BccEmail,
CcEmail = emailTemplate.CcEmail,
PriorityCode = emailTemplate.Priority,
Priority = priorityLookupLabel.Label,
Subject = emailTemplate.EmailTemplateContents.Subject,
Body = HttpUtility.HtmlDecode(emailTemplate.EmailTemplateContents.Body),
StatusCode = emailTemplate.StatusCode,
Status = statusLookupLabel.Label,
ToEmail = emailTemplate.ToEmail,
TriggerSqlCommand = emailTemplate.TriggerSqlCommand,
TriggerType = emailTemplate.TriggerType,
ModifDate = emailTemplate.ModifDate
}).ToList();
и один из них использует .Include
примерно так:
var results = _context.EmailTemplates
.Where(e => e.CompanyId == 3)
.Include(e => e.EmailTemplateContents)
.Include(e => e.Lookups)
.ThenInclude(g => g.LookupLabels)
.Include(e => e.LookupsNavigation)
.ThenInclude(g => g.LookupLabels)
.Select(e => new EmailTemplateModel
{
Code = e.Code,
TemplateName = e.TemplateName,
FromEmail = e.FromEmail,
BccEmail = e.BccEmail,
CcEmail = e.CcEmail,
PriorityCode = e.Priority,
PriorityLabel = e.Lookups.LookupLabels.FirstOrDefault(l => l.Locale == 1033),
Subject = e.EmailTemplateContents.Subject,
Body = HttpUtility.HtmlDecode(e.EmailTemplateContents.Body),
StatusCode = e.StatusCode,
StatusLabel = e.LookupsNavigation.LookupLabels.FirstOrDefault(l => l.Locale == 1033),
ToEmail = e.ToEmail,
TriggerSqlCommand = e.TriggerSqlCommand,
TriggerType = e.TriggerType,
ModifDate = e.ModifDate
}).ToList();
Я попытался понять, есть ли разница в производительности между этими двумя типами, поэтому я проверил запрос, сгенерированный EF, с помощью профилировщика.
Сценарий SQL, созданный из оператора Join:
SELECT
[e].[Code], [e].[TemplateName], [e].[FromEmail], [e].[BccEmail],
[e].[CcEmail], [e].[Priority], [l1].[Label],
[e0].[Subject], [e0].[Body], [e].[StatusCode], [l2].[Label],
[e].[ToEmail], [e].[TriggerSqlCommand], [e].[TriggerType],
[e].[ModifDate]
FROM [EmailTemplates] AS [e]
INNER JOIN [Lookups] AS [l] ON (('PRIORITIES' = [l].[GroupKey]) AND ([e].[Priority] = [l].[DetailKey])) AND ([e].[CompanyId] = [l].[CompanyId])
INNER JOIN [Lookups] AS [l0] ON (('STATUSES' = [l0].[GroupKey]) AND ([e].[StatusCode] = [l0].[DetailKey])) AND ([e].[CompanyId] = [l0].[CompanyId])
INNER JOIN [LookupLabels] AS [l1] ON ((1033 = [l1].[Locale]) AND ([l].[DetailKey] = [l1].[DetailKey])) AND ([l].[CompanyId] = [l1].[CompanyId])
INNER JOIN [LookupLabels] AS [l2] ON ((1033 = [l2].[Locale]) AND ([l0].[DetailKey] = [l2].[DetailKey])) AND ([l0].[CompanyId] = [l2].[CompanyId])
LEFT JOIN [EmailTemplateContents] AS [e0] ON ([e].[CompanyId] = [e0].[CompanyId]) AND ([e].[Code] = [e0].[EmailTemplateCode])
WHERE [e].[CompanyId] = 3
Сценарий SQL, созданный из оператора .Include
:
SELECT
[e].[Code], [e].[TemplateName], [e].[FromEmail], [e].[BccEmail],
[e].[CcEmail], [e].[Priority], [t0].[DetailKey], [t0].[CompanyId],
[t0].[Locale], [t0].[Label], [t0].[OrderNo], [e0].[Subject], [e0].[Body],
[e].[StatusCode], [t2].[DetailKey], [t2].[CompanyId], [t2].[Locale],
[t2].[Label], [t2].[OrderNo], [e].[ToEmail], [e].[TriggerSqlCommand],
[e].[TriggerType], [e].[ModifDate]
FROM [EmailTemplates] AS [e]
INNER JOIN [Lookups] AS [l] ON ([e].[Priority] = [l].[DetailKey]) AND ([e].[CompanyId] = [l].[CompanyId])
LEFT JOIN [EmailTemplateContents] AS [e0] ON ([e].[CompanyId] = [e0].[CompanyId]) AND ([e].[Code] = [e0].[EmailTemplateCode])
INNER JOIN [Lookups] AS [l0] ON ([e].[StatusCode] = [l0].[DetailKey]) AND ([e].[CompanyId] = [l0].[CompanyId])
LEFT JOIN (
SELECT [t].[DetailKey], [t].[CompanyId], [t].[Locale], [t].[Label], [t].[OrderNo]
FROM (
SELECT [l1].[DetailKey], [l1].[CompanyId], [l1].[Locale], [l1].[Label], [l1].[OrderNo], ROW_NUMBER() OVER(PARTITION BY [l1].[DetailKey], [l1].[CompanyId] ORDER BY [l1].[DetailKey], [l1].[CompanyId], [l1].[Locale]) AS [row]
FROM [LookupLabels] AS [l1]
WHERE [l1].[Locale] = 1033
) AS [t]
WHERE [t].[row] <= 1
) AS [t0] ON ([l].[DetailKey] = [t0].[DetailKey]) AND ([l].[CompanyId] = [t0].[CompanyId])
LEFT JOIN (
SELECT [t1].[DetailKey], [t1].[CompanyId], [t1].[Locale], [t1].[Label], [t1].[OrderNo]
FROM (
SELECT [l2].[DetailKey], [l2].[CompanyId], [l2].[Locale], [l2].[Label], [l2].[OrderNo], ROW_NUMBER() OVER(PARTITION BY [l2].[DetailKey], [l2].[CompanyId] ORDER BY [l2].[DetailKey], [l2].[CompanyId], [l2].[Locale]) AS [row]
FROM [LookupLabels] AS [l2]
WHERE [l2].[Locale] = 1033
) AS [t1]
WHERE [t1].[row] <= 1
) AS [t2] ON ([l0].[DetailKey] = [t2].[DetailKey]) AND ([l0].[CompanyId] = [t2].[CompanyId])
WHERE [e].[CompanyId] = 3
I сравнил фактические планы выполнения для обоих, чтобы увидеть, в чем разница между этими двумя.
Вот план выполнения соединения:
и вот план выполнения включения:
Стоимость обоих запросов равна 50%.
Теперь у меня есть пара вопросов:
- Исходя из стоимости запроса (50%), следует ли мне рассматривать эти два показателя одинаково с точки зрения производительности?
- Есть ли какие-либо предложения по использованию include или join, чтобы сделать один из них быстрее или с меньшими затратами?
- Каковы преимущества и недостатки использования Join (синтаксис / обслуживание)?
- Каковы плюсы и минусы использования Include (синтаксис / обслуживание)?
- Какой из них следует использовать, если в таблице несколько записей или много записей?