1. 程式人生 > >智慧將SqlServer的查詢語句轉換為分頁語句

智慧將SqlServer的查詢語句轉換為分頁語句

主要用到了jsqlparser,前面有篇部落格介紹過:

為了給Mybatis分頁外掛增加對sqlserver的支援,專門寫了這樣一個獨立的工具,只依賴jsqlparser。

這個類不僅是為了給分頁外掛使用的,他還能獨立使用,使用它你可以方便的生成一個分頁查詢。

簡單講一下處理的邏輯:

通過對SqlServer進行分析,利用jsqlparser方便的解析,然後對sql結構進行修改,生成最後的分頁語句。

首先一個sql通常有兩種情況,一種是普通的一個select查詢,一種是通過union,minus等連線的多個查詢。

當發現是多個查詢的時候,會在原來的SQL基礎上在外面包含一層查詢,讓原來的查詢變成子查詢。

外層的查詢會從多個查詢中的第一個查詢中提取查詢列(有別名的使用別名),因為每個查詢的列都是一樣的,所以找一個提取就行。

另外在多個SQL中的最後一個相比其他來說可能會多一些條件,這裡主要考慮的是order by,如果有order by語句,會把order by移到外層SQL上。

做完上面的處理後,就和第一種普通的一個select查詢一樣了。

接下來處理這一個select查詢。

第一步先獲取查詢列,並且會對別名和表名進行一些特殊處理。

第二步給SQL增加ROW_NUMBER(),將order by提取到OVER中

第三步處理全部子查詢,如果子查詢包含order by,會增加top 100 percent

第四步在select查詢外包一層TOP查詢。

經過上面的步驟就能得到一個合理結構的分頁查詢了。

其中有一些細節性的東西jsqlparser都考慮到了,不需要自己去特殊處理,例如distinct。

下面是兩個例子。

這個類是獨立的,使用的時候可以初始化一個,然後直接呼叫方法即可。

初始化:

public static final SqlServer sqlServer = new SqlServer();


第一個,多個查詢UNION ALL

@Test
public void testSqlUnion() throws JSQLParserException {
    String originalSql = "select countryname,countrycode code from country where id >170 " +
            "union all " +
            "select countryname,countrycode code from country where id < 10 order by code";
    System.out.println(sqlServer.convertToPageSql(originalSql, 1, 10));
}

生成的SQL如下(經過人工格式化):
SELECT TOP 10 PAGE_TABLE_ALIAS.countryname, PAGE_TABLE_ALIAS.code
  FROM (SELECT ROW_NUMBER() OVER(ORDER BY code) PAGE_ROW_NUMBER,
               WRAP_OUTER_TABLE.countryname,
               WRAP_OUTER_TABLE.code
          FROM ((SELECT countryname, countrycode code
                   FROM country
                  WHERE id > 170) UNION ALL
                (SELECT countryname, countrycode code
                   FROM country
                  WHERE id < 10)) AS WRAP_OUTER_TABLE) AS PAGE_TABLE_ALIAS
 WHERE PAGE_ROW_NUMBER > 1
 ORDER BY PAGE_ROW_NUMBER


第二個,簡單查詢

@Test
public void testSqlDistinct() throws JSQLParserException {
    String originalSql = "select distinct countrycode,countryname from country order by countrycode";
    System.out.println(sqlServer.convertToPageSql(originalSql, 1, 10));
}

生成的SQL如下(經過人工格式化):
SELECT TOP 10 PAGE_TABLE_ALIAS.countrycode, PAGE_TABLE_ALIAS.countryname
  FROM (SELECT DISTINCT ROW_NUMBER() OVER(ORDER BY countrycode) PAGE_ROW_NUMBER,
                        countrycode,
                        countryname
          FROM country) AS PAGE_TABLE_ALIAS
 WHERE PAGE_ROW_NUMBER > 1
 ORDER BY PAGE_ROW_NUMBER


注意:

1.由於需要提取order by,所以儘可能保證最外層的SQL包含order by

2.如果沒有order by,那麼上面呼叫的convertToPageSql還有第四個引數orderBy

public String convertToPageSql(String sql, int offset, int limit, String orderBy) 

如果原來的sql有order by,那麼通過該方法指定orderBy之後會覆蓋原sql中的order by

人為指定的時候很難把握欄位名字的寫法,所以建議在sql中帶上order by