2012-06-11 53 views
3

我有SQL查詢加入3個表,其中一個只是多對多連接另外兩個。我用一個Spring JDBC ResultSetExtractor類的結果集轉換成我的對象看起來大致是這樣的:如何將分頁應用於帶有聯接的SQL查詢的結果?

class Customer { 
    private String id; 
    private Set<AccountType> accountTypes; 
    ... 
} 

的ResultSetExtractor類的實現看起來是這樣的:

public List<Client> extractData(ResultSet rs) throws SQLException, 
    DataAccessException { 
     Map<Integer, Client> clientsMap = new LinkedHashMap<Integer, Client>(); 
     while (rs.next()) { 
      int id = rs.getInt("id"); 
      // add the client to the map only the first time 
      if (!clientsMap.containsKey(id)) { 
       Client client = new Client(); 
       client.setId(id); 
       ... 
       clientsMap.put(id, client); 
      } 
      // always add the account type to the existing client 
      Client client = clientsMap.get(id); 
      client.addAccountType(extractAccountTypeFrom(rs, id)); 
     } 
     return new ArrayList<Client>(clientsMap.values()); 
} 

這工作得很好,而不分頁。

但是,我需要分析這些結果。我通常做的方式是通過增加這查詢,例如:

SELECT ... ORDER BY name ASC LIMIT 10 OFFSET 30; 

然而,由於這一查詢聯接,當我限制結果的數量,我其實限制(連接結果的數量即作爲客戶將出現的次數與他們擁有的賬戶類型數量相同,LIMIT不是應用客戶數量,而是應用於客戶數量* accountTypes,這不是我想要的)。

我想出的唯一解決辦法是刪除該限制(和偏移,因爲這也將是錯誤的)從查詢和編程應用它們:

List<Client> allClients = jdbcTemplate.query.... 
List<Client> result = allClients.subList(offset, offset+limit); 

但是,這顯然不是一個非常好的,高效的方案。有沒有更好的辦法?

+0

Renato,如何在三個表上連接創建VIEW,然後使用ORDER BY名稱ASC LIMIT 10 OFFSET 30;在VIEW_NAME的select *上使用技巧?運行這個由你的DBA來看看他們是否樂意這樣做。 –

+0

@Rob Kielty - 感謝您的建議......我會試着看看是否可能比我剛剛發佈的解決方案更有效。 – Renato

+0

測試是要走的路。如果你有一個DBA維護數據庫,他們應該很高興你向他們詢問每種方法的優缺點。 –

回答

6

如何寫一個問題讓你思考,並且實際上幫助你想象一個針對你自己問題的解決方案,這很有趣。

我能夠解決這個問題,只需將查詢的分頁部分添加到我的主查詢的子查詢中,而不是主查詢本身。

例如,而不是做:

SELECT client.id, client.name ... 
FROM clients AS client 
LEFT JOIN client_account_types AS cat ON client.id = cat.client_id 
FULL JOIN account_types AS at ON cat.account_type_id = at.id 
ORDER BY client.name ASC 
LIMIT 10 OFFSET 30; 

我這樣做:

SELECT client.id, client.name ... 
FROM (
    SELECT * FROM clients 
    ORDER BY name ASC 
    LIMIT 10 OFFSET 0 
) AS client 
LEFT JOIN client_account_types AS cat ON client.id = cat.client_id 
FULL JOIN account_types AS at ON cat.account_type_id = at.id; 

希望這也有助於其他人。

+0

我正在處理相同的問題,一直無法弄清楚這個查詢如何解決問題。如果客戶端要求pageSize爲10,那麼它期望它獲得10個客戶端,其中每個客戶端具有N個帳號類型等。所以最多你可以實現的是10行或少於10行(如果任何客戶端有多個account_type),你最終將返回少於10行。 – Sudarshan

0

如果您的DBMS支持它,請使用窗口功能DENSE_RANK。例如:

SELECT * FROM 
    (SELECT *, DENSE_RANK() OVER (ORDER BY name, id) count FROM 
    (SELECT a.id, a.name, b.title, DENSE_RANK() OVER (ORDER BY a.name, a.id) offset_ 
     FROM AUTHOR a, BOOK b 
     WHERE a.id = b.authorId) result_offset 
    WHERE offset_ > 30) result_offset_count 
WHERE count <= 10