我有SQL查询连接3个表,一个只是多对多连接其他两个。我使用Spring JDBC ResultSetExtractor将ResultSet转换为我的对象,看起来大致如下:
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;
但是,由于此查询有连接,当我限制结果数量时,我实际上限制了JOINED结果的数量(即,因为客户端将显示与其拥有的帐户类型数量一样多的次数,LIMIT不是客户端数量,而是客户端数量* accountTypes,这不是我想要的。“
我提出的唯一解决方案是从查询中删除LIMIT(和OFFSET,因为这也是错误的)并以编程方式应用它们:
List<Client> allClients = jdbcTemplate.query....
List<Client> result = allClients.subList(offset, offset+limit);
但这显然不是一个非常好的,有效的解决方案。还有更好的方法吗?
答案 0 :(得分: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;
希望这也有助于其他人。
答案 1 :(得分: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