日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

浅谈SQL Server数据库分页

發布時間:2024/9/20 数据库 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈SQL Server数据库分页 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

數據庫分頁是老生常談的問題了。如果使用ORM框架,再使用LINQ的話,一個Skip和Take就可以搞定。但是有時由于限制,需要使用存儲過程來實現。在SQLServer中使用存儲過程實現分頁的已經有很多方法了。之前在面試中遇到過這一問題,問如何高效實現數據庫分頁。剛好上周在業務中也遇到了這個需求,所以在這里簡單記錄和分享一下。

一 需求

這里以SQLServer的示例數據庫NorthWind為例,里面有一張Product表,現在假設我們的需求是要以UnitPrice降序排列,并且分頁,每一頁10條記錄。要求服務端分頁。參數為每頁記錄數和頁碼。

二 實現

Top分頁

當時采用的最直接做法就是使用兩個Top來實現, 最后返回的結果是升序的,在C#代碼里再處理一下就可以了。 這里作為演示,語句中使用 * 為了方便,實際開發中要替換為具體的列名。下面的方法簡單吧。

SELECT TOP (@pageSize)* FROM ( SELECT TOP ( @pageSize * @pageIndex )*FROM [Northwind].[dbo].[Products]ORDER BY UnitPrice DESC) AS product ORDER BY product.UnitPrice

但是這個代碼是有問題的,不知道各位發現了沒有。當符合條件的紀錄集小于每頁記錄數時,沒有問題,但是當大于就有問題了,比如,在實例數據庫中Products中有 77 條記錄,當每頁10條記錄,第8頁只應該返回7條記錄,第9頁應該返回空,但是使用如上的方法,每次都會返回10條記錄。

沿用上面的思路,把代碼修改為了如下采用三層Select,最內一層查詢所有記錄之前的數據,然后第二層選擇Top PageSize個所有NOT IN 第一層數據中的數據即可,因為使用了NOT IN所以不存在第一種方法中的bug

SELECT * FROM dbo.Products WHERE ProductID IN (SELECT TOP ( @pageSize )ProductIDFROM dbo.ProductsWHERE ProductID NOT IN ( SELECT TOP ( @pageSize * (@pageIndex-1) )ProductIDFROM dbo.ProductsORDER BY UnitPrice DESC )ORDER BY dbo.Products.UnitPrice DESC ) ORDER BY dbo.Products.UnitPrice ASC

使用ROW_NUMBER 函數分頁

其實還有一種最簡單最直接的思路,那就是采用臨時表,即在內存中創建一個表變量,該變量中包含一個自增列,表關鍵字列,然后將待排序的表按照排序條件和規則插入到這張表中,然后就可以將自增列作為行號使用了,在比較早的如SQLServer 2000中,只能這樣做,但是對于大數據量的記錄集,需要創建的臨時表也比較大,效率比較低,這里就不介紹了。

在SQLServer2005中引入了ROW_NUMBER()?函數,通過這個函數,可以根據給定好的排序字段規則,生成記錄序號,其基本用法為:

SELECT ROW_NUMBER() OVER ( ORDER BY dbo.Products.ProductID DESC ) AS rownum ,* FROM dbo.Products

這樣,結果集中第一列就為 rownum,從1開始按步長為1遞增,這有點類似從1開始步長為1的自增字段。 這里需要提一下的是,這個語句中賦值的rownum列不能使用在當前的where語句中,也不可以把整個ROW_NUMBER()語句放到where中作為條件,下面兩種使用方式都是錯誤的。

SELECT ROW_NUMBER() OVER ( ORDER BY dbo.Products.ProductID DESC ) AS rownum ,* FROM dbo.Products WHERE rownum BETWEEN 1 AND 10

會提示錯誤:

Invalid column name 'rownum'. SELECT ROW_NUMBER() OVER ( ORDER BY dbo.Products.ProductID DESC ) AS rownum ,* FROM dbo.Products WHERE ( ROW_NUMBER() OVER (ORDER BY City) AS rown ) BETWEEN 1 AND 10

會提示錯誤:

Incorrect syntax near the keyword 'AS'.

正確的做法是,把查詢的結果作為一個內查詢,再在外面套上一個外查詢語句:

SELECT * FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY dbo.Products.ProductID DESC ) AS rownum ,*FROM dbo.Products) AS temp WHERE temp.rownum BETWEEN 1 AND 10

有了以上基礎之后,我們就可以利用ROW_NUMBER這個特性來進行排序了。

SELECT * FROM ( SELECT TOP ( @pageSize * @pageIndex )ROW_NUMBER() OVER ( ORDER BY dbo.Products.UnitPrice DESC ) AS rownum ,*FROM dbo.Products) AS temp WHERE temp.rownum > ( @pageSize * ( @pageIndex - 1 ) ) ORDER BY temp.UnitPrice

策略很簡單,首先我們選取包含要查頁的數據,然后使用ROW_NUMER函數進行編號, 然后在外查詢中指定rownum大于頁起始記錄即可。這種方式簡單快捷。

這里還有一種使用CTE的方式?(common_table_expression,公用表表達式,不是CTE四六級哦, 我第一次接觸到這個是面試的時候被問到如何使用SQL編寫遞歸, 呵呵),使用很簡單,就是把內查詢放在CTE 里面,如下:

WITH ProductEntityAS ( SELECT TOP ( @pageSize * @pageIndex )ROW_NUMBER() OVER ( ORDER BY dbo.Products.UnitPrice DESC ) AS rownum ,*FROM dbo.Products) SELECT * FROM ProductEntity WHERE ProductEntity.rownum > ( @pageSize * ( @pageIndex - 1 ) ) ORDER BY ProductEntity.UnitPrice

這種性能和上面的類似。但是在某些情況下, 使用CTE會比直接采用外接查詢具有更好的效率。例如,我們可以僅使用CTE來存儲行號,關鍵字以及排序字段,然后用來和原表做join查詢,如下:

WITH ProductEntityAS ( SELECT TOP ( @pageSize * @pageIndex )ROW_NUMBER() OVER ( ORDER BY dbo.Products.UnitPrice DESC ) AS rownum ,ProductID ,--主鍵,UnitPrice--待排序字段FROM dbo.Products) SELECT * FROM ProductEntityINNER JOIN dbo.Products ON dbo.Products.ProductID = ProductEntity.ProductID WHERE ProductEntity.rownum > ( @pageSize * ( @pageIndex - 1 ) ) ORDER BY ProductEntity.UnitPrice

使用ROW_NUMBER來進行分頁是一種使用很廣的分頁方式, 在本文開頭講到在LINQ中可以采用的TAKE 和 SKIP語句,但是與數據庫交互只能使用SQL語句,LINQ在內部會幫我們轉化為合適的SQL語句,語句里面其實也是采用ROW_NUMBER這一函數,為了演示,我們新建一個Console程序,然后在里面添加一個LINQ To SQL的類,使用方法非常簡單,如下:

List<Product> product; int pageSize = 10; int pageIndex = 8; using (ProductsDataContext context = new ProductsDataContext()) {product = context.Products.OrderByDescending(x => x.UnitPrice)//排序.Skip(pageSize * (pageIndex-1))//跳過前面的記錄.Take(pageSize)//選取每一頁個數.ToList(); }

寥寥幾句就實現了分頁。

我們知道LINQ其實是將C#表達式樹轉換成了SQL語言,通過SQLServer Profile 工具,我們可以看到程序發送給SQLServer的請求,如下:

我把下面的語句拷貝出來,可以看到

EXEC sp_executesql N'SELECT [t1].[ProductID], [t1].[ProductName], [t1].[SupplierID], [t1].[CategoryID], [t1].[QuantityPerUnit], [t1].[UnitPrice], [t1].[UnitsInStock], [t1].[UnitsOnOrder], [t1].[ReorderLevel], [t1].[Discontinued] FROM (SELECT ROW_NUMBER() OVER (ORDER BY [t0].[UnitPrice] DESC) AS [ROW_NUMBER], [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued]FROM [dbo].[Products] 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 = 70, @p1 = 10

這正是我們之前手寫的采用ROW_NUMBER 的分頁程序。可見,簡簡單單的一句SKIP和TAKE,LINQ在后面幫我們做了很多工作。

使用OFFSET FETCH子句分頁

既然LINQ這么簡單的搞定了分頁,那么SQLServer中有沒有類似的簡單的語句就能搞定分頁了,答案是有的,那就是SQL Server Compact 4.0中引入的OFFSET FETCH子句。

SELECT * FROM dbo.Products ORDER BY UnitPrice DESC OFFSET ( @pageSize * ( @pageIndex - 1 )) ROWS FETCH NEXT @pageSize ROWS ONLY;

是不是和LINQ很像,OFFSEET相當于SKIP,FETCH NEXT相當于TAKE。

可以在官網上下載SQL Server CE 4.0,目前僅支持SQL Server 2012及SQL Server 2014,不過可以使用Microsoft Webmatrix這個工具來用這一新功能。

比較

在討論性能之前,首先需要明確的是,我們在編寫SQL語句的時候,盡量要減少不必要字段的輸出,文中出于演示,所以都用的*,在實際中不要這樣。還有就是要根據業務邏輯,比如查詢條件,建立合適的聚合索引和非聚合索引,索引對于查找的效率影響非常大,SQL中的索引其實就是建立某種平衡查找樹,如B樹來進行,這方面的知識可以看我之前寫的算法中的文章,再有就是了解一下SQL Server 的一些特性比如CTE,IN 和Exist的區別等等,有些小的地方對性能可能有一定的影響。

在上面這些處理好了之后,我們現在來討論那種分頁方案更好。

  • 采用Top – Not In - Top方案比較復雜,里面包含了in語句,效率不高,但是兼容個版本的SQL Server。
  • 采用ROW_Number方法實現分頁難易適中,效率較高。LINQ中的SKIP和TAKE也是采用這種方式來進行分頁的,應該是目前采用的比較廣泛的分頁方式。
  • FFSET FETCH 方法是SQL Server CE 4.0?中才引入的,由于本文沒有SQL Server 2012以及測試數據,從comparing-performance-for-different-sql-server-paging-methods這篇文章來以及園子里的Sql Server 2012 分頁方法分析(offset and fetch),性能應該是比較好的。

以上是對SQLServer數據庫SQL分頁的一點總結,希望對您有所幫助。

總結

以上是生活随笔為你收集整理的浅谈SQL Server数据库分页的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产在线一区视频 | 丰满少妇久久久久久久 | 日韩不卡av | 一区二区三区日韩电影 | 欧美日韩亚洲一区二区三区 | 天天综合天天添夜夜添狠狠添 | 中文字幕亚洲图片 | 久久成人精品一区二区 | 黑人精品欧美一区二区蜜桃 | 成人a级网站 | 日日插日日操 | 蜜臀久久99精品久久久久久宅男 | 女生张开腿让男生插 | 女性裸体瑜伽无遮挡 | 男人添女人下部高潮视频 | 香蕉视频免费看 | 第一福利在线视频 | 91口爆一区二区三区在线 | 少妇又紧又深又湿又爽视频 | 欧美特级一级片 | 亚洲欧洲精品视频 | 天天有av | 一区二区免费在线观看 | 成人av电影在线播放 | 综合五月网| 在线观看国产小视频 | 亚洲激情成人 | 97精品一区二区 | 久久av影院| 久久草网站| 亚洲高清毛片 | 男女裸体影院高潮 | 久草99| 久久99国产综合精品免费 | 欧美日韩天堂 | 色片网站在线观看 | 天天综合av | 97国产精品视频人人做人人爱 | 女人被男人躁得好爽免费视频 | 日韩阿v | 色爽影院| 一区二区三区精品国产 | 亚洲丝袜一区 | 香蕉视频啪啪 | 欧美精品hd | 天天爽天天摸 | 亚洲欧洲精品一区 | 欧美性色19p | 久久露脸国语精品国产 | 人人模人人干 | 国产一区二区高清 | 在线中文字幕第一页 | 大尺度叫床戏做爰视频 | 国产综合精品一区二区三区 | 日本一区二区不卡在线观看 | 亚洲狼人天堂 | 巨乳美女动漫 | 日韩中文字幕一区二区 | 波多野结衣操 | 天天综合久久综合 | 成年人免费在线看 | 秋霞电影网一区二区 | 成人a毛片| 91看片淫黄大片91桃色 | 亚洲特黄毛片 | 日韩中文一区二区三区 | 淫五月天 | 黄色片成年人 | 欧美黄色大片网站 | 精品一区二区电影 | 国产高清视频一区 | 日韩欧美中文字幕一区 | 中文字幕制服丝袜 | av永久免费在线观看 | 特黄av| 亚洲成人黄色片 | 日韩国产亚洲欧美 | 亚洲三级免费观看 | 无码精品国产一区二区三区 | 亚洲成人精品网 | 亚洲网址 | 美女搞黄视频网站 | 最新黄色网址在线观看 | 久久精品麻豆 | 哺乳期喷奶水丰满少妇 | 中文字幕日韩精品在线观看 | 特黄特色大片免费播放器使用方法 | 欧美日韩精品一区二区三区蜜桃 | 国产一级二级三级在线观看 | 欧美日韩免费网站 | 日韩短视频 | 欧美乱轮| 久操福利在线 | 国产三级漂亮女教师 | 性一交一乱一精一晶 | 欧美日本一区二区 | 夜夜春影院| а天堂中文在线官网 | 日韩成人一区 |