【LINQ】分享:举例证明Linq to Sql的数据库端分页货真价实
我们以前写程序,提到数据库端分页,一般会感觉实现起来蛮麻烦的,尤其是对SQL 2000的DB,要自己写一堆SQL或写个存储过程什么的。而现在如果用VS2008里的Linq查询语法的话,对开发人员来说就变的超级简单了。
在上周五(11月20日) Linq介绍的课程上,大家有讨论到Linq to Sql的延迟执行和数据库端分页具体是怎么做到的?我今天用一个小例子测试了一下,分享给大家。
Linq的分页语法如下:
[数据源对象集合].Skip((index-1) * pageSize).Take(pageSize)
比如一个显示sys_users表所有记录的Grid,每页显示20条记录,现在要显示第5页数据的话, Linq的写法如下:
var userList = ctx.sys_users.Skip((5-1)*20).Take(20);
//上面这句的意思也就是查sys_users表,跳过前80条记录,取后面的20条记录
现在我们结合下面的几张图片一边Debug,一边从SQL Server Profiler来看看具体的执行过程:
我们想要的结果是:查sys_users表中的第3、4条记录。
1、当运行到var userList14 = ctx.sys_users.Skip(2).Take(2); 这句时,可以从Profiler看到还没有做任何查询相关的动作。
2、再执行一步,从Profiler看到还是没有做任何查询相关的动作。这就证明var userList14 = ctx.sys_users.Skip(2).Take(2);这句Linq语句只是创建了查询,并没有执行查询,这就是典型的Linq的“延迟执行”,只有当真正用到数据时,才做查询。
3、再执行一步,从Profiler看到还是没有做任何查询相关的动作。
4、最后再执行一步,给GridView做DataBind,现在从Profiler看到执行了查询,并且可以看到编译器帮我们把"ctx.sys_users.Skip(2).Take(2)"这句简单的Linq查询语句转化为较复杂的SQL查询语句。
生成的SQL语句为:
exec sp_executesql N'SELECT [t1].[user_id], [t1].[first_name], [t1].[last_name], [t1].[age], [t1].[birth_date], [t1].[email], [t1].[sex]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[user_id], [t0].[first_name], [t0].[last_name], [t0].[age], [t0].[birth_date], [t0].[email], [t0].[sex]) AS [ROW_NUMBER], [t0].[user_id], [t0].[first_name], [t0].[last_name], [t0].[age], [t0].[birth_date], [t0].[email], [t0].[sex]
FROM [dbo].[sys_users] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
ORDER BY [t1].[ROW_NUMBER]',N'@p0 int,@p1 int',@p0=2,@p1=2
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[user_id], [t0].[first_name], [t0].[last_name], [t0].[age], [t0].[birth_date], [t0].[email], [t0].[sex]) AS [ROW_NUMBER], [t0].[user_id], [t0].[first_name], [t0].[last_name], [t0].[age], [t0].[birth_date], [t0].[email], [t0].[sex]
FROM [dbo].[sys_users] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
ORDER BY [t1].[ROW_NUMBER]',N'@p0 int,@p1 int',@p0=2,@p1=2
查询结果为:
从上面的运行情况来看,应该可以基本了解Linq的延迟执行和数据库端分页的执行原理了吧。
另外要说明两点:
1、上面是针对SQL Server 2005做的测试,生成的SQL语句主要用了ROW_NUMBER这个新特性来做的分页。而如果是SQL Server 2000的话,因为没有ROW_NUMBER,所以生成的SQL语句有些不同,还是用TOP来做分页,SQL 2000中生成的SQL大概如下:
SELECT TOP 2 [t1].[user_id], [t1].[first_name], [t1].[last_name], [t1].[age], [t1].[birth_date], [t1].[email], [t1].[sex]
FROM [dbo].[sys_users] AS [t1]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM (
SELECT TOP 2 [t2].[user_id]
FROM [dbo].[sys_users] AS [t2]
) AS [t3]
WHERE [t1].[user_id] = [t3].[user_id]
))
FROM [dbo].[sys_users] AS [t1]
WHERE NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM (
SELECT TOP 2 [t2].[user_id]
FROM [dbo].[sys_users] AS [t2]
) AS [t3]
WHERE [t1].[user_id] = [t3].[user_id]
))
2、另外要说明一点,在Debug过程中,如果即时查看Linq查询语句的结果视图,会使得Linq查询立即执行。还用上面的例子来看,我在执行到第二步时,去看了一下userList14这个变量的值,就导致了执行查询,如下图所示:
如果大家有其它什么问题,欢迎提出,大家一起探讨。