您好,登錄后才能下訂單哦!
這篇文章主要介紹“sqlserver2005的分頁優化”,在日常操作中,相信很多人在sqlserver2005的分頁優化問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”sqlserver2005的分頁優化”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
一、hibernate分頁 hibernate對MsSql的偽分頁
分頁是web項目中比不可少的一個功能,數據量大的時候不能全部展示必然要用到分頁技術。相信大家對hibernate中的分頁都不陌生:
Java代碼
public Query setMaxResults(int maxResults);
public Query setFirstResult(int firstResult);
Java代碼
public Query setMaxResults(int maxResults);
public Query setFirstResult(int firstResult);
只要調用了這兩個方法并設置好參數,hibernate自動分頁完全屏蔽了底層分頁技術,這也是眾多開發者喜歡hibernate的原因之一。
項目開發中遇到一個奇怪的問題。數據庫采用的是Sql Server 2005,也設置了上面兩個參數,可是每次發送到數據庫端的SQL語句都是select top ....語句。即便是查詢第10w條,也只有一個select top 語句,不免引起對hibernate實現sql server分頁的懷疑。hibernate針對不同數據庫實現的分頁方法封裝在對應數據庫的方言里,通過getLimitString方法轉化成對應數據庫的分頁算法。
以常見的Mysql數據庫的方言MySQLDialect為例:
Java代碼
public String getLimitString(String sql, boolean hasOffset) {
return new StringBuffer( sql.length() + 20 )
.append( sql )
.append( hasOffset ? " limit ?, ?" : " limit ?" )
.toString();
}
Java代碼
public String getLimitString(String sql, boolean hasOffset) {
return new StringBuffer( sql.length() + 20 )
.append( sql )
.append( hasOffset ? " limit ?, ?" : " limit ?" )
.toString();
}
采用了大家熟悉的的limit進行分頁。
數據庫的方言Oracle9iDialect:
Java代碼
StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
if (hasOffset) {
pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
}
else {
pagingSelect.append("select * from ( ");
}
pagingSelect.append(sql);
if (hasOffset) {
pagingSelect.append(" ) row_ where rownum <= ?) where rownum_ > ?");
}
else {
pagingSelect.append(" ) where rownum <= ?");
}
Java代碼
StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
if (hasOffset) {
pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
}
else {
pagingSelect.append("select * from ( ");
}
pagingSelect.append(sql);
if (hasOffset) {
pagingSelect.append(" ) row_ where rownum <= ?) where rownum_ > ?");
}
else {
pagingSelect.append(" ) where rownum <= ?");
}
利用Oracle的rownum 結合三層嵌套查詢完成分頁。這個三層是Oracle最經典高效的分頁算法。
可是針對Sql Server的方言SQLServerDialect:
Java代碼
public String getLimitString(String querySelect, int offset, int limit) {
if ( offset > 0 ) {
throw new UnsupportedOperationException( "query result offset is not supported" );
}
return new StringBuffer( querySelect.length() + 8 )
.append( querySelect )
.insert( getAfterSelectInsertPoint( querySelect ), " top " + limit )
.toString();
}
Java代碼
public String getLimitString(String querySelect, int offset, int limit) {
if ( offset > 0 ) {
throw new UnsupportedOperationException( "query result offset is not supported" );
}
return new StringBuffer( querySelect.length() + 8 )
.append( querySelect )
.insert( getAfterSelectInsertPoint( querySelect ), " top " + limit )
.toString();
}
揉揉眼睛、再揉揉,沒錯,只出現了一個top語句。這就意味著如果查詢第10w頁的數據,需要把前10w頁數據全部提取出來。hibernate針對sql server的分頁是偽分頁,所以隨著數據量日益增加用戶抱怨系統速度慢,程序員抱怨hibernate性能低,dba抱怨開發人員sql功底太淺。
不知道hibernate開發組,出于什么目前或情況沒有真正提供sql server的分頁技術,那我們自己來實現。
方言類:
Java代碼
public class SQLServer2005Dialect extends SQLServerDialect {
/**
*
* 是否需要綁定limit參數?
*
* 在SQL Server中使用top時不能使用參數表示top條數,而使用ROW_NUMBER()則需要提供limit參數
*/
private ThreadLocal<Boolean> supportsVariableLimit = new ThreadLocal<Boolean>();
public SQLServer2005Dialect() {
registerFunction("bitand", new BitAndFunction());
registerFunction("bitxor", new BitXorFunction());
registerFunction("bitor", new BitOrFunction());
setSupportsVariableLimit(false);
}
/**
*
* <p>
* 設置是否先綁定limit參數。
* </p>
*
* @param first
*/
private void setSupportsVariableLimit(boolean first) {
this.supportsVariableLimit.set(Boolean.valueOf(first));
}
/**
*
* <p>
* 獲取sql中select子句位置。
* </p>
*
* @param sql
*
* @return int
*/
protected static int getSqlAfterSelectInsertPoint(String sql) {
int selectIndex = sql.toLowerCase().indexOf("select");
int selectDistinctIndex = sql.toLowerCase().indexOf("select distinct");
return selectIndex + ((selectDistinctIndex == selectIndex) ? 15 : 6);
}
public boolean supportsLimitOffset() {
return true;
}
/*
* Hibernate在獲得Limit String(已添加了limit子句)后,如果此方法返回true,
*
* 則會添加額外的參數值(ROW_NUMBER()范圍)(策略可能是這樣:有offset設置兩個參數值,沒有設置一個參數值)
*/
public boolean supportsVariableLimit() {
return ((Boolean) this.supportsVariableLimit.get()).booleanValue();
}
public boolean useMaxForLimit() {
return true;
}
/**
* 首頁top,以后用ROW_NUMBER
*/
public String getLimitString(String query, int offset, int limit) {
setSupportsVariableLimit(offset > 0);
if (offset == 0) {
return new StringBuffer(query.length() + 8).append(query).insert(
getSqlAfterSelectInsertPoint(query), " top " + limit)
.toString();
}
return getLimitString(query, offset > 0);
}
public String getLimitString(String sql, boolean hasOffset) {
int orderByIndex = sql.toLowerCase().lastIndexOf("order by");
if (orderByIndex <= 0) {
throw new UnsupportedOperationException(
"must specify 'order by' statement to support limit operation with offset in sql server 2005");
}
String sqlOrderBy = sql.substring(orderByIndex + 8);
String sqlRemoveOrderBy = sql.substring(0, orderByIndex);
int insertPoint = getSqlAfterSelectInsertPoint(sql);
return new StringBuffer(sql.length() + 100)
.append("with tempPagination as(")
.append(sqlRemoveOrderBy)
.insert(
insertPoint + 23,
" ROW_NUMBER() OVER(ORDER BY " + sqlOrderBy
+ ") as RowNumber,")
.append(
") select * from tempPagination where RowNumber>? and RowNumber<=?")
.toString();
}
}
Java代碼
public class SQLServer2005Dialect extends SQLServerDialect {
/**
*
* 是否需要綁定limit參數?
*
* 在SQL Server中使用top時不能使用參數表示top條數,而使用ROW_NUMBER()則需要提供limit參數
*/
private ThreadLocal<Boolean> supportsVariableLimit = new ThreadLocal<Boolean>();
public SQLServer2005Dialect() {
registerFunction("bitand", new BitAndFunction());
registerFunction("bitxor", new BitXorFunction());
registerFunction("bitor", new BitOrFunction());
setSupportsVariableLimit(false);
}
/**
*
* <p>
* 設置是否先綁定limit參數。
* </p>
*
* @param first
*/
private void setSupportsVariableLimit(boolean first) {
this.supportsVariableLimit.set(Boolean.valueOf(first));
}
/**
*
* <p>
* 獲取sql中select子句位置。
* </p>
*
* @param sql
*
* @return int
*/
protected static int getSqlAfterSelectInsertPoint(String sql) {
int selectIndex = sql.toLowerCase().indexOf("select");
int selectDistinctIndex = sql.toLowerCase().indexOf("select distinct");
return selectIndex + ((selectDistinctIndex == selectIndex) ? 15 : 6);
}
public boolean supportsLimitOffset() {
return true;
}
/*
* Hibernate在獲得Limit String(已添加了limit子句)后,如果此方法返回true,
*
* 則會添加額外的參數值(ROW_NUMBER()范圍)(策略可能是這樣:有offset設置兩個參數值,沒有設置一個參數值)
*/
public boolean supportsVariableLimit() {
return ((Boolean) this.supportsVariableLimit.get()).booleanValue();
}
public boolean useMaxForLimit() {
return true;
}
/**
* 首頁top,以后用ROW_NUMBER
*/
public String getLimitString(String query, int offset, int limit) {
setSupportsVariableLimit(offset > 0);
if (offset == 0) {
return new StringBuffer(query.length() + 8).append(query).insert(
getSqlAfterSelectInsertPoint(query), " top " + limit)
.toString();
}
return getLimitString(query, offset > 0);
}
public String getLimitString(String sql, boolean hasOffset) {
int orderByIndex = sql.toLowerCase().lastIndexOf("order by");
if (orderByIndex <= 0) {
throw new UnsupportedOperationException(
"must specify 'order by' statement to support limit operation with offset in sql server 2005");
}
String sqlOrderBy = sql.substring(orderByIndex + 8);
String sqlRemoveOrderBy = sql.substring(0, orderByIndex);
int insertPoint = getSqlAfterSelectInsertPoint(sql);
return new StringBuffer(sql.length() + 100)
.append("with tempPagination as(")
.append(sqlRemoveOrderBy)
.insert(
insertPoint + 23,
" ROW_NUMBER() OVER(ORDER BY " + sqlOrderBy
+ ") as RowNumber,")
.append(
") select * from tempPagination where RowNumber>? and RowNumber<=?")
.toString();
}
}
函數:
Java代碼
public class BitAndFunction implements SQLFunction {
public Type getReturnType(Type type, Mapping mapping) {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
public String render(List args, SessionFactoryImplementor factory)
throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException(
"BitAndFunction requires 2 arguments!");
}
return args.get(0).toString() + " & " + args.get(1).toString();
}
}
Java代碼
public class BitAndFunction implements SQLFunction {
public Type getReturnType(Type type, Mapping mapping) {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
public String render(List args, SessionFactoryImplementor factory)
throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException(
"BitAndFunction requires 2 arguments!");
}
return args.get(0).toString() + " & " + args.get(1).toString();
}
}
Java代碼
public class BitOrFunction implements SQLFunction {
public Type getReturnType(Type type, Mapping mapping) {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
public String render(List args, SessionFactoryImplementor factory)
throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException(
"BitOrFunction requires 2 arguments!");
}
return args.get(0).toString() + " | " + args.get(1).toString();
}
}
Java代碼
public class BitOrFunction implements SQLFunction {
public Type getReturnType(Type type, Mapping mapping) {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
public String render(List args, SessionFactoryImplementor factory)
throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException(
"BitOrFunction requires 2 arguments!");
}
return args.get(0).toString() + " | " + args.get(1).toString();
}
}
Java代碼
public Type getReturnType(Type type, Mapping mapping) {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
public String render(List args, SessionFactoryImplementor factory)
throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException(
"BitXorFunction requires 2 arguments!");
}
return args.get(0).toString() + " ^ " + args.get(1).toString();
}
到此,關于“sqlserver2005的分頁優化”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。