7

我有连接 3 个表的 SQL 查询,其中一个只是连接另外两个表的多对多。我使用 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);

但这显然不是一个非常好的、有效的解决方案。有没有更好的办法?

4

2 回答 2

10

有趣的是,写一个问题会让你思考,实际上有助于为你自己的问题想出一个解决方案。

我能够通过简单地将查询的分页部分添加到我的主查询的子查询而不是主查询本身来解决这个问题。

例如,不要这样做:

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;

希望这对其他人也有帮助。

于 2012-06-11T00:28:20.770 回答
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
于 2017-05-25T07:55:05.590 回答