人生苦短,必須性感。看看桃花,再聊聊技術


嘿嘿,我又開始寫博客了。

其實也就是隨便寫寫咯。2013剛剛開始,有運氣好的時候,也有運氣壞的時候。之所以說運氣好是因為過年和朋友打麻將,基本上就看到我一個人胡咯,各種好牌,自摸的都不好意思了。其實我技術真的不行的,但似乎像我這種愣頭青一般都是無心插柳成蔭喔。然后運氣壞的時候也是霉的不行。上上周和群里的朋友騎車翻越龍泉到櫻桃溝看櫻花的時候,不慎沖出了下坡變道,請假在家療養了一周嘛。記得當時整個人就癱在那兒了,大口大口的喘氣,感覺到臉上在不斷地往外面淌血,車車也不曉得摔得哪兒去了。還好在后面的車友跟了上來,幾個妹子細心地蘸着開水擦我臉上的血,甚至還有人看我呼吸不過來的樣子說要給我做人工呼吸。也許是這句嚇到我了,我兩扭兩扭竟然站起來了!甩甩手啊腳的,發現沒有什么問題,兜兜里面的手機也還是好的,身上沾了好多草灰,臉上雖然有幾個口子但也不是很疼。然后,有騎友說叫車直接拉我回家,我一看我的車還是好的,就堅持着繼續上路咯。后來在龍泉大伙還一起吃了個飯,回家都晚上11點了。一照鏡子,其實也沒有想像中的那么嚴重嘛。

現在想起來,其實摔車的那次運氣也不算太壞的。摔出去的那個坡也不是很陡。同行的都是朝氣蓬勃,熱心互助的車友。最嚴重的傷口也在太陽穴和眼睛之間。也沒有斷手斷腳之類的。車車也沒有摔壞。看來是我摔的有技巧喲。話說龍泉的桃花開了,這周再騎過去轉轉。

再說說工作上的事吧。聽說這周星期天要加班,但我感覺沒什么事情啊。然后中午問同事加班做什么,同事說加班拿加班工資噻,我暈哦。今天就是改了網管系統的一些BUG。而且發現有一些數據量比較大的頁面點分頁的時候響應那個慢啊!奇怪的是客戶都沒有反映么。於是我就到代碼庫里面去找啊找。找到了下面的分頁方法:

View Code
  1         /// <summary>
  2         /// 運行分頁查詢
  3         /// </summary>
  4         /// <param name="tableName">查詢數據表或視圖名</param>
  5         /// <param name="indexName">索引字段名</param>
  6         /// <param name="indexDbType">索引字段類型</param>
  7         /// <param name="columns">返回字段列表</param>
  8         /// <param name="selection">查詢條件</param>
  9         /// <param name="orderBy">排序方式</param>
 10         /// <param name="pageIndex">頁面開始索引</param>
 11         /// <param name="pageSize">分頁大小</param>
 12         /// <param name="buildObj">IDataReader 處理委托</param>
 13         /// <returns></returns>
 14         public RecordSet<T> ExecutePaging<T>(string tableName, string indexName, SqlDbType indexDbType, string[] columns, string selection, string orderBy,
 15             int pageIndex, int pageSize, DataReaderParserHandler<T> buildObj)
 16         {
 17             if (pageIndex < 0)
 18             {
 19                 pageIndex = 0;
 20             }
 21 
 22             if (pageSize == 0)
 23             {
 24                 return new RecordSet<T>();
 25             }
 26 
 27             StringBuilder commandText = new StringBuilder();
 28             commandText.AppendFormat(@"
 29 DECLARE @PageLowerBound int
 30 DECLARE @PageUpperBound int
 31 
 32 SET @PageLowerBound = @PageSize * @PageIndex
 33 SET @PageUpperBound = @PageLowerBound + @PageSize + 1
 34 
 35 CREATE TABLE #PageIndexForUsers 
 36 (
 37     IndexID int IDENTITY (1, 1) NOT NULL,
 38     {0} {1}
 39 )", indexName, indexDbType == SqlDbType.Int ? "int" : "bigint");
 40 
 41             commandText.AppendFormat(@"
 42 
 43 INSERT INTO 
 44     #PageIndexForUsers ({1})
 45 SELECT
 46     [{1}]
 47 FROM
 48     {0}", tableName, indexName);
 49 
 50             if (!string.IsNullOrEmpty(selection))
 51             {
 52                 commandText.AppendFormat(@"
 53 WHERE
 54     {0}", selection);
 55             }
 56 
 57             if (!string.IsNullOrEmpty(orderBy))
 58             {
 59                 commandText.AppendFormat(@"
 60 ORDER BY
 61     {0}", orderBy);
 62             }
 63 
 64             commandText.AppendFormat(@"
 65 
 66 SELECT
 67     {0}
 68 FROM
 69     [{1}]
 70 WHERE 
 71     [{2}] IN
 72         (
 73             SELECT
 74                 [{2}]
 75             FROM
 76                 #PageIndexForUsers 
 77             WHERE
 78                 [IndexID] > @PageLowerBound AND [IndexID] < @PageUpperBound
 79         )
 80 ", columns.Length == 0 ? "*" : string.Join(",", columns), tableName, indexName);
 81             commandText.AppendFormat(@"
 82 
 83 SELECT count(*) FROM #PageIndexForUsers
 84 
 85 DROP TABLE #PageIndexForUsers ");
 86 
 87             SqlCommand cmd = new SqlCommand();
 88             cmd.CommandText = commandText.ToString();
 89             cmd.Parameters.Add("@PageIndex", SqlDbType.Int).Value = pageIndex;
 90             cmd.Parameters.Add("@PageSize", SqlDbType.Int).Value = pageSize;
 91 
 92             RecordSet<T> sets = new RecordSet<T>();
 93             sets.PageIndex = pageIndex;
 94             sets.PageSize = pageSize;
 95 
 96             using (IDataReader dr = this.ExecuteReader(cmd))
 97             {
 98                 while (dr.Read())
 99                 {
100                     sets.Add(buildObj(dr));
101                 }
102 
103                 if (dr.NextResult()
104                     && dr.Read())
105                 {
106                     sets.TotalRecords = (int)dr[0];
107                 }
108                 dr.Close();
109             }
110 
111             return sets;
112         }

上面的代碼就是利用臨時表進行分頁咯。事實證明,這個在大數量分頁的時候似乎效率並不是很高啊。
話說SqlServer不是在很久之前就引進了“表變量”嘛,分頁的話效率肯定比臨時表高不少。而且,也不知道為什么當初沒有寫一個通用的分頁存儲過程。吶,大牛的心思你真的不懂。

其實很多朋友,包含之前我在做一些東西需要用到分頁的時候,都是利用下面的SQL哦:

SELECT TOP PageSize *
FROM TableName
WHERE ID NOT IN
        (SELECT TOP PageSize*(PageIndex-1) id  FROM TableName ORDER BY id)
ORDER BY ID

原理一看便知。Top語句的效率很高,通常情況下ID也是一個表的聚簇索引。但 not in 的效率就不敢恭維咯,即使換作 not exists 語句,也不會有太大的改觀吧。

記得很久以前看到一篇.NET的面試題集,有一道寫Sql的,題干大概是這樣的:

“在SQLServer數據庫,取出一個表A中第21到第30條記錄,其中,ID為自動增長的主鍵, 但可能不是連續的”。

有朋友就給出了兩種解法:

解1:select top 10 * from A where id not in (select top 20 id from A) 
解2: select top 10 * from A where id > (select max(id) from (select top 20 id from A )as T)

 

解1不必多說,解2利用max函數提取出id的最大值,以此為參照物進行分頁,同時去掉了 not in 語句。思路當然比第一種高明咯。

解2的分頁原型如下:

select top PageSize * from TableName
where id>
      (select max (id) from
        (select top PageSize*(PageIndex-1) id from TableName order by id) as T
       )    
  order by id

改造成存儲過程基本就是下面這個樣子:

CREATE PROCEDURE P_DataPagination
                @tblName      VARCHAR(255),        -- 查詢的表名
                @returnFields VARCHAR(1000) = '*', -- 需要返回的數據列
                @sortField      VARCHAR(255) = '', -- 排序的字段名
                @sortType    BIT = 0,              -- 設置排序類型, 非 0 值則降序
                @pageSize     INT = 10,            -- 頁尺寸
                @pageIndex    INT = 1,             -- 頁碼
                @totalCount      BIT = 0,          -- 返回記錄總數, 非 0 值則返回
                @strWhere     VARCHAR(1500) = ''   -- 查詢條件 (注意: 不要加 where)                                           
AS
  DECLARE  @strSQL VARCHAR(5000)  -- 主語句
  DECLARE  @strTmp VARCHAR(110)   -- 臨時變量
  DECLARE  @strOrder VARCHAR(400) -- 排序語句
  IF @totalCount != 0             --如果@totalCount傳遞過來的不是0,就執行總數統計。
    BEGIN
      SET @strSQL = 'select count(*) as TotalCount from [' + @tblName + ']'
      IF @strWhere != ''
      begin
        SET @strSQL = @strSQL + ' where ' + @strWhere
      end
    END
    
  ELSE
    BEGIN
      IF @sortType != 0
        BEGIN
          SET @strTmp = '<(select min'
          SET @strOrder = ' order by [' + @sortField + '] desc'
        END
       ELSE
        BEGIN
          SET @strTmp = '>(select max'
          SET @strOrder = ' order by [' + @sortField + '] asc'
        END
      IF @pageIndex = 1
        BEGIN
          Set @strSql = 'select top ' + Str(@pageSize) + ' ' + @returnFields + '  from [' + @tblName + '] '
          IF @strWhere != ''
          begin
            SET @strSQL = @strSql + ' where ' + @strWhere 
          end
          SET @strSql = @strSql + ' ' + @strOrder        
        END
       ELSE
        BEGIN
          SET @strSQL = 'select top ' + Str(@pageSize) + ' ' + @returnFields + '  from [' + @tblName + '] where [' + @sortField + ']' + @strTmp + '([' + @sortField + ']) from (select top ' + Str((@pageIndex - 1) * @pageSize) + ' [' + @sortField + '] from [' + @tblName + ']' + @strOrder + ') as tblTmp)' + @strOrder
          IF @strWhere != ''
          begin
            SET @strSQL = 'select top ' + Str(@pageSize) + ' ' + @returnFields + '  from [' + @tblName + '] where [' + @sortField + ']' + @strTmp + '([' + @sortField + ']) from (select top ' + Str((@pageIndex - 1) * @pageSize) + ' [' + @sortField + '] from [' + @tblName + '] where ' + @strWhere + ' ' + @strOrder + ') as tblTmp) and ' + @strWhere + ' ' + @strOrder
          end
        END
    END
  EXEC(@strSQL)
GO

經過我的測試,上面這個分頁存儲過程性能確實比較穩定哦,而且在分頁數據量明顯增大的情況下,也有非常高效的表現。

下面是我的測試代碼:

declare @startTime datetime ;
set @startTime=getdate();

exec P_DataPagination 
    @totalCount = 0,
    @tblName = 'targetsPort',
    @returnFields = 'id,portDesc,createTime,IfIndex,ifSpeed,IfName',
    @sortField = 'id',
    @pageSize = 2000,
    @pageIndex = 2,
    @sortType=0,
    
    @strWhere = ' portDesc like ''1%'' '
    
select [耗費時間(ms)]=datediff(ms,@startTime,getdate()) 

基本上也該收尾咯。雖然說咱家的防御值比較高,但路過的各位大神吐槽不要太猛烈哈。└(^o^)┘


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM