好久沒有用數據庫了。最近在做項目時突然發現6年前自己的分頁代碼有問題的。
這個問題客戶居然從來沒反饋過。原來是通過主鍵來分頁的,但排序條件加入後其實不能保證主鍵隊列也是排序的,因此 ... WHERE ZID > MAX(...) ... 的做法不對的。
現在改進,使用 NOT IN 的方法。思路是:比如顯示第3頁,每頁10條記錄,那麼取出30條記錄,但是過濾掉前20條,這樣排序是解決了,但效率應該降低了。
貼代碼:
CREATE PROCEDURE sp_GetPageData
@sSelect NVARCHAR(100), --select 子句
@sFrom NVARCHAR(200), --from 子句
@sWhere NVARCHAR(200), --where 子句
@sOrder NVARCHAR(50), --order by 子句
@iCurPageNo int, --當前頁碼
@iPageSize int, -- 分頁大小
@iRecordCount int out --輸出總數據條目數量
AS
DECLARE @iPageCount int --分頁數量
DECLARE @SQLString NVARCHAR(800) --總SQL
DECLARE @ParmDefinition NVARCHAR(50) --參數定義
DECLARE @sWhereIn NVARCHAR(200) --NOT IN 子句
IF @iPageSize < 1
SET @iPageSize = 10
--計算記錄數目
SET @SQLString = N'SELECT @iRecordCount = COUNT(*) FROM ' + @sFrom + N' WHERE' + @sWhere
--+ ' ORDER BY ' + @sOrder
SET @ParmDefinition = N'@iRecordCount int OUT'
print @SQLString
EXECUTE sp_executesql @SQLString,@ParmDefinition,@iRecordCount OUT
print N'@iRecordCount = ' + CAST(@iRecordCount AS varchar)
if (0 = @iRecordCount)
begin
print 'data is null'
return
end
--計算分頁
if (@iRecordCount % @iPageSize > 0)
SET @iPageCount = @iRecordCount / @iPageSize + 1
ELSE
SET @iPageCount = @iRecordCount / @iPageSize
--保證請求頁在頁碼範圍內
if (@iCurPageNo > @iPageCount)
SET @iCurPageNo = @iPageCount
--返回數據
if (@iCurPageNo > 1)
BEGIN
SET @sWhereIn = N' AND m.ZID NOT IN(SELECT TOP ' + CAST(@iPageSize * (@iCurPageNo - 1) AS VARCHAR) + N' m.ZID FROM ' + @sFrom + N' WHERE' + @sWhere + ' ORDER BY ' + @sOrder + ')'
SET @SQLString = N'SELECT TOP ' + CAST(@iPageSize AS VARCHAR) + @sSelect + N' FROM ' + @sFrom + N' WHERE' + @sWhere + @sWhereIn + ' ORDER BY ' + @sOrder
END
else -- 第一頁
SET @SQLString = N'SELECT TOP ' + CAST(@iPageSize AS VARCHAR(8)) + @sSelect + N' FROM ' + @sFrom + N' WHERE' + @sWhere + ' ORDER BY ' + @sOrder
print @SQLString
exec (@SQLString)
select RecordCount = @iRecordCount
go
--測試輸入
declare @iRecordCount int
exec sp_GetPageData
N' m.*, g.ZNAME GroupName ',
N'TB_USER m LEFT JOIN TB_GROUP g ON m.ZGROUP_ID = g.ZID ',
N' 1=1',
N' m.ZID DESC',
10,
1,
@iRecordCount out
print 'iRecordCount = ' + CAST(@iRecordCount AS VARCHAR)