簡體   English   中英

為什么Entity Framework會生成嵌套的SQL查詢?

[英]Why does the Entity Framework generate nested SQL queries?

為什么Entity Framework會生成嵌套的SQL查詢?

我有這個代碼

    var db = new Context();
    var result = db.Network.Where(x => x.ServerID == serverId)
        .OrderBy(x=> x.StartTime)
        .Take(limit);

哪個產生了這個! (注意雙選語句)

SELECT
`Project1`.`Id`, 
`Project1`.`ServerID`, 
`Project1`.`EventId`, 
`Project1`.`StartTime`
FROM (SELECT
`Extent1`.`Id`, 
`Extent1`.`ServerID`, 
`Extent1`.`EventId`, 
`Extent1`.`StartTime`
FROM `Networkes` AS `Extent1`
 WHERE `Extent1`.`ServerID` = @p__linq__0) AS `Project1`
 ORDER BY 
`Project1`.`StartTime` DESC LIMIT 5

我應該更改什么才能產生一個選擇語句? 我正在使用MySQL和Entity Framework與Code First。

更新

無論傳遞給OrderBy()方法的參數類型如何,我都有相同的結果。

更新2:定時

Total Time (hh:mm:ss.ms)    05:34:13.000
Average Time (hh:mm:ss.ms)  25:42.000
Max Time (hh:mm:ss.ms)  51:54.000
Count   13
First Seen  Nov 6, 12 19:48:19
Last Seen   Nov 6, 12 20:40:22

原始查詢:

SELECT `Project?`.`Id`, `Project?`.`ServerID`, `Project?`.`EventId`, `Project?`.`StartTime` FROM (SELECT `Extent?`.`Id`, `Extent?`.`ServerID`, `Extent?`.`EventId`, `Extent?`.`StartTime`, FROM `Network` AS `Extent?` WHERE `Extent?`.`ServerID` = ?) AS `Project?` ORDER BY `Project?`.`Starttime` DESC LIMIT ?

我使用一個程序從MySQL中的當前進程中獲取快照。

其他查詢同時執行,但是當我將其更改為一個SELECT語句時,它永遠不會超過一秒。 也許我還有別的東西在繼續; 我問'因為我不是這樣的...

更新3:解釋聲明

實體框架生成

'1', 'PRIMARY', '<derived2>', 'ALL', NULL, NULL, NULL, NULL, '46', 'Using filesort'
'2', 'DERIVED', 'Extent?', 'ref', 'serveridneventid,serverid', 'serveridneventid', '109', '', '45', 'Using where'

一個班輪

'1', 'SIMPLE', 'network', 'ref', 'serveridneventid,serverid', 'serveridneventid', '109', 'const', '45', 'Using where; Using filesort'

這來自我的QA環境,因此我上面粘貼的時間與rowcount explain語句無關。 我認為有大約500,000條記錄匹配一個服務器ID。

我從MySQL切換到SQL Server。 我不想最終完全重寫應用程序層。

這是從表達式樹邏輯構建查詢的最簡單方法。 通常表現不會成為問題。 如果您遇到性能問題,可以嘗試這樣的方法來恢復實體:

var results = db.ExecuteStoreQuery<Network>(
    "SELECT Id, ServerID, EventId, StartTime FROM Network WHERE ServerID = @ID", 
    serverId);

results = results.OrderBy(x=> x.StartTime).Take(limit);

我最初的印象是這樣做實際上會更有效率,盡管在對MSSQL服務器進行測試時,我得到了<1秒的響應。

使用單個select語句,它會對所有記錄( Order By )進行排序,然后將它們過濾到您想要查看的集合( Where ),然后排在前5位( Limit 5或者對我來說, Top 5 )。 在大型表格上,排序占用了大部分時間。 使用嵌套語句,它首先將記錄過濾到子集,然后才對其執行昂貴的排序操作。

編輯:我測試了這個,但我意識到我的測試中有一個錯誤使它無效。 測試結果已刪除。

為什么Entity Framework會生成嵌套查詢? 簡單的答案是因為實體框架將您的查詢表達式分解為表達式樹,然后使用該表達式樹來構建查詢。 樹自然生成嵌套查詢表達式(即子節點生成查詢,父節點生成對該查詢的查詢)。

為什么Entity Framework不能簡化查詢並按原樣編寫它? 簡單的答案是因為可以進入查詢生成引擎的工作量有限,雖然它現在比早期版本更好,但它並不完美,可能永遠不會。

所有這一切都表示,您手寫的查詢和本例中生成的查詢EF之間不應存在明顯的速度差異。 數據庫非常聰明,可以生成在任何一種情況下首先應用WHERE子句的執行計划。

如果要在不使用子選擇的情況下讓EF生成查詢,請在查詢中使用常量,而不是變量。

我之前創建了自己的.Where和所有其他LINQ方法,它們首先遍歷表達式樹並將所有變量,方法調用等轉換為Expression.Constant。 這是因為實體框架中的這個問題...

我只是偶然發現了這篇文章,因為我遇到了同樣的問題。 我已經花了幾天跟蹤它,它只是在mysql中生成一個糟糕的查詢。

我已經在mysql.com上提交了一個錯誤http://bugs.mysql.com/bug.php?id=75272

總結一下這個問題:

這個簡單的查詢

context.products
    .Include(x => x.category)
    .Take(10)
    .ToList();

被翻譯成

SELECT
`Limit1`.`C1`, 
`Limit1`.`id`, 
`Limit1`.`name`, 
`Limit1`.`category_id`, 
`Limit1`.`id1`, 
`Limit1`.`name1`
FROM (SELECT
`Extent1`.`id`, 
`Extent1`.`name`, 
`Extent1`.`category_id`, 
`Extent2`.`id` AS `id1`, 
`Extent2`.`name` AS `name1`, 
1 AS `C1`
FROM `products` AS `Extent1` INNER JOIN `categories` AS `Extent2` ON `Extent1`.`category_id` = `Extent2`.`id` LIMIT 10) AS `Limit1`

並表現得很好。 無論如何,外部查詢幾乎沒用。 現在如果我添加一個OrderBy

context.products
    .Include(x => x.category)
    .OrderBy(x => x.id)
    .Take(10)
    .ToList();

查詢更改為

SELECT
`Project1`.`C1`, 
`Project1`.`id`, 
`Project1`.`name`, 
`Project1`.`category_id`, 
`Project1`.`id1`, 
`Project1`.`name1`
FROM (SELECT
`Extent1`.`id`, 
`Extent1`.`name`, 
`Extent1`.`category_id`, 
`Extent2`.`id` AS `id1`, 
`Extent2`.`name` AS `name1`, 
1 AS `C1`
FROM `products` AS `Extent1` INNER JOIN `categories` AS `Extent2` ON `Extent1`.`category_id` = `Extent2`.`id`) AS `Project1`
 ORDER BY 
`Project1`.`id` ASC LIMIT 10

哪個是壞的,因為order by在外部查詢中。 這意味着MySQL必須提取每條記錄才能執行orderby,從而導致using filesort

我確認SQL Server(至少Comapact)不會為相同的代碼生成嵌套查詢

SELECT TOP (10) 
[Extent1].[id] AS [id], 
[Extent1].[name] AS [name], 
[Extent1].[category_id] AS [category_id], 
[Extent2].[id] AS [id1], 
[Extent2].[name] AS [name1], 
FROM  [products] AS [Extent1]
LEFT OUTER JOIN [categories] AS [Extent2] ON [Extent1].[category_id] = [Extent2].[id]
ORDER BY [Extent1].[id] ASC

實際上,實體框架生成的查詢很難看,比LINQ 2 SQL少,但仍然很難看。

但是,很可能您的數據庫引擎將制定所需的執行計划,並且查詢將順利運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM