在 MyBatis 中,GROUP BY
子句通常用于 SQL 查询中,以便根据一个或多个列对结果集进行分组。GROUP BY
通常与聚合函数(如 COUNT
、SUM
、AVG
、MAX
、MIN
等)一起使用,以便对每个分组进行计算。
1. 基本语法
GROUP BY
的基本语法如下:
SELECT column1, column2, aggregate_function(column3)
FROM table_name
WHERE condition
GROUP BY column1, column2;
column1
,column2
:要分组的列。aggregate_function(column3)
:对每个分组应用的聚合函数。table_name
:要查询的表。condition
:可选的过滤条件。
2. 在 MyBatis 中使用 GROUP BY
在 MyBatis 中,你可以直接在 SQL 映射文件(Mapper.xml
)或注解中使用 GROUP BY
子句。
2.1 在 XML 中使用 GROUP BY
假设你有一个 Orders
表,并且你想根据 customer_id
分组,计算每个客户的订单总数。
<select id="getOrderCountByCustomer" resultType="map">
SELECT customer_id, COUNT(order_id) AS order_count
FROM Orders
GROUP BY customer_id
</select>
在这个例子中,resultType="map"
表示返回的结果是一个 Map
,其中键是列名,值是对应的数据。
2.2 在注解中使用 GROUP BY
如果你使用的是注解方式,可以在 @Select
注解中直接编写 SQL 语句。
@Select("SELECT customer_id, COUNT(order_id) AS order_count FROM Orders GROUP BY customer_id")
List<Map<String, Object>> getOrderCountByCustomer();
3. 使用 GROUP BY
与 HAVING
HAVING
子句用于过滤分组后的结果。它与 WHERE
类似,但 WHERE
是在分组前过滤,而 HAVING
是在分组后过滤。
例如,你想找出订单数大于 5 的客户:
<select id="getCustomersWithMoreThan5Orders" resultType="map">
SELECT customer_id, COUNT(order_id) AS order_count
FROM Orders
GROUP BY customer_id
HAVING order_count > 5
</select>
4. 多列分组
你可以根据多个列进行分组。例如,你想根据 customer_id
和 order_date
分组,计算每个客户每天的订单数:
<select id="getDailyOrderCountByCustomer" resultType="map">
SELECT customer_id, order_date, COUNT(order_id) AS order_count
FROM Orders
GROUP BY customer_id, order_date
</select>
5. 结合其他 SQL 语句
GROUP BY
可以与其他 SQL 语句(如 JOIN
、ORDER BY
等)结合使用。例如,你想根据客户名称和订单日期分组,并计算每个客户每天的订单数:
<select id="getDailyOrderCountByCustomerName" resultType="map">
SELECT c.customer_name, o.order_date, COUNT(o.order_id) AS order_count
FROM Orders o
JOIN Customers c ON o.customer_id = c.customer_id
GROUP BY c.customer_name, o.order_date
ORDER BY c.customer_name, o.order_date
</select>
6. 返回自定义对象
如果你不想返回 Map
,而是想返回一个自定义的 Java 对象,可以在 resultType
中指定该对象的全限定类名。
假设你有一个 CustomerOrderCount
类:
public class CustomerOrderCount {
private int customerId;
private int orderCount;
// Getters and Setters
}
你可以在 SQL 映射文件中这样写:
<select id="getOrderCountByCustomer" resultType="com.example.CustomerOrderCount">
SELECT customer_id AS customerId, COUNT(order_id) AS orderCount
FROM Orders
GROUP BY customer_id
</select>
7. 注意事项
GROUP BY
子句中的列必须出现在SELECT
列表中,除非它们是聚合函数的参数。- 如果你在
SELECT
中使用了聚合函数,但没有使用GROUP BY
,那么查询将返回一个单一的结果行。 HAVING
子句只能用于过滤分组后的结果,不能用于过滤单个行。
8. 示例
假设你有一个 Orders
表,结构如下:
CREATE TABLE Orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
amount DECIMAL(10, 2)
);
你想查询每个客户的订单总金额:
<select id="getTotalAmountByCustomer" resultType="map">
SELECT customer_id, SUM(amount) AS total_amount
FROM Orders
GROUP BY customer_id
</select>
9. 总结
GROUP BY
是 SQL 中非常强大的功能,能够帮助你根据某些列对数据进行分组,并对每个分组进行聚合计算。在 MyBatis 中,你可以通过 XML 或注解的方式轻松地使用 GROUP BY
,并结合其他 SQL 语句来实现复杂的查询需求。
10 案例背景
假设我们有一个电商系统,其中有两个表:
Customers
表:存储客户信息。Orders
表:存储订单信息。
表结构如下:
CREATE TABLE Customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(100)
);
CREATE TABLE Orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE,
amount DECIMAL(10, 2)
);
需求
我们需要实现以下功能:
- 查询每个客户的订单总数。
- 查询每个客户的总消费金额。
- 查询订单数超过 5 的客户。
实现步骤
1. 查询每个客户的订单总数
SQL 语句:
SELECT customer_id, COUNT(order_id) AS order_count
FROM Orders
GROUP BY customer_id;
MyBatis Mapper XML:
<select id="getOrderCountByCustomer" resultType="map">
SELECT customer_id, COUNT(order_id) AS order_count
FROM Orders
GROUP BY customer_id
</select>
Java 接口:
List<Map<String, Object>> getOrderCountByCustomer();
返回结果:
[
{"customer_id": 1, "order_count": 3},
{"customer_id": 2, "order_count": 5},
{"customer_id": 3, "order_count": 2}
]
2. 查询每个客户的总消费金额
SQL 语句:
SELECT customer_id, SUM(amount) AS total_amount
FROM Orders
GROUP BY customer_id;
MyBatis Mapper XML:
<select id="getTotalAmountByCustomer" resultType="map">
SELECT customer_id, SUM(amount) AS total_amount
FROM Orders
GROUP BY customer_id
</select>
Java 接口:
List<Map<String, Object>> getTotalAmountByCustomer();
返回结果:
[
{"customer_id": 1, "total_amount": 300.00},
{"customer_id": 2, "total_amount": 500.00},
{"customer_id": 3, "total_amount": 200.00}
]
3. 查询订单数超过 5 的客户
SQL 语句:
SELECT customer_id, COUNT(order_id) AS order_count
FROM Orders
GROUP BY customer_id
HAVING order_count > 5;
MyBatis Mapper XML:
<select id="getCustomersWithMoreThan5Orders" resultType="map">
SELECT customer_id, COUNT(order_id) AS order_count
FROM Orders
GROUP BY customer_id
HAVING order_count > 5
</select>
Java 接口:
List<Map<String, Object>> getCustomersWithMoreThan5Orders();
返回结果:
[
{"customer_id": 2, "order_count": 6}
]
4. 结合 JOIN
查询客户名称
如果我们还想在结果中包含客户名称,可以使用 JOIN
将 Customers
表和 Orders
表连接起来。
SQL 语句:
SELECT c.customer_name, COUNT(o.order_id) AS order_count
FROM Orders o
JOIN Customers c ON o.customer_id = c.customer_id
GROUP BY c.customer_name;
MyBatis Mapper XML:
<select id="getOrderCountByCustomerName" resultType="map">
SELECT c.customer_name, COUNT(o.order_id) AS order_count
FROM Orders o
JOIN Customers c ON o.customer_id = c.customer_id
GROUP BY c.customer_name
</select>
Java 接口:
List<Map<String, Object>> getOrderCountByCustomerName();
返回结果:
[
{"customer_name": "Alice", "order_count": 3},
{"customer_name": "Bob", "order_count": 5},
{"customer_name": "Charlie", "order_count": 2}
]
5. 返回自定义对象
如果我们不想返回 Map
,而是想返回一个自定义的 Java 对象,可以定义一个 CustomerOrderCount
类:
public class CustomerOrderCount {
private String customerName;
private int orderCount;
// Getters and Setters
}
MyBatis Mapper XML:
<select id="getOrderCountByCustomerName" resultType="com.example.CustomerOrderCount">
SELECT c.customer_name AS customerName, COUNT(o.order_id) AS orderCount
FROM Orders o
JOIN Customers c ON o.customer_id = c.customer_id
GROUP BY c.customer_name
</select>
Java 接口:
List<CustomerOrderCount> getOrderCountByCustomerName();
返回结果:
List<CustomerOrderCount> result = mapper.getOrderCountByCustomerName();
for (CustomerOrderCount coc : result) {
System.out.println(coc.getCustomerName() + ": " + coc.getOrderCount());
}