![](/img/trans.png)
[英]Why does Entity Framework 6 generate complex SQL queries for simple lookups?
[英]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()
方法的參數類型如何,我都有相同的結果。
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語句時,它永遠不會超過一秒。 也許我還有別的東西在繼續; 我問'因為我不是這樣的...
實體框架生成
'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.