7

3つのテーブルを結合するSQLクエリがあり、1つは他の2つを接続する多対多です。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は適用されません。クライアントの数ですが、clients * 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 に答える