我注意到.Take()
的位置對結果SQL沒有影響。例如說我有此查詢:.Take()的位置對生成的SQL沒有影響
IQueryable<Item> query =
db.Items.Where(i => i.CategoryID == categoryID && i.Deleted == false)
.OrderByDescending(i => i.ItemID).Skip(0);
然後執行以下兩個查詢:
IQueryable<ItemViewModel> query1 =
query.Take(20).Select(i => new ItemViewModel { ItemID = i.ItemID });
IQueryable<ItemViewModel> query2 =
query.Select(i => new ItemViewModel { ItemID = i.ItemID }).Take(20);
實體框架將生成兩個查詢以下相同的SQL:
SELECT TOP (20)
[Project1].[ItemID] AS [ItemID],
FROM (SELECT [Project1].[ItemID] AS [ItemID],
row_number() OVER (ORDER BY [Project1].[ItemID] DESC) AS [row_number]
FROM (SELECT
[Filter1].[ItemID] AS [ItemID],
FROM (SELECT [Extent1].[ItemID] AS [ItemID],
[Extent1].[CategoryID] AS [CategoryID]
FROM [dbo].[Items] AS [Extent1]
WHERE 0 = [Extent1].[Deleted]) AS [Filter1]
WHERE ([Filter1].[CategoryID] = @p__linq__0) AND (@p__linq__0 IS NOT NULL)
) AS [Project1]
) AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[ItemID] DESC
爲什麼它這樣做?它不是應該爲子查詢中的TOP 20
生成query1的以下內容嗎?
SELECT
[Project1].[ItemID] AS [ItemID]
FROM (SELECT [Project1].[ItemID] AS [ItemID]
FROM (SELECT TOP (20)
[Extent1].[ItemID] AS [ItemID],
row_number() OVER (ORDER BY [Extent1].[ItemID] DESC) AS [row_number]
FROM [dbo].[Items] AS [Extent1]
WHERE ([Extent1].[CategoryID] = @p__linq__0) AND (@p__linq__0 IS NOT NULL)
AND (0 = [Extent1].[Deleted]) AND [Extent1].[row_number] > 0
ORDER BY [Extent1].[ItemID] DESC
) AS [Project1]
) AS [Project1]
我注意到,移動TOP 20
內會增加時間從7秒在我的實際查詢即時響應,因爲它不TOP 20
之前做的投影。
編輯:不幸的是,我似乎無法找到一種方法來強制實體框架做到這一點,因爲query.Take(20).Select(i => new { ... }).ToString() == query.Select(i => new { .. }).Take(20).ToString()
。也許這是EF中的一個錯誤?
這裏的問題是'Select()'。我希望Take()在選擇之前發生。在我的例子中,我一直在做'query.Where(filter)... .Take(10)'。出於某種原因'query.Select(r => new {..})。(10)'和query.Take(10).Select(r => new {..})是一樣的。我不希望SELECT在整個表上由SQL執行。在我的數據庫中,將「TOP」從外部移動到內部將使查詢時間從7秒減少到即時結果(在實際查詢中,在選擇內部發生了很多事情)。 –
@dhsto如果你在結果上面,選擇不應該在整個數據庫上執行。如果真的如此,我會懷疑查詢提供者存在問題。如果無法進行優化,無論出於何種原因,那麼這並不會讓您有太多的選擇,除了不使用LINQ並自己編寫SQL。 – Servy
@dhsto在Management Studio中運行sql並查看[Execution Plan](執行計劃)(http://stackoverflow.com/questions/758912/how-to-read-an-execution-plan-in-sql-server) 。您將看到實際發生的排序,這是Servy在談論的數據庫引擎爲您執行的重新排序。看看[我的dba網站的這個問題](http://dba.stackexchange.com/questions/29884/execution-plan-flips-filter-and-execute-scalar-when-using-the-pk -causes-cast-to)在where子句中添加一個'和'來改變執行計劃並導致一個轉換錯誤。 –