# sql-blog
场景:
今天写了一个 针对每个用户查询对应的多个标签,和它的订单数量还有总价
表如下:
user 用户表:
user_tags 用户与标签关联表:
tag_managment 标签表:
order 订单表:
首先我们得考虑如何完成这个业务的查询先想 sql 怎么写:
SELECT | |
t.id, | |
t.head, | |
t.nickname, | |
t.open_id, | |
t.wx_union_id, | |
t.sex, | |
t.phone, | |
t.state, | |
t.address, | |
t.create_time, | |
t.delete_time, | |
t.is_del, | |
CAST(COUNT(o.id) / 2 AS UNSIGNED) AS order_count, | |
SUM(o.total_price / 2) AS order_price | |
FROM | |
tab_user t | |
LEFT JOIN tab_user_tags t1 ON t1.user_id = t.id | |
LEFT JOIN tab_tag_managent t2 ON t2.id = t1.tags_id AND t2.is_del = 0 | |
LEFT JOIN tab_order o ON o.create_ther = t.id | |
WHERE | |
t.is_del = 0 | |
GROUP BY | |
t.id | |
LIMIT 5; |
首先通过左连接对应三张表的关系,然后使用 sql 函数计算出每个用户的所有订单数量和总和,但是我们还需要几个模糊查询完整 sql 如下:
SELECT | |
t.id, | |
t.head, | |
t.nickname, | |
t.open_id, | |
t.wx_union_id, | |
t.sex, | |
t.phone, | |
t.state, | |
t.address, | |
t.create_time, | |
t.delete_time, | |
t.is_del, | |
CAST(COUNT(o.id) / 2 AS UNSIGNED) AS order_count, | |
SUM(o.total_price / 2) AS order_price | |
FROM | |
tab_user t | |
LEFT JOIN tab_user_tags t1 ON t1.user_id = t.id | |
LEFT JOIN tab_tag_managent t2 ON t2.id = t1.tags_id AND t2.is_del = 0 | |
LEFT JOIN tab_order o ON o.create_ther = t.id | |
WHERE | |
t.is_del = 0 | |
AND t.nickname LIKE CONCAT( '%', '123', '%' ) | |
AND t.wx_union_id LIKE CONCAT( '%', '123', '%' ) | |
AND t.phone LIKE CONCAT( '%', '123', '%' ) | |
AND t.sex LIKE CONCAT( '%', '男', '%' ) | |
AND t2.tag_name LIKE CONCAT( '%', '123', '%' ) | |
AND t.create_time BETWEEN '2024-04-13 10:11:02' | |
AND '2024-04-15 10:11:08' | |
AND t.id LIKE CONCAT( '%', 1, '%' ) | |
GROUP BY | |
t.id | |
LIMIT 5; |
下面编号 java 代码:
这里 除了用户的 Mapper 就省略了:
Mapper
@Mapper | |
public interface UserMapper extends MPJBaseMapper<User> | |
{ | |
Page<User> getAll(Page<User> page,@Param("user") User user); | |
} |
xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > | |
<!--namespace 绑定一个对应的 Dao (Mapper) 接口 --> | |
<mapper namespace="org.dromara.dkxmodules.mapper.UserMapper"> | |
<resultMap id="newUserDto" type="org.dromara.dkxmodules.entity.User"> | |
<result column="id" property="id" /> | |
<result column="head" property="head" /> | |
<result column="nickname" property="nickname" /> | |
<result column="open_id" property="openId" /> | |
<result column="wx_union_id" property="wxUnionId" /> | |
<result column="sex" property="sex" /> | |
<result column="phone" property="phone" /> | |
<result column="state" property="state" /> | |
<result column="address" property="address" /> | |
<result column="create_time" property="createTime" /> | |
<result column="delete_time" property="deleteTime" /> | |
<result column="is_del" property="isDel" /> | |
<result column="order_count" property="orderCount" /> | |
<result column="order_price" property="orderPrice" /> | |
<collection property="tags" ofType="java.lang.String" | |
column="id" foreignColumn="id" select="getTags"> | |
</collection> | |
</resultMap> | |
<!--SQL 语句头标签 --> | |
<!--select: 查询语句,id: 对应接口的方法名,parameterType: 参数类型,resultType: 返回值类型 --> | |
<select id="getAll" parameterType="org.dromara.dkxmodules.entity.User" resultMap="newUserDto"> | |
SELECT | |
t.id, | |
t.head, | |
t.nickname, | |
t.open_id, | |
t.wx_union_id, | |
t.sex, | |
t.phone, | |
t.state, | |
t.address, | |
t.create_time, | |
t.delete_time, | |
t.is_del, | |
CAST(COUNT(o.id) / 2 AS UNSIGNED) AS order_count, | |
SUM(o.total_price / 2) AS order_price | |
FROM | |
tab_user t | |
LEFT JOIN tab_user_tags t1 ON t1.user_id = t.id | |
LEFT JOIN tab_tag_managent t2 ON t2.id = t1.tags_id AND t2.is_del = 0 | |
LEFT JOIN tab_order o ON o.create_ther = t.id | |
<where> | |
t.is_del = 0 | |
<if test="user.nickname != null and user.nickname != ''"> | |
AND t.nickname LIKE CONCAT('%', #{user.nickname}, '%') | |
</if> | |
<if test="user.wxUnionId != null and user.wxUnionId != ''"> | |
AND t.wx_union_id LIKE CONCAT('%', #{user.wxUnionId}, '%') | |
</if> | |
<if test="user.phone != null and user.phone != ''"> | |
AND t.phone LIKE CONCAT('%', #{user.phone}, '%') | |
</if> | |
<if test="user.sex != null and user.sex != ''"> | |
AND t.sex LIKE CONCAT('%', #{user.sex}, '%') | |
</if> | |
<if test="user.tagsName != null and user.tagsName != ''"> | |
AND t2.tag_name LIKE CONCAT('%', #{user.tagsName}, '%') | |
</if> | |
<if test="user.startDate != null and user.startDate != '' and user.endDate != null and user.endDate != ''"> | |
AND t.create_time BETWEEN #{user.startDate} AND #{user.endDate} | |
</if> | |
<if test="user.id != null and user.id != ''"> | |
AND t.id LIKE CONCAT('%', #{user.id}, '%') | |
</if> | |
</where> | |
GROUP BY | |
t.id; | |
</select> | |
<select id="getTags" resultType="java.lang.String"> | |
SELECT | |
tag_name | |
FROM | |
tab_tag_managent ttm | |
LEFT JOIN tab_user_tags tut ON tut.tags_id = ttm.id | |
LEFT JOIN tab_user tu ON tu.id = tut.user_id | |
WHERE | |
tu.id = #{id} | |
</select> | |
</mapper> |
由于我们还需要用户的所有标签信息所以下面写了一个 getTags 的 select 这个会返回到实体类的集合字段中,其中 resultMap 中的 collection 里面要 column="id" foreignColumn="id",这里会把查询的用户 id 传入 并找到 select 对应的查询然后进行查询返回结果到 tags 字段中返回类型是 String 类型的集合
实体类
/** | |
* 用户表 | |
*/ | |
@Data | |
@NoArgsConstructor | |
@AllArgsConstructor | |
@TableName(value = "tab_user") | |
public class User | |
{ | |
/** | |
* 用户 id | |
*/ | |
@ExcelIgnore | |
@TableId(type = IdType.AUTO) | |
private BigInteger id; | |
/** | |
* 用户头像 | |
*/ | |
@ExcelIgnore | |
private String head; | |
/** | |
* 用户昵称 | |
*/ | |
@ExcelProperty("用户昵称") | |
private String nickname; | |
/** | |
* 微信登录标识 id | |
*/ | |
@ExcelProperty("微信登录标识") | |
private String openId; | |
/** | |
* 微信号码 | |
*/ | |
@ExcelProperty("微信号码") | |
private String wxUnionId; | |
/** | |
* 用户性别 | |
*/ | |
@ExcelProperty("用户性别") | |
@ExcelDictFormat(readConverterExp = "0=男,1=女") | |
private String sex; | |
/** | |
* 用户手机号 | |
*/ | |
@ExcelProperty("用户手机号") | |
private String phone; | |
/** | |
* 用户状态 (0: 启用,1: 未启用) | |
*/ | |
@ExcelProperty("用户状态") | |
private String state; | |
/** | |
* 用户地址 | |
*/ | |
@ExcelProperty("用户地址") | |
private String address; | |
/** | |
* 创建时间 | |
*/ | |
@ExcelProperty("创建时间") | |
private String createTime; | |
/** | |
* 删除时间 | |
*/ | |
@ExcelProperty("删除时间") | |
private String deleteTime; | |
/** | |
* 逻辑删除 | |
*/ | |
@ExcelIgnore | |
@TableLogic | |
private Integer isDel; | |
// 这里排除的字段是用来查询或者接收数据的 | |
@ExcelIgnore | |
@TableField(exist = false) | |
private String tagsName; | |
@ExcelIgnore | |
@TableField(exist = false) | |
private List<String> tags; | |
@ExcelIgnore | |
@TableField(exist = false) | |
private Integer orderCount; | |
@ExcelIgnore | |
@TableField(exist = false) | |
private Integer orderPrice; | |
@ExcelIgnore | |
@TableField(exist = false) | |
private String startDate; | |
@ExcelIgnore | |
@TableField(exist = false) | |
private String endDate; | |
} |
serviceImpl
/** | |
* 查询全部数据并分页,模糊查询 | |
* @return | |
*/ | |
@Override | |
public PageResult<User> getAll(PageParams params, User user) | |
{ | |
Page<User> page = new Page<>(params.getPageNo(), params.getPageSize()); | |
Page<User> all = userMapper.getAll(page, user); | |
List<User> records = all.getRecords(); | |
long total = all.getTotal(); | |
return new PageResult<>(records, total, params.getPageNo(), params.getPageSize()); | |
} |
controller
/** | |
* 查询全部数据并分页,模糊查询 | |
* @param pageParams | |
* @return | |
*/ | |
@PostMapping("/getAll") | |
public PageResult<User> getAll(PageParams pageParams,@RequestBody User user) | |
{ | |
return service.getAll(pageParams, user); | |
} |
测试结果:
如果要想返回标签里面是一个对象的话,如下:
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > | |
<!--namespace 绑定一个对应的 Dao (Mapper) 接口 --> | |
<mapper namespace="org.dromara.dkxmodules.mapper.UserMapper"> | |
<resultMap id="newUserDto" type="org.dromara.dkxmodules.entity.UserEntity"> | |
<result column="id" property="id" /> | |
<result column="head" property="head" /> | |
<result column="nickname" property="nickname" /> | |
<result column="open_id" property="openId" /> | |
<result column="wx_union_id" property="wxUnionId" /> | |
<result column="sex" property="sex" /> | |
<result column="phone" property="phone" /> | |
<result column="state" property="state" /> | |
<result column="address" property="address" /> | |
<result column="create_time" property="createTime" /> | |
<result column="delete_time" property="deleteTime" /> | |
<result column="is_del" property="isDel" /> | |
<result column="order_count" property="orderCount" /> | |
<result column="order_price" property="orderPrice" /> | |
<collection property="tags" ofType="org.dromara.dkxmodules.entity.vo.UserTagVo" | |
column="id" foreignColumn="id" select="getTags"> | |
</collection> | |
</resultMap> | |
<!--SQL 语句头标签 --> | |
<!--select: 查询语句,id: 对应接口的方法名,parameterType: 参数类型,resultType: 返回值类型 --> | |
<select id="getAll" parameterType="org.dromara.dkxmodules.entity.UserEntity" resultMap="newUserDto"> | |
SELECT | |
t.id, | |
t.head, | |
t.nickname, | |
t.open_id, | |
t.wx_union_id, | |
t.sex, | |
t.phone, | |
t.state, | |
t.address, | |
t.create_time, | |
t.delete_time, | |
t.is_del, | |
CAST(COUNT(o.order_id) / 2 AS UNSIGNED) AS order_count, | |
SUM(o.actual_payment / 2) AS order_price | |
FROM | |
tab_user t | |
LEFT JOIN tab_user_tags t1 ON t1.user_id = t.id | |
LEFT JOIN tab_tag_managent t2 ON t2.id = t1.tags_id AND t2.is_del = 0 | |
LEFT JOIN tab_order o ON o.user_id = t.id | |
<where> | |
t.is_del = 0 | |
<if test="user.nickname != null and user.nickname != ''"> | |
AND t.nickname LIKE CONCAT('%', #{user.nickname}, '%') | |
</if> | |
<if test="user.wxUnionId != null and user.wxUnionId != ''"> | |
AND t.wx_union_id LIKE CONCAT('%', #{user.wxUnionId}, '%') | |
</if> | |
<if test="user.phone != null and user.phone != ''"> | |
AND t.phone LIKE CONCAT('%', #{user.phone}, '%') | |
</if> | |
<if test="user.sex != null and user.sex != ''"> | |
AND t.sex LIKE CONCAT('%', #{user.sex}, '%') | |
</if> | |
<if test="user.tagsName != null and user.tagsName != ''"> | |
AND t2.tag_name LIKE CONCAT('%', #{user.tagsName}, '%') | |
</if> | |
<if test="user.tagId != null and user.tagId != ''"> | |
AND t2.id LIKE CONCAT('%', #{user.tagId}, '%') | |
</if> | |
<if test="user.startDate != null and user.startDate != '' and user.endDate != null and user.endDate != ''"> | |
AND t.create_time BETWEEN #{user.startDate} AND #{user.endDate} | |
</if> | |
<if test="user.id != null and user.id != ''"> | |
AND t.id LIKE CONCAT('%', #{user.id}, '%') | |
</if> | |
</where> | |
GROUP BY | |
t.id, | |
t.head, | |
t.nickname, | |
t.open_id, | |
t.wx_union_id, | |
t.sex, | |
t.phone, | |
t.state, | |
t.address, | |
t.create_time, | |
t.delete_time, | |
t.is_del | |
</select> | |
<select id="getTags" resultType="org.dromara.dkxmodules.entity.vo.UserTagVo"> | |
SELECT | |
tag_name AS tagName, tag_color AS tagColor | |
FROM | |
tab_tag_managent ttm | |
LEFT JOIN tab_user_tags tut ON tut.tags_id = ttm.id | |
LEFT JOIN tab_user tu ON tu.id = tut.user_id | |
WHERE | |
tu.id = #{id} | |
</select> | |
</mapper> |
把实体类的泛型更改:
@ExcelIgnore | |
@TableField(exist = false) | |
private List<UserTagVo> tags; |
数据格式:
{ | |
"items": [ | |
{ | |
"id": "1789958514896728066", | |
"head": "http://192.168.2.35:9000/ruoyi/2024/05/13/c5988ff5342f4bb4bdee0cb7d3f47bde.jpeg", | |
"nickname": "Misjudgment", | |
"birthday": null, | |
"openId": "o_Pd964vdtzklLVH08zzEpWBsUFE", | |
"wxUnionId": "wex324", | |
"sex": "0", | |
"phone": "19831988276", | |
"state": "0", | |
"address": "河北省邢台市沙河市十里亭", | |
"createTime": "2024-05-13 17:59:00", | |
"deleteTime": null, | |
"isDel": 0, | |
"tagsName": null, | |
"tags": [ | |
{ | |
"tagName": "回头客", | |
"tagColor": "2" | |
} | |
], | |
"orderCount": 0, | |
"orderPrice": null, | |
"startDate": null, | |
"endDate": null, | |
"tagId": null, | |
"pageSize": 2147483647, | |
"pageNo": 0 | |
} | |
], | |
"counts": 1, | |
"page": 0, | |
"pageSize": 10 | |
} |