﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Nothing is impossible for a willing heart-文章分类-sqlserver and Orcale</title><link>http://www.blogjava.net/freedream/category/15856.html</link><description>I belive I can</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 04:01:00 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 04:01:00 GMT</pubDate><ttl>60</ttl><item><title>orcale数据分页提取数</title><link>http://www.blogjava.net/freedream/articles/100297.html</link><dc:creator>JAVA_UFO</dc:creator><author>JAVA_UFO</author><pubDate>Wed, 21 Feb 2007 05:50:00 GMT</pubDate><guid>http://www.blogjava.net/freedream/articles/100297.html</guid><wfw:comment>http://www.blogjava.net/freedream/comments/100297.html</wfw:comment><comments>http://www.blogjava.net/freedream/articles/100297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/freedream/comments/commentRss/100297.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/freedream/services/trackbacks/100297.html</trackback:ping><description><![CDATA[orcale数据分页提取数<br />方法一:<br /><br />select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段<br /><br />10 = 每页记录数<br /><br />20 = (当前页 + 1) * 每页记录数<br /><br />以上语句即可以实现分页，但是最后取出的结果排序是升序，如果需要结果集为降序（例如时间），则有两种方法可以处理<br /><br />1.使用以下语句，但效率可能要降低一些<br /><br />select * from 表名 b, (select top 10 主键字段,排序字段 from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a order by 排序字段 ) c where b.主键字段 = c.主键字段 order by c.排序字段 desc<br /><br />2.在ado里处理,将记录集游标移到最后，然后前移<br /><br />''以下为asp范例<br /><br />set rsTemp = Server.CreateObject("adodb.recordset")<br /><br />rsTemp.Open 语句,conn,1,1<br /><br />rsTemp.MoveLast<br /><br />for i = 1 to rsTemp.RecordCount<br /><br />     '取值....<br /><br />    rsTemp.MovePrevious<br /><br />next<br /><br /> <br /><br />经测试，以上分页方法比使用临时表分页速度还要快，并且简单易用.<br /><br />方法二：<br /><br />大数据量下的分页<br /><br />    对于非常大的数据模型而言，分页检索时，每次都加载整个数据源非常浪费。通常的选择是检索页面大小的块区的数据，而非检索所有的数据，然后单步执行当前行。<br /><br />    本文演示ASP.net的DataGrid和Sql Server 实现大数据量下的分页，为了便于实现演示，数据表采用了Northwind数据库的Orders表（830条记录）。<br /><br />    如果数据表中有唯一的自增索引，并且这个字段没有出现断号现象。检索页面大小的块区数据就非常简单了。通过简单的Sql语句就可以实现这个功能：<br /><br />    select * from orders where orderid between 10248 and 10253<br /><br />    其中，开始编号为：(CurrentPageIndex - 1) * PageSize  结束编号为：CurrentPageIndex * PageSize<br /><br />    当然，如果这个字段断号不是很严重，而且允许不是很严格的按照每页条数分页，这样的方法也是可以用的。<br /><br />    如果这个字段断号，或者需要按照其他条件排序分页，就要复杂些了。首先要获得这个页面需要显示的编号，然后再按照这个编号获得需要的块区数据。根据编号获得块区数据很简单。不过用下面方式获得数据排序并不是按照指定的id列表顺序，这时候还要附加order by 命令。<br /><br />select * from orders where orderid in (10248,10249,10250,10251,10252,10253) order by orderid desc<br /><br />    获得这个页面需要显示的编号列表就复杂多了，而且有多种方案：<br /><br />方案一：维护一个表，这个表记录需要显示的这些编号排序顺序。（这个表可以是临时表，也可以是物理表）。下面演示了利用一个全局临时表。这个全局临时表记录需要显示的编号。注意排序，这里的order by 就是需要显示的排序顺序。<br /><br />create table ##temptable(iid int IDENTITY (1, 1) NOT NULL,mainid int NOT NULL)insert ##temptable(mainid) select OrderID from orders order by OrderID descselect * from ##temptabledrop table ##temptable -- 实际执行时候，删除全部临时表当然不再这里执行。<br /><br />这个临时表存在，获得指定分页的分块数据就很简单了。看下面代码：<br /><br />create table ##temptable(iid int IDENTITY (1, 1) NOT NULL,mainid int NOT NULL)insert ##temptable(mainid) select OrderID from orders order by OrderID descdeclare @PageSize int,@CurrPage int,@strSQL varchar(2000),@IDStr varchar(1000)select @PageSize = 30select @CurrPage = 2select @IDStr = ''select @IDStr = @IDStr + ltrim(rtrim(str(MainID))) + ',' from ##temptable where iid between ((@CurrPage-1)*@PageSize+1) and @CurrPage*@PageSizeif @IDStr &lt;&gt; '' begin select @IDStr = left(@IDStr,len(@IDStr)-1)endselect @strSQL = 'select * from orders where OrderID in ('+@IDStr+')  order by OrderID desc 'exec(@strSQL)drop table ##temptable<br /><br />注意：实际使用这个方案的时候，还要考虑何时更新这个全局临时表，一般是放到计划任务中，定时更新这个汇总表。<br /><br />方案二：每次都去查询，每次获得最新的编号顺序。由于这时候不存在这个临时表，书写获得需要显示页面的编号的字符串就需要点技巧，看下面的代码： ASP.net 的 DataGrid 提供了使用这种分区的数据的方法。 DataGrid 通过 AllowCustomPaging 和 VirtualItemCount 属性支持块区操作。如果 AllowCustomPaging 为 true，则 DataGrid 不会根据 CurrentPageIndex 计算数据模型中的起始显示位置。DataGrid 将显示数据模型中的所有数据，而页导航栏将当前位置报告为 (VirtualItemCount+PageSize-1)/PageSize 之 CurrentPageIndex 页。下面的示例说明此功能。<br /><br />declare @PageSize int,@CurrPage int,@topnum int,@previous intselect @PageSize = 30select @CurrPage = 2select @topnum = @CurrPage * @PageSizeselect @previous = (@CurrPage - 1) * @PageSizedeclare @i int,@IDStr nvarchar(500),@strSQL nvarchar(1000)select @i = 0select @strSQL = N''select @strSQL = @strSQL + N' select top '+str(@topnum)+ ' @i = @i + 1 'select @strSQL = @strSQL + N',  @IdStr = 'select @strSQL = @strSQL + N'case when @i &gt; '+str(@previous)+' then  @IdStr + ltrim(rtrim(str(OrderID))) + '','' 'select @strSQL = @strSQL + N'else N''''end 'select @strSQL = @strSQL + N'from Orders 'select @strSQL = ltrim(rtrim(@strSQL)) + N' order by OrderID desc 'Select @IdStr = N''exec sp_executesql @strSQL,N'@i int,@IdStr varchar(500) output',@i,@IdStr outputif len(rtrim(ltrim(@IdStr))) &gt; 0begin select @IdStr = left(@IdStr,len(@IdStr)-1)endselect @strSQL = 'select * from orders where OrderID in ('+@IDStr+')'exec(@strSQL)<br /><br /> <br /><br />     protected void BindDataGrid(int currpage) {  string strConn = "Data Source=(local);Integrated Security=SSPI;database=Northwind";  // 请确认 机器名/ASPNET 用户可以访问Northwind数据库  SqlCommand cmd = new SqlCommand();  SqlConnection conn = new SqlConnection(strConn);  SqlParameter[]  parms = new SqlParameter[] {   new SqlParameter("@PageSize",SqlDbType.Int),   new SqlParameter("@CurrPage",SqlDbType.Int),   new SqlParameter("@SearchSql",SqlDbType.NVarChar,128),   new SqlParameter("@Count",SqlDbType.Int),  };  parms[0].Value = DataGrid1.PageSize;  parms[1].Value = (currpage+1);   //  数据库的分页算法第一页是1  DataGrid的第一页是0  parms[2].Value = DBNull.Value;  parms[3].Direction = ParameterDirection.Output;  parms[3].Value = DBNull.Value;  DataSet DS = new DataSet();  try   {   if (conn.State != ConnectionState.Open) conn.Open();   cmd.Connection = conn;   cmd.CommandText = "Selected_Page_List";   cmd.CommandType = CommandType.StoredProcedure;   if (parms != null)    {    foreach (SqlParameter parm in parms)     cmd.Parameters.Add(parm);   }   SqlDataAdapter DA = new SqlDataAdapter(cmd);   DA.Fill(DS);   int aa = Convert.ToInt32(parms[3].Value.ToString());   cmd.Parameters.Clear();   if (currpage == 0)   {    DataGrid1.VirtualItemCount = aa;   }   DataGrid1.CurrentPageIndex = currpage;   DataGrid1.DataSource = DS;   DataGrid1.DataBind();  }  catch(Exception ewx)  {   conn.Close();   Response.Write (ewx.Message.ToString());   Response.End();  } }    void Page_Load(Object sender, EventArgs E ) {  if (!IsPostBack)   {   BindDataGrid(0);   // 第一次打开这个页面，访问分页的第一页  }    }    void MyDataGrid_Page(Object sender, DataGridPageChangedEventArgs e) {  BindDataGrid(e.NewPageIndex);    }<br /><br />如果你有更多数据量的表稍加修改，也可以使用本演示程序。其下是演示代码下载，演示代码使用的是方案二。使用方法看readme.txt文件。<br /><br />整个演示代码 下载<br /><br />http://chs.gotdotnet.com/quickstart/aspplus/samples/webforms/ctrlref/webctrl/datagrid/doc_datagrid.aspx#paging<br />这里演示了利用DataGrid 的这个功能(没有本文中讨论的利用存储过程获得分区数据)。如对DataGrid的这个功能不太熟悉，请先看这里。<br /><br /> <br /><br />方法三:<br /><br />虽然 DataGrid 控件自己带了一个分页处理机制，但它是将符合查询条件的所有记录读入内存，然后进行分页显示的。随着符合条件的记录数目增多，就会出现运行效率问题，或者至少是资源的利用率下降。<br /><br />下面的代码示例都以下面的表结构为准：<br /><br />　<br />　     Articles 表     SQL Server 类型     Oracle 类型<br />PK     Id     int (自增)     number(9) (插入时在当前最大值上加1)<br />　     Author     nvarchar(10)     nvarchar2(10)<br />　     Title     nvarchar(50)     nvarchar2(50)<br />　     PubTime     datetime     date<br /><br />SQL Server / Access 等微软产品中，我们通常的自定义分页有两种思路：<br /><br />一种是以 ASP.NET Forum 为代表的、“临时表”方法：即在存储过程中建立一个临时表，该临时表包含一个序号字段（1，2，3，....）以及表的主键（其他能够唯一确定一行记录的字段也是可以的）字段。存储过程可能如下：（编号 SS1）<br />CREATE Procedure GetAllArticles_Paged<br />(<br />     @PageIndex int,<br />     @PageSize int,<br />     @TotalRecords out int,<br />     @TotalPages out int<br />)<br />AS<br /><br />DECLARE @PageLowerBound int<br />DECLARE @PageUpperBound int<br /><br />-- Set the page bounds<br />SET @PageLowerBound = @PageSize * @PageIndex<br />SET @PageUpperBound = @PageLowerBound + @PageSize + 1<br /><br />-- Create a temp table to store the select results<br />CREATE TABLE #tmp<br />(<br />     RecNo int IDENTITY (1, 1) NOT NULL,<br />     ArticleID int<br />)<br /><br />INSERT INTO #tmp<br />     SELECT [ID]<br />     FROM Articles<br />     ORDER BY PubTime DESC<br /><br />SELECT A.*<br />FROM Articles A (nolock), #tmp T<br />WHERE A.ID = T.ArticleID AND<br />     T.RecNo &gt; @PageLowerBound AND<br />     T.RecNo &lt; @PageUpperBound<br />ORDER BY T.RecNo<br /><br />GO<br /><br />另一种可能更适合程序中“拼凑” SQL 语句：用两次 TOP 命令取得我们所要的分页数据，例如：（编号 SS2）<br />SELECT * FROM<br />     (<br />     SELECT TOP(PageSize) * FROM<br />     (<br />          SELECT TOP (PageSize * PageIndex) *<br />          FROM Articles<br />          ORDER BY PubTime DESC<br />     )<br />     ORDER BY PubTime ASC<br />)<br />ORDER BY PubTime DESC<br /><br />这个的想法就是“掐头去尾”，还有不少分页的方法，这里就不一一列出了。<br /><br />对于 Oracle 数据库，有几处不同严重妨碍了上面几个方法的实施，比如，Oracle 不支持 TOP 关键字：不过这个好像并不十分严重，因为它提供了 rownum 这个隐式游标，可以实现与 TOP 类似的功能，如：<br />SELECT TOP 10 ... FROM WHERE ...<br /><br />要写成<br />SELECT ... FROM ... WHERE ... AND rownum &lt;= 10<br /><br />rownum 是记录序号（1，2，3...），但有一个比较麻烦的事情是：如果 SQL 语句中有 ORDER BY ... 排序的时候，rownum 居然是先“标号”后排序！这样，这个序号如果不加处理是不合乎使用需求的。<br /><br />至于临时表，Oracle 的临时表和 SQL Server 的有很大不同，我还没搞懂这个东西，就不妄加揣测了。<br /><br />国内网站中介绍 Oracle 分页的资料很少，我找到了一个国外站点（www.faqts.com）的一篇 FAQ，根据这篇文章的介绍，可以如下分页：（编号 Ora1）<br />SELECT * FROM<br />     (<br />     SELECT A.*, rownum r<br />     FROM<br />          (<br />          SELECT *<br />          FROM Articles<br />          ORDER BY PubTime DESC<br />          ) A<br />     WHERE rownum &lt;= PageUpperBound<br />     ) B<br />WHERE r &gt; PageLowerBound;<br /><br />其中蓝色部分可以改为任意的、需要的 SQL SELECT 语句，这点倒是挺方便的。<br /><br />方法四:<br /><br />今天突然发现，Oracle原来可以这样实现分页功能：<br /><br />select * from (select rownum rdd,field1,field2 from t_table where rownum&lt;=400) where  rdd&gt;200<br /><br />上述语句实现了从第201条记录开始处取200条记录<img src ="http://www.blogjava.net/freedream/aggbug/100297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/freedream/" target="_blank">JAVA_UFO</a> 2007-02-21 13:50 <a href="http://www.blogjava.net/freedream/articles/100297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>