前言:
從微軟發佈Linq To SQL依此,程序員圍繞其與SqlDataAdapter等相比進行討論,根據CSDN上的報道,LINQ比SqlDataReader落後的速度不超過10%。更加相信微軟對Linq性能的分析會結合算法和統計結果來比較,對於linq查詢性能--博客園的黃昕已經有所分析。於是產生想實驗一下Linq和SqlDataAdapter等分別在大數據量下進行分頁。園子裏的Yzl的研究室已經對Linq分頁可能出現的問題提出一種情況。(以下只是實驗過程並非測試所以沒有benchmark).
實驗步驟:
一、數據庫
數據庫名People
數據表名Prof 包括ID Name Age三個字段
記錄數 10萬條(也許不是很足夠)
下面的數據庫代碼並非最佳方案,大家貼一下自己的
從微軟發佈Linq To SQL依此,程序員圍繞其與SqlDataAdapter等相比進行討論,根據CSDN上的報道,LINQ比SqlDataReader落後的速度不超過10%。更加相信微軟對Linq性能的分析會結合算法和統計結果來比較,對於linq查詢性能--博客園的黃昕已經有所分析。於是產生想實驗一下Linq和SqlDataAdapter等分別在大數據量下進行分頁。園子裏的Yzl的研究室已經對Linq分頁可能出現的問題提出一種情況。(以下只是實驗過程並非測試所以沒有benchmark).
實驗步驟:
一、數據庫
數據庫名People
數據表名Prof 包括ID Name Age三個字段
記錄數 10萬條(也許不是很足夠)
下面的數據庫代碼並非最佳方案,大家貼一下自己的
CREATE DATABASE People
ON
Primary
(
NAME='people',
FILENAME='D:\people_Data.mdf',
SIZE=10,
FILEGROWTH=10%
)
LOG ON
(
NAME='peoplelog',
FILENAME='D:\people_Log.ldf',
SIZE=10,
FILEGROWTH=10%
)
GO
USE People
CREATE TABLE Prof
(
ID INT IDENTITY(1,1) NOT NULL,
Name NVARCHAR(100) COLLATE Chinese_PRC_CI_AS NULL ,
Age INT NULL,
)
GO
測試代碼一(SQL部分):
網上已經有很多非常好的分頁算法,各人按照設計的需要選擇合適的爲好,特別提一下Thin的算法(很簡潔),測試中採用了李洪根發佈的其中一種分頁算法
//省略行參數設置和拼接
string strcmd = "SELECT TOP 20 * " +
"FROM Prof " +
"WHERE (ID >" +
"(SELECT MAX(ID) FROM (SELECT TOP 60000 id FROM Prof ORDER BY id) AS T)) ORDER BY ID";
測試代碼二(Linq部分)
Linq To SQL的分頁主要通過Skip和Take操作符實現,代碼如下:
//省去設置參數
//分頁Skip(PageSize * PageIndex).Take(PageSize)
PeopleDataContext dc = new PeopleDataContext();
var query = (from p in dc.Prof select p).Skip(60000).Take(20);
然而卻出現異常:
此提供程序只支持對返回實體或投影(包含所有標識列)的有序查詢使用 Skip(),這種查詢爲單表(非聯接)查詢,或者爲 Distinct、Except、Intersect 或 Union (非 Concat)操作。
第一次遇到這種異常(當然很多朋友並不會,而且已經看出問題所在了),查找Skip的定義
public static IEnumerable<TSource> Skip<TSource>(
this IEnumerable<TSource> source,
int count
)
此方法通過使用延遲執行實現。即時返回值爲一個對象,該對象存儲執行操作所需的所有信息。只有通過直接調用對象的 GetEnumerator 方法或使用 Visual C# 中的 foreach(或 Visual Basic 中的 For Each)來枚舉該對象時,才執行此方法表示的查詢。
this IEnumerable<TSource> source,
int count
)
此方法通過使用延遲執行實現。即時返回值爲一個對象,該對象存儲執行操作所需的所有信息。只有通過直接調用對象的 GetEnumerator 方法或使用 Visual C# 中的 foreach(或 Visual Basic 中的 For Each)來枚舉該對象時,才執行此方法表示的查詢。
再看一下該查詢生成的SQL代碼:
SELECT TOP 20 [t0].[ID], [t0].[Name], [t0].[Age]
FROM [dbo].[Prof] AS [t0]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM (
SELECT TOP 60000 [t1].[ID]
FROM [dbo].[Prof] AS [t1]
) AS [t2]
WHERE [t0].[ID] = [t2].[ID]
))
FROM [dbo].[Prof] AS [t0]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM (
SELECT TOP 60000 [t1].[ID]
FROM [dbo].[Prof] AS [t1]
) AS [t2]
WHERE [t0].[ID] = [t2].[ID]
))
Skip查詢需要數據標識列提供查詢的根據,是否可以假設Skip是通過標識列的唯一性來逐一返回對象的呢?
修改數據庫People表Prof,設置其ID爲主鍵,(上面的數據庫相應修改爲)
ALTER TABLE Prof
ADD CONSTRAINT PK_ID PRIMARY KEY (ID)
GO
再次運行,顯示查詢耗時:00:00:00.0478485
問題:
1.爲什麼兩次查詢的耗時相差那麼大呢?(估計是個人機器以及代碼問題:))
2.Skip是否通過主鍵的唯一性逐次返回查詢對象?
總結:
SQLServer的執行效率是按照語義來執行的,也許Linq在性能上不一定和SQLDataAdapter等完全一樣,但是在開發效率上,我們可以看出Linq的實現代碼的簡易性是相對較好的,只要克服其中的一些問題,相信Linq會爲以後的數據查詢提供更強大幫助!
備註:
Scott寫了一系列Linq的介紹非常經典
王磊發表的文章中提到過了大量數據分頁的實現,主要用的數據庫爲Northwind而相應的代碼也完善。
Linq性能測試[url]http://www.mbeller.de/2007/12/performance-comparison-between-linq.html[/url]
修改數據庫People表Prof,設置其ID爲主鍵,(上面的數據庫相應修改爲)
ALTER TABLE Prof
ADD CONSTRAINT PK_ID PRIMARY KEY (ID)
GO
再次運行,顯示查詢耗時:00:00:00.0478485
問題:
1.爲什麼兩次查詢的耗時相差那麼大呢?(估計是個人機器以及代碼問題:))
2.Skip是否通過主鍵的唯一性逐次返回查詢對象?
總結:
SQLServer的執行效率是按照語義來執行的,也許Linq在性能上不一定和SQLDataAdapter等完全一樣,但是在開發效率上,我們可以看出Linq的實現代碼的簡易性是相對較好的,只要克服其中的一些問題,相信Linq會爲以後的數據查詢提供更強大幫助!
備註:
Scott寫了一系列Linq的介紹非常經典
王磊發表的文章中提到過了大量數據分頁的實現,主要用的數據庫爲Northwind而相應的代碼也完善。
Linq性能測試[url]http://www.mbeller.de/2007/12/performance-comparison-between-linq.html[/url]