EF Core 3 - 不必要的 ORDER BY 减慢查询

Posted

技术标签:

【中文标题】EF Core 3 - 不必要的 ORDER BY 减慢查询【英文标题】:EF Core 3 - unnecessary ORDER BY slowing down query 【发布时间】:2022-01-06 19:25:31 【问题描述】:

我正在从(非常小的)数据库中检索一些数据,并且查询需要很长时间。

这是 EF Core 代码:

charts = _context.Charts
    .Where(x => x.TableId == csv.Table.TableId)
    .AsNoTracking()
    .Include(x => x.Filters).ThenInclude(x => x.Field)
    .Include(x => x.Grouping)
    .Include(x => x.Value)
    .Include(x => x.PrimarySeries)
    .Include(x => x.Series.OrderBy(y => y.Field.FieldName)).ThenInclude(x => x.Field)
    .OrderBy(x => x.ChartName)
    .ToList<Chart>();

当我使用 .ToQueryString() 提取查询时,EF Core 正在执行以下操作:

DECLARE @__csv_Table_TableId_0 int = 546;

SELECT [c].[ChartId], [c].[AfterFetchJS], [c].[BeforeFetchJS], [c].[ChartName], [c].[ChartTitleJS], [c].[CustomSuffixJS], [c].[GroupingFieldId], [c].[LineWidth], [c].[OnClickJS], [c].[OverrideCheckboxLabel], [c].[PointHoverRadius], [c].[PointRadius], [c].[PrimarySeriesFieldId], [c].[TableId], [c].[Type], [c].[ValueAggregation], [c].[ValueFieldId], [c].[ValueRounding], [c].[YAxisScale], [c].[addSeriesMustHaveVal], [c].[beforeInitSliderJS], [c].[buttonDscr], [c].[chartTitle], [c].[colorByValue], [c].[colorSteps], [c].[customLegend], [c].[enableCrosshairs], [c].[filterExclude], [c].[highColor], [c].[highValue], [c].[loadAllSeries], [c].[lowColor], [c].[lowValue], [c].[primarySeriesExclude], [c].[removeZeroValues], [c].[sliderFilter], [c].[sliderTooltipAlways], [c].[spreadSlider], [c].[stackedBarChart], [c].[xAxisFontSize], [c].[yAxisFontSize], [c].[yAxisSteps], [f].[FieldId], [f].[DataAttribute], [f].[DecimalPlaces], [f].[DefaultCSVImportValue], [f].[DropdownGroup], [f].[FieldName], [f].[Format], [f].[HideAdd], [f].[HideFilter], [f].[HideTable], [f].[Hint], [f].[IntDropdown], [f].[IsRequired], [f].[LabelOverride], [f].[MaxDate], [f].[MaxDec], [f].[MaxInt], [f].[MinDate], [f].[MinDec], [f].[MinInt], [f].[NotMapped], [f].[NotMappedCode], [f].[NotMappedSort], [f].[NullLabel], [f].[OverrideImportSQL], [f].[OverrideSave], [f].[OverrideValidation], [f].[ParentId], [f].[Prefix], [f].[SVGId], [f].[Sequence], [f].[ShortTypeText], [f].[Suffix], [f].[TableId], [f].[TextareaRows], [f].[Type], [f0].[FieldId], [f0].[DataAttribute], [f0].[DecimalPlaces], [f0].[DefaultCSVImportValue], [f0].[DropdownGroup], [f0].[FieldName], [f0].[Format], [f0].[HideAdd], [f0].[HideFilter], [f0].[HideTable], [f0].[Hint], [f0].[IntDropdown], [f0].[IsRequired], [f0].[LabelOverride], [f0].[MaxDate], [f0].[MaxDec], [f0].[MaxInt], [f0].[MinDate], [f0].[MinDec], [f0].[MinInt], [f0].[NotMapped], [f0].[NotMappedCode], [f0].[NotMappedSort], [f0].[NullLabel], [f0].[OverrideImportSQL], [f0].[OverrideSave], [f0].[OverrideValidation], [f0].[ParentId], [f0].[Prefix], [f0].[SVGId], [f0].[Sequence], [f0].[ShortTypeText], [f0].[Suffix], [f0].[TableId], [f0].[TextareaRows], [f0].[Type], [f1].[FieldId], [f1].[DataAttribute], [f1].[DecimalPlaces], [f1].[DefaultCSVImportValue], [f1].[DropdownGroup], [f1].[FieldName], [f1].[Format], [f1].[HideAdd], [f1].[HideFilter], [f1].[HideTable], [f1].[Hint], [f1].[IntDropdown], [f1].[IsRequired], [f1].[LabelOverride], [f1].[MaxDate], [f1].[MaxDec], [f1].[MaxInt], [f1].[MinDate], [f1].[MinDec], [f1].[MinInt], [f1].[NotMapped], [f1].[NotMappedCode], [f1].[NotMappedSort], [f1].[NullLabel], [f1].[OverrideImportSQL], [f1].[OverrideSave], [f1].[OverrideValidation], [f1].[ParentId], [f1].[Prefix], [f1].[SVGId], [f1].[Sequence], [f1].[ShortTypeText], [f1].[Suffix], [f1].[TableId], [f1].[TextareaRows], [f1].[Type], [t].[ChartFieldId], [t].[ChartId_AddAllFilters], [t].[ChartId_AllFilters], [t].[ChartId_ButtonsInsteadOfDropdowns], [t].[ChartId_ForceSelection], [t].[ChartId_HiddenFilters], [t].[ChartId_Series], [t].[ChartId_ValueMustBeNull], [t].[FieldId], [t].[MaxAllowed], [t].[SortOrder], [t].[FieldId0], [t].[DataAttribute], [t].[DecimalPlaces], [t].[DefaultCSVImportValue], [t].[DropdownGroup], [t].[FieldName], [t].[Format], [t].[HideAdd], [t].[HideFilter], [t].[HideTable], [t].[Hint], [t].[IntDropdown], [t].[IsRequired], [t].[LabelOverride], [t].[MaxDate], [t].[MaxDec], [t].[MaxInt], [t].[MinDate], [t].[MinDec], [t].[MinInt], [t].[NotMapped], [t].[NotMappedCode], [t].[NotMappedSort], [t].[NullLabel], [t].[OverrideImportSQL], [t].[OverrideSave], [t].[OverrideValidation], [t].[ParentId], [t].[Prefix], [t].[SVGId], [t].[Sequence], [t].[ShortTypeText], [t].[Suffix], [t].[TableId], [t].[TextareaRows], [t].[Type], [t0].[ChartFieldId], [t0].[ChartId_AddAllFilters], [t0].[ChartId_AllFilters], [t0].[ChartId_ButtonsInsteadOfDropdowns], [t0].[ChartId_ForceSelection], [t0].[ChartId_HiddenFilters], [t0].[ChartId_Series], [t0].[ChartId_ValueMustBeNull], [t0].[FieldId], [t0].[MaxAllowed], [t0].[SortOrder], [t0].[FieldId0], [t0].[DataAttribute], [t0].[DecimalPlaces], [t0].[DefaultCSVImportValue], [t0].[DropdownGroup], [t0].[FieldName], [t0].[Format], [t0].[HideAdd], [t0].[HideFilter], [t0].[HideTable], [t0].[Hint], [t0].[IntDropdown], [t0].[IsRequired], [t0].[LabelOverride], [t0].[MaxDate], [t0].[MaxDec], [t0].[MaxInt], [t0].[MinDate], [t0].[MinDec], [t0].[MinInt], [t0].[NotMapped], [t0].[NotMappedCode], [t0].[NotMappedSort], [t0].[NullLabel], [t0].[OverrideImportSQL], [t0].[OverrideSave], [t0].[OverrideValidation], [t0].[ParentId], [t0].[Prefix], [t0].[SVGId], [t0].[Sequence], [t0].[ShortTypeText], [t0].[Suffix], [t0].[TableId], [t0].[TextareaRows], [t0].[Type]
FROM [Charts] AS [c]
LEFT JOIN [Fields] AS [f] ON [c].[GroupingFieldId] = [f].[FieldId]
LEFT JOIN [Fields] AS [f0] ON [c].[ValueFieldId] = [f0].[FieldId]
LEFT JOIN [Fields] AS [f1] ON [c].[PrimarySeriesFieldId] = [f1].[FieldId]
LEFT JOIN (
    SELECT [c0].[ChartFieldId], [c0].[ChartId_AddAllFilters], [c0].[ChartId_AllFilters], [c0].[ChartId_ButtonsInsteadOfDropdowns], [c0].[ChartId_ForceSelection], [c0].[ChartId_HiddenFilters], [c0].[ChartId_Series], [c0].[ChartId_ValueMustBeNull], [c0].[FieldId], [c0].[MaxAllowed], [c0].[SortOrder], [f2].[FieldId] AS [FieldId0], [f2].[DataAttribute], [f2].[DecimalPlaces], [f2].[DefaultCSVImportValue], [f2].[DropdownGroup], [f2].[FieldName], [f2].[Format], [f2].[HideAdd], [f2].[HideFilter], [f2].[HideTable], [f2].[Hint], [f2].[IntDropdown], [f2].[IsRequired], [f2].[LabelOverride], [f2].[MaxDate], [f2].[MaxDec], [f2].[MaxInt], [f2].[MinDate], [f2].[MinDec], [f2].[MinInt], [f2].[NotMapped], [f2].[NotMappedCode], [f2].[NotMappedSort], [f2].[NullLabel], [f2].[OverrideImportSQL], [f2].[OverrideSave], [f2].[OverrideValidation], [f2].[ParentId], [f2].[Prefix], [f2].[SVGId], [f2].[Sequence], [f2].[ShortTypeText], [f2].[Suffix], [f2].[TableId], [f2].[TextareaRows], [f2].[Type]
    FROM [ChartFields] AS [c0]
    LEFT JOIN [Fields] AS [f2] ON [c0].[FieldId] = [f2].[FieldId]
) AS [t] ON [c].[ChartId] = [t].[ChartId_AllFilters]
LEFT JOIN (
    SELECT [c1].[ChartFieldId], [c1].[ChartId_AddAllFilters], [c1].[ChartId_AllFilters], [c1].[ChartId_ButtonsInsteadOfDropdowns], [c1].[ChartId_ForceSelection], [c1].[ChartId_HiddenFilters], [c1].[ChartId_Series], [c1].[ChartId_ValueMustBeNull], [c1].[FieldId], [c1].[MaxAllowed], [c1].[SortOrder], [f3].[FieldId] AS [FieldId0], [f3].[DataAttribute], [f3].[DecimalPlaces], [f3].[DefaultCSVImportValue], [f3].[DropdownGroup], [f3].[FieldName], [f3].[Format], [f3].[HideAdd], [f3].[HideFilter], [f3].[HideTable], [f3].[Hint], [f3].[IntDropdown], [f3].[IsRequired], [f3].[LabelOverride], [f3].[MaxDate], [f3].[MaxDec], [f3].[MaxInt], [f3].[MinDate], [f3].[MinDec], [f3].[MinInt], [f3].[NotMapped], [f3].[NotMappedCode], [f3].[NotMappedSort], [f3].[NullLabel], [f3].[OverrideImportSQL], [f3].[OverrideSave], [f3].[OverrideValidation], [f3].[ParentId], [f3].[Prefix], [f3].[SVGId], [f3].[Sequence], [f3].[ShortTypeText], [f3].[Suffix], [f3].[TableId], [f3].[TextareaRows], [f3].[Type]
    FROM [ChartFields] AS [c1]
    LEFT JOIN [Fields] AS [f3] ON [c1].[FieldId] = [f3].[FieldId]
) AS [t0] ON [c].[ChartId] = [t0].[ChartId_Series]
WHERE [c].[TableId] = @__csv_Table_TableId_0
ORDER BY [c].[ChartName], [c].[ChartId], [f].[FieldId], [f0].[FieldId], [f1].[FieldId], [t].[ChartFieldId], [t].[FieldId0], [t0].[FieldName], [t0].[ChartFieldId], [t0].[FieldId0]

如果我将 ORDER BY 更改为 ORDER BY [c].[ChartName] 它运行良好/快速。

因此,EF Core 添加到 ORDER BY 的额外(不必要的)列似乎是降低查询速度的原因。

我可以删除额外的(自动添加的)ORDER BY 列吗?或者我还能做些什么来解决缓慢的问题?

注意:数据库是SQL Server express,只有几十行。

【问题讨论】:

【参考方案1】:

关于“或者我还能做些什么来解决缓慢的问题?”您问题的一部分: 很可能会减慢查询速度的一件事是许多包含(和 ThenIncludes)。 尝试在“.ToList()”之前添加“.AsSplitQuery()”。这很可能会加快您的查询速度。

更新:抱歉,我刚刚意识到您已使用 ef-core-3.1 标记了您的问题。 AsSplitQuery 仅从 EF 核心 5 开始可用。

【讨论】:

感谢您的回复 René ... 我会调查 .AsSplitQuery() 以及是否将我的项目升级到 EF Core 5 来试用

以上是关于EF Core 3 - 不必要的 ORDER BY 减慢查询的主要内容,如果未能解决你的问题,请参考以下文章

为啥 EF Core 最后要添加一个额外的 ORDER BY

EF Core 3 Linq 无法翻译

EF - order by specific values

MySQL分组条件,group by order by limit 顺序

为啥 EF Core 会生成倒位比较

使用 ORDER BY 查询 CosmosDB 子集合