MySQL-常用语句记录
按照子表的id升序查询出子表字段去重并且逗号隔开
GROUP_CONCAT(DISTINCT column_name ORDER BY column_name SEPARATOR ',')
举例
-
b.xxx 的值不区分大小写 select a.*, GROUP_CONCAT(DISTINCT b.xxx ORDER BY b.id ASC SEPARATOR ',') as bxxx from order a left join detail b a.id= b.o_id group by a.id
-
b.xxx的值区分大小写 select a.*, GROUP_CONCAT(DISTINCT BINARY b.xxx ORDER BY b.id ASC SEPARATOR ',') as bxxx from order a left join detail b a.id= b.o_id group by a.id
count(1) count(*) 和 count(name)的区别
count(1)和count(*)
在 MySQL 中,COUNT(*) 和 COUNT(1) 通常是等价的,因为它们都用来计算表中的行数,并且在大多数情况下,它们的性能和结果是一样的,且性能差异微乎其微
SELECT COUNT(*) FROM table;
SELECT COUNT(1) FROM table;
count(name)
- 功能: 计算 name 列中非 NULL 的行数。如果 name 列包含 NULL 值,则这些 NULL 值不会被计入。
- 性能: 由于需要考虑 name 列的 NULL 值,因此在某些情况下,性能可能会略逊色于 COUNT(*),但差别通常不大。
总结: 在大多数情况下,COUNT() 和 COUNT(1) 在 MySQL 中表现一致,你可以使用它们中的任何一个来计算行数。不过,**COUNT() 通常是更直观和推荐的选择**。
数据同步差异排查
场景: 从A库同步一张表A1到B库的B1, A表总共30001,B表30000条.找出没有同步过去的那一条
方案是查询出A表的所有ID,然后把所有id UNION ALL,在进行两个表的关联
SELECT ids.id
FROM (
SELECT 1 as id
UNION ALL SELECT 43581
UNION ALL SELECT 43583
.... 假设中间还有n条
UNION ALL SELECT 43607
UNION ALL SELECT 43609
) AS ids
left join shop_new ds on ds.id = ids.id
where ds.id is null
;
通过叶子结点找到上级树
返回的结果包含blood_id = 23的数据行
用途是, 通过叶子节点找所有上级,然后封装成链表的形式,代码相对复杂.所以可以换一个思路,先通过以下SQL找到上级树.然后从parent_id集合中排除blood_id集合,得到first_prent_id集合,表示当前节点所在上级树的第一层节点的id集合
然后遍历first_prent_id集合,通过Java查询以下的SQL的结果集中, 父id为**first_prent_id.get(i)**的数据,则取到了当前节点的下一级节点列表.
WITH RECURSIVE ancestors AS (
SELECT id,blood_id,parent_id FROM blood_tree WHERE blood_id = 23
UNION
SELECT p.id,p.blood_id,p.parent_id FROM blood_tree p
JOIN ancestors a ON p.blood_id = a.parent_id
)
select * from ancestors;
解释:
-
CTE 表示递归表达式
-
WITH RECURSIVE ancestors AS (...)
定义了一个名为ancestors(可以自定义)
的递归 CTE -
表达的意思是:
- 以blood_id = 23的数据行row1为参考,查询所有
blood_id=row1.parent_id
的数据行rowM - 然后继续对rowM的每一行数据, 进行递归,查询
blood_id=rowM.parent_id
的所有数据行 - 最后将这些数据UNION在一起.得到关于某个叶子节点的上级树
- 以blood_id = 23的数据行row1为参考,查询所有
完整的Java代码如下:
private List<BloodNodeResVo> getParentBloods(Integer bloodId) {
List<BloodNodeResVo> treeList = new ArrayList<>();
List<PtBloodTree> bloodTreeList = ptBloodTreeService.getParentTreeByBloodId(bloodId);
if (CollectionUtils.isEmpty(bloodTreeList)) {
return treeList;
}
//包括每一层的父节点
Set<Integer> parentIds = bloodTreeList.stream().map(PtBloodTree::getParentId).collect(Collectors.toSet());
//包括每一层的子节点
Set<Integer> bloodIds = bloodTreeList.stream().map(PtBloodTree::getBloodId).collect(Collectors.toSet());
//所有节点.用于后期批量一次性查SQL.
Set<Integer> allBloodIds = new HashSet<>();
allBloodIds.addAll(parentIds);
allBloodIds.addAll(bloodIds);
//父节点中移除所有的叶子节点.因为第一层的节点不再有父节点.所以第一层的节点也就不存在于子节点中
parentIds.removeAll(bloodIds);
List<PtBloodIdx> ptBloodIdxes = listByIds(allBloodIds);
Map<Integer, PtBloodIdx> bloodIdxMap = Optional.ofNullable(ptBloodIdxes).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(PtBloodIdx::getId, Function.identity(), (k1, k2) -> k1));
//遍历第一层的节点
for (Integer parentId : parentIds) {
PtBloodIdx ptBloodIdx = bloodIdxMap.get(parentId);
BloodNodeResVo bloodNodeResVo = new BloodNodeResVo();
bloodNodeResVo.setBloodId(ptBloodIdx.getId());
bloodNodeResVo.setBloodName(ptBloodIdx.getBloodName());
bloodNodeResVo.setNumber(1);
treeList.add(bloodNodeResVo);
//获取子节点
getChild(bloodTreeList, bloodNodeResVo, bloodIdxMap);
}
return treeList;
}
void getChild(List<PtBloodTree> bloodTreeList, BloodNodeResVo parent, Map<Integer, PtBloodIdx> bloodIdxMap) {
bloodTreeList.stream().filter(p -> p.getParentId().equals(parent.getBloodId())).forEach(p -> {
PtBloodIdx bloodIdx = bloodIdxMap.get(p.getBloodId());
BloodNodeResVo child = new BloodNodeResVo();
child.setBloodId(bloodIdx.getId());
child.setBloodName(bloodIdx.getBloodName());
parent.getChilds().add(child);
//递归调用子节点的子节点
getChild(bloodTreeList, child, bloodIdxMap);
});
}
通过父节点找到所有子下级树
返回的结果包含blood_id = 23的数据行
WITH RECURSIVE descendants AS (
SELECT blood_id,parent_id,blood_level FROM blood_tree WHERE blood_id = 23
UNION
SELECT p.blood_id,p.parent_id,p.blood_level FROM blood_tree p
JOIN descendants a ON p.parent_id = a.blood_id
)
select * from descendants;