csql分頁
Ⅰ C# 獲取資料庫內容分頁,求大神指點,在線等
有兩種方法:
1.使用資料庫,也就是使用sql來實現。這部分,你網上一搜一大堆。所以,這里不回答了。
2.如果你使用的framework是大於.NET3.5以上的,那麼,非常簡單,使用linq即可。
效果圖:
privateDataTabledt=newDataTable();
privateinti=0;
privatevoidForm1_Load(objectsender,EventArgse)
{
dt.Columns.Add("id");
dt.Columns.Add("name");
for(inti=0;i<10;i++)
{
dt.Rows.Add(i.ToString(),"A"+i.ToString());
}
btnNext.PerformClick();
}
privatevoidbtnNext_Click(objectsender,EventArgse)
{
varquery=fromqindt.AsEnumerable()
whereConvert.ToInt32(q["id"])>=2*i&&Convert.ToInt32(q["id"])<=2*i+1
selectq;
dataGridView1.DataSource=query.AsDataView();
i++;
}
Ⅱ 大數據量實時統計排序分頁查詢(並發數較小時)的幾點建議
大數據量實時統計排序分頁查詢的瓶頸不是函數(count,sum等)執行,
不是having, 也不是order by,甚至不是表join, 導致慢的原因就在於「數據量太大本身」
就是將表劃分為M份相互獨立的部分,可以是分表,也可以是不分表但冗餘一個取模結果欄位
實際結果是不分表比分表更加靈活,只需稍加配置,就可以動態切分大表,隨意更改M的大小。
將1條慢sql(大於30秒)拆分成為N條查詢速度巨快的sql(單條sql執行時間控制在20毫秒以內)
然後再web應用中以適當的線程數去並發查詢這些執行時間快的N條小sql再匯總結果
第一步查詢中去並發執行這N條小sql, 只取排序欄位和標識欄位,其他欄位一律丟棄
匯總結果後定位出當前頁面要顯示的pageNum條數據,再進行第二步查詢,取出頁面上需要展示的所有欄位
PS:這一點是至關重要的,其他幾點都可以不看,這點是最關鍵的。慢慢解釋一下:
有三種方式統計所有的記錄,
a) 第一種方式是把資料庫中所有記錄(只取排序欄位和標識欄位並且不做任何sum,count having order by等操作)
全部拉到web應用中,在web應用中完成所有的計算
b) 第二種方式是把資料庫中所有記錄做sum count having等操作之後的所有行數拉到web應用中,在web應用中完成剩餘計算
c) 第三種方式是把資料庫中所有記錄做sum count having order by等操作之後把limit後的數據拉到web應用中,
在web應用中對limit後的數據再計算
顯然,第一種方式 資料庫什麼活都不做只取數據 是不可行的。以lg_order_count_seller為例,1500萬行,
如果只算id, seller_id和order_count 這三個bigint類型,至少需要拉8*3*1500 0000 = 360000000=340M,
拉到內存中之後存儲需要8*4*15000000= 460M,這還不算List是的2的n次方這個特點和計算排序等的內存開銷,
不僅資料庫與web應用機器IO扛不住,就是應用自身恐怕也要OOM了。
第二種方式,所有記錄做sum count having等操作之後,由於是group by seller_id的,總得數據量變為100萬(就是賣家總數),
這樣子一來,共需要拉8*3*100 0000 = 23M,拉到內存之後,需要8*4*100 0000 = 30M, 再算上List是的2的n次方這個特點和
計算排序等的內存開銷也不會超過100M, IO的時間和內存開銷勉強可以考慮接受。
第三種方式,所有記錄做sum count having order by等操作之後把limit後的數據拉到web應用中,因為做了limit,所以,
數據量很小了,無論是IO還是內存開銷都已經很小了。可以忽略。
綜合以上三種,第三種方式適用於頁面的前n頁和後n頁,因為這個limit的數據量隨著頁數的增大而增大,
當大到每個切分後的小表的數據量時就轉為第二種方式了。
第二種方式適用於頁面的第[n+1, totaoPageNum-n]頁。
① 問題描述:
優化之前,還是是一條大慢sql查詢時,由於資料庫排序是穩定排序,
所以當兩條記錄排序欄位值相同時他們在頁面上的頁碼位置是固定的。
優化之後,當並行執行這N條小sql時,由於無法控制這些小sql的先後執行順序,
導致在web應用中當兩條記錄的排序欄位值相同時在頁面上的頁碼位置是隨機的。
② 解決辦法:
除了拉標識欄位(seller_id)和排序欄位(order_count_sum)之外,再取一個unique(id)的欄位,當兩條記錄的排序欄位值相同時,
再用這個unique的欄位(在賣家監控中這個欄位是id)進行第二次排序.這樣就解決了排序不穩定的問題。
③ 也許,看到這里會有疑問,為什麼不用seller_id?seller_id也是唯一, 這樣子不是少取id這個欄位,減少IO了?
seller_id雖然也是唯一,可以輔助排序,但是不要忘記資料庫的排序規則是:
如果兩列的值相等,那麼序號在前的排在前面,這里的序號就是主鍵(自動生成,autoincrement),
如果用seller_id的話還是不能保證排序的穩定性,只能用主鍵id.
把資料庫的連接,掃表,計算等資源優先讓給用戶關注的主要元素,次要元素可等主要元素載入完成之後再載入。
反應在賣家監控頁面中,查數據和查頁頁碼的sql語句基本相同,是在競爭同一資源,
所以,需要做一個策略,優先把資源讓給查數,數據查完之後再去查頁碼。
由於多線程取數據並沒有從本質上提高資料庫性能,所以必須針對大數據量實時統計排序分頁查詢做限流
我這里打個比方:食堂有6個窗口,物流團隊吃飯要買6個菜,平均每買1個菜需要1分鍾的時間,
如果派我一個人去一個窗口買的話需要6分鍾的時間
假如派6個人分別去6個窗口買這6個菜,只需要1分鍾的時間
但是,如果除了物流團隊,再來其他5個團隊呢,也就是說6個團隊每個團隊買6個菜共買36個菜,
這樣子有的團隊先買完,有的團隊後買完,但平均時間還是6分鍾。本質上沒有變化。
所以,對於特定的查詢條件,必須進行限流。讓每分鍾至多有6個團隊買菜,這樣子能使得情況變得不至於太糟糕。
這一點從目前來看只能是展望了,比如mysql資料庫換更為強大的oracle資料庫,
或更換InnoDb引擎為其他,或更換SATA硬碟為SSD 。。。。。。
相同的查詢條件,原來一個頁面查詢時間由於超過60秒超時了,根據1-6點建議優化之後,查詢時間變為2秒至3.5秒之間。
Ⅲ 不使用資料庫怎麼在頁面上實現分頁顯示
一.邏輯分頁
1.邏輯分頁的第一種方式,利用ResultSet的滾動分頁。步驟如下:
a.根據條件sql查詢資料庫。
b.得到ResultSet的結果集,由於ResultSet帶有游標,因此可以使用其next()方法來指向下一條記錄。
c.利用next()方法,得到分頁所需的結果集。
這種分頁方式依靠的是對結果集的演算法來分頁,因此通常被稱為「邏輯分頁」。
代碼如下:
/**
* TestPageResultSetDAO.java
*
* Copyright 2008. All Rights Reserved.
*/
package com.cosmow.pageresultset.;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.cosmow.pageresultset.entity.Bars;
/**
* TODO class TestPageResultSetDAO
*
* Revision History
*
* 2008-7-7,Cosmo,created it
*/
public class TestPageResultSetDAO {
private final String FIND_BARS_PAGE = "SELECT * FROM YYBARS ORDER BY id";
/**
* 提供JDBC連接方法,返回一個Connection的實例
*
* @return
* @throws SQLException
*/
private Connection getConnection() throws SQLException {
try {
final String url = "jdbc:oracle:thin:@localhost:1521:ORCL";
final String user = "store";
final String password = "store_password";
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection(url, user, password);
return con;
} catch (ClassNotFoundException e) {
throw new SQLException(e.getMessage());
}
}
/**
* 邏輯分頁方法一,該方法使用移位(rs.next)來進行
*
* @param currentPage
* 當前頁
* @param showRows
* 一頁顯示的數據量
*/
public List<Bars> pageListOne(int currentPage, int showRows) {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<Bars> resultList = new ArrayList<Bars>();
try {
con = getConnection();
ps = con.prepareStatement(FIND_BARS_PAGE);
rs = ps.executeQuery();
// 過濾結果集的變數
int skipBegin = (currentPage - 1) * showRows;
int skipEnd = currentPage * showRows;
// 翻頁計數器
int currentNum = 0;
// 當返回結果集中有記錄時
while (rs.next()) {
// 以下情況將保證在結果集中有記錄時的應用
if (currentNum >= skipBegin && currentNum < skipEnd) {
Bars bar = new Bars();
bar.setId(rs.getLong("id"));
bar.setName(rs.getString("name"));
bar.setType(rs.getInt("type"));
bar.setCreatorId(rs.getLong("creator_id"));
resultList.add(bar);
if (currentNum == skipEnd - 1)
break;
}
currentNum++;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (rs != null)
rs.close();
if (ps != null)
ps.close();
if (con != null)
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return resultList;
}
}
2.邏輯分頁的第二種方式
利用Scrollable ResultSets(可滾動結果集合)來快速定位到某個游標所指定的記錄行,所使用的是ResultSet的absolute()方法。
改進代碼如下:
/**
* TestPageResultSetDAO.java
*
* Copyright 2008. All Rights Reserved.
*/
package com.cosmow.pageresultset.;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.cosmow.pageresultset.entity.Bars;
/**
* TODO class TestPageResultSetDAO
*
* Revision History
*
* 2008-7-7,Cosmo,created it
*/
public class TestPageResultSetDAO {
private final String FIND_BARS_PAGE = "SELECT * FROM YYBARS ORDER BY id";
/**
* 提供JDBC連接方法,返回一個Connection的實例
*
* @return
* @throws SQLException
*/
private Connection getConnection() throws SQLException {
try {
final String url = "jdbc:oracle:thin:@localhost:1521:ORCL";
final String user = "store";
final String password = "store_password";
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection(url, user, password);
return con;
} catch (ClassNotFoundException e) {
throw new SQLException(e.getMessage());
}
}
/**
* 邏輯分頁方法二,使用absolute()方法分頁
*
* @param currentPage
* 當前頁
* @param showRows
* 一頁顯示的數據量
*/
public List<Bars> pageListTwo(int currentPage, int showRows) {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<Bars> resultList = new ArrayList<Bars>();
try {
con = getConnection();
ps = con.prepareStatement(FIND_BARS_PAGE);
rs = ps.executeQuery();
// 過濾結果集的變數
int skipBegin = (currentPage - 1) * showRows;
int skipEnd = currentPage * showRows;
// 利用rs.absolute進行定位
if (!rs.absolute(skipBegin))
return resultList;
// 當返回結果集中有記錄時
while (rs.next()) {
// 以下情況將保證在結果集中有記錄時的應用
if (skipBegin < skipEnd) {
Bars bar = new Bars();
bar.setId(rs.getLong("id"));
bar.setName(rs.getString("name"));
bar.setType(rs.getInt("type"));
bar.setCreatorId(rs.getLong("creator_id"));
resultList.add(bar);
if (skipBegin == skipEnd - 1)
break;
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (rs != null)
rs.close();
if (ps != null)
ps.close();
if (con != null)
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return resultList;
}
}
雖然和第一種方式區別不大,單效率比ResultSet滾動要好,但是absolute()方法並不是所有jdbc驅動都支持。
可用如下代碼測試當前jdbc驅動是否支持可滾動結果集:
int type = rs.getType();
if (type == ResultSet.TYPE_SCROLL_INSENSITIVE || type == ResultSet.TYPE_SCROLL_SENSITIVE)
System.out.println("Result set is scrollable");
else
System.out.println("Result set is not scrollable");
二.物理分頁
利用資料庫本身的一些特性來分頁。即:利用了資料庫對sql語法的優化,提高分頁性能。
1.針對Oracle資料庫
步驟如下:
a.根據所使用的資料庫特性來組織sql進行分頁。
b.每次跳轉頁面的sql查詢都不相同。
通用的sql分頁方式,「限制行數結果集的倒序」分頁,步驟如下:
(1).取得符合條件的所有結果集中可以唯一標識的Key值(通常是主鍵),並正向排序。
(2).利用資料庫提供的特殊方法進行「最大結果集」的限制(在Oracle中使用rownum, sql server中使用top, mysql中使用limit...),
該「最大結果集」指包含當前所處頁的所有記錄數,「最大結果集」應該只包含惟一的Key值。
(3).對步驟(2)中的「最大結果集」進行逆序,並取得「顯示當前頁顯示數量的結果集」,該結果集中只包含惟一的Key值。
(4).通過步驟(3)中所取得的Key值取得顯示數據,該顯示數據就是當前頁應該顯示的數據。
2.針對MySQL資料庫
在MySQL資料庫中offset關鍵字的意思是"越過",而limit關鍵字的意思是「限制」,利用這兩者結合可輕松分頁。
(1)取得符合條件的結果集,包含全欄位。
(2)利用offset關鍵字越過一段結果集(被越過的結果集就是"(當前頁 - 1) * 一頁顯示數")。
(3)利用limit關鍵字限製取得一段結果集(被限製取得的結果集就是一頁顯示數)
代碼如下:
/**
* TestPageResultSetDAO.java
*
* Copyright 2008. All Rights Reserved.
*/
package com.cosmow.pageresultset.;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.cosmow.pageresultset.entity.Bars;
/**
* TODO class TestPageResultSetDAO
*
* Revision History
*
* 2008-7-7,Cosmo,created it
*/
public class TestPageResultSetDAO {
private final String FIND_BARS_MYSQL = "select * from yybars order by id limit ? offset ?";
/**
* 提供JDBC連接方法,返回一個Connection的實例
*
* @return
* @throws SQLException
*/
private Connection getConnection() throws SQLException {
try {
final String url = "jdbc:oracle:thin:@localhost:1521:ORCL";
final String user = "store";
final String password = "store_password";
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection(url, user, password);
return con;
} catch (ClassNotFoundException e) {
throw new SQLException(e.getMessage());
}
}
/**
* 物理分頁方法二針對mysql,使用sql語句的limit和offset來進行分頁
*
* @param currentPage
* 當前頁
* @param showRows
* 一頁顯示的數據量
*/
public List<Bars> pageListFour(int currentPage, int showRows) {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<Bars> resultList = new ArrayList<Bars>();
try {
con = getConnection();
ps = con.prepareStatement(FIND_BARS_MYSQL);
//傳入參數,第一個參數表示顯示幾條記錄(limit關鍵字的含義)
ps.setInt(1, showRows);
//第二個參數表示丟棄幾條記錄(offset關鍵字的含義)
ps.setInt(2, showRows * (currentPage - 1));
rs = ps.executeQuery();
// 當返回結果集中有記錄時
while (rs.next()) {
Bars bar = new Bars();
bar.setId(rs.getLong("id"));
bar.setName(rs.getString("name"));
bar.setType(rs.getInt("type"));
bar.setCreatorId(rs.getLong("creator_id"));
resultList.add(bar);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (rs != null)
rs.close();
if (ps != null)
ps.close();
if (con != null)
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return resultList;
}
}
分頁結論:
1.物理分頁速度上並不一定快於邏輯分頁,邏輯分頁速度上也並不一定快於物理分頁。
2.物理分頁總是優於邏輯分頁:沒有必要將屬於資料庫端的壓力加諸到應用端來,就算速度上存在優勢,
然而其它性能上的優點足以彌補這個缺點。
3.在分頁工作前,有必要了解使用資料庫本身的一些sql語句特點更好的分頁。
分頁方法有很多,主要看哪種符合你的要求。
希望我的回答對你有幫助,若幫助到您請及時的採納
及時不採納也請贊同下,讓咱有繼續回答的動力哈