1、官方涵义
FIND_IN_SET(str,strlist) : str 要查询的字符串,strlist 需查询的字段,参数以”,”分隔,形式如 (1,2,6,8,10,22);该函数的作用是查询字段(strlist)中是否包含(str)的结果,返回结果为null或记录。
SELECT FIND_IN_SET('b', 'a,b,c,d');
// 结果:2
// 因为 b 在strlist集合中2的位置, a是位置1
select FIND_IN_SET('1', '1');
// 结果:1
// 这时候的strlist集合有点特殊,只有一个字符串
select FIND_IN_SET('2', '1,2');
// 结果:2
select FIND_IN_SET('6', '1');
// 结果:0 strlist中不存在str,所以返回0。
综上: FIND_IN_SET函数中,若前一个字符串包含在后一个字符串集合中,返回大于0的数,该数为前一个字符串在后一个字符串中的位置。
2、find_in_set() 和 in 的区别
1、in后面只能跟常量, find_in_set()函数可以使用常量或字段。
2、in是完全匹配,find_in_set()函数是精确匹配,字段值以英文”,”分隔。
另:like是广泛的模糊匹配,字符串中没有分隔符,Find_IN_SET 是精确匹配,字段值以英文”,”分隔,Find_IN_SET查询的结果要小于like查询的结果
3、应用场景
1、文章表type字段查询
文章表里面有个type字段,它存储的是文章类型,有 1头条、2推荐、3热点、4图文等等 。现在有篇文章他既是头条,又是热点,还是图文,type中以 1,3,4 的格式存储。那我们如何用sql查找所有type中有4的图文类型的文章呢?
select * from article where FIND_IN_SET('4',type)
2、部门树查询,匹配当前节点及所有子节点
2.1 数据表字段说明
2.2 匹配部门id或父id为100的数据
SELECT dept_id FROM sys_dept WHERE dept_id = 100 or FIND_IN_SET( 100 , ancestors )
4、遍历树形结构
(id,pid)格式数据转树和森林结构工具类设计与实现_小熊vip的博客-CSDN博客
树形结构很多地方都有应用,比如我们在构造网站后台的授权限树的时候,再比如我们在设计多级留言的时候、还有分类,部门层级结构等等。表设计都必须还有当前id,父id,所有父id。
有些时候我们的树形结构并不需要过多设计,这是因为我们的很多时候对这棵树的要求不高(两层、三层就行了),这时候我们很容易的会按照层级划分树形结构,然后查询数据库的时候会一层一层的嵌套查询。如果层次比较浅这种做法是可取的(或者我们本来就不打算一次将树加载完全,而是在需要时再加载,那分层级的多次加载也许有用武之地)。但是考虑这种情况:我们的树比较深,而且要一次加载完全。要是按照一层一层的加载原则,那么光是访问数据库的查询语句就是指数形式的了。性能肯定不好。那么除了一层一层的查询好有更好的办法吗?肯定是有的,多查询一些资料会发现这种做法比较常见了:(id,pid),通俗说就是一个节点只需要记住自己的id和父亲的id(根节点没有pid可以设置一个特殊值)就可以拥有呈现这棵树的完整结构全部信息了,仔细想一想自己学过的数据结构,是不是这样的?好了这样一来我们在设计数据库的时候就可以很轻松的设计一棵树的树形结构了。那么有个问题,我们在展现出来的时候总不能直接显示一连串的(id,pid)集合吧。我们要的是树形结构。这时候我们其实非常想实现的是从(id,pid)到
(id,children【】)的转化。毕竟我们在展现树形结构的时候后一种格式更适合页面的呈现,而前一种比较适合数据的存储格式。
首先是自定义节点类:
/**
* 节点类
* @author bearsmall
*
*/
@Data
public class TreeNode {
private int id;//主键ID
private int pid;//父节点ID
private List<TreeNode> children = new ArrayList<TreeNode>();//子孙节点
private Object content;//节点内容,可以是具体多个字段,继承基类(属性id,pid;children)
public TreeNode(int id, Object content) {
this.id = id;
this.content = content;
}
public TreeNode(int id, int pid, Object content) {
this.id = id;
this.pid = pid;
this.content = content;
}
}
然后是树节点管理类:
/**
* 树节点管理类
*
* @author bearsmall
*
*/
public class TreeNodeManager {
private List<TreeNode> list;// 树的所有节点
public TreeNodeManager(TreeNode[] items) {
list = new ArrayList<TreeNode>();
for (TreeNode treeNode : items) {
list.add(treeNode);
}
}
public TreeNodeManager(List<TreeNode> items) {
list = items;
}
/**
* 根据节点ID获取一个节点
*
* @param id
* 节点ID
* @return 对应的节点对象
*/
public TreeNode getTreeNodeAT(int id) {
for (TreeNode treeNode : list) {
if (treeNode.getId() == id)
return treeNode;
}
return null;
}
/**
* 获取树的根节点
*
* @return 一棵树的根节点
*/
public TreeNode getRoot() {
for (TreeNode treeNode : list) {
if (treeNode.getPid() == 0)
return treeNode;
}
return null;
}
}
最后是树节点转化类:
/**
* 节点归并类
* @author bearsmall
*
*/
public class TreeNodeMerger {
/**
* 将节点数组归并为一棵树(填充节点的children域)
* 时间复杂度为O(n^2)
* @param items 节点域
* @return
*/
public static TreeNode merge(TreeNode[] items){
TreeNodeManager treeNodeManager = new TreeNodeManager(items);
for (TreeNode treeNode : items) {
if(treeNode.getPid()!=0){
TreeNode t = treeNodeManager.getTreeNodeAT(treeNode.getPid());
t.getChildren().add(treeNode);
}
}
return treeNodeManager.getRoot();
}
/**
* 将节点数组归并为一棵树(填充节点的children域)
* 时间复杂度为O(n^2)
* @param items 节点域
* @return
*/
public static TreeNode merge(List<TreeNode> items){
TreeNodeManager treeNodeManager = new TreeNodeManager(items);
for (TreeNode treeNode : items) {
if(treeNode.getPid()!=0){
TreeNode t = treeNodeManager.getTreeNodeAT(treeNode.getPid());
t.getChildren().add(treeNode);
}
}
return treeNodeManager.getRoot();
}
}
简单测试一下:
public class Main {
public static void main(String[] args) {
TreeNode[] treeNodes = new TreeNode[10];
// 这里只有一个根节点,即只有一颗树
treeNodes[0] = new TreeNode(1, 0, "");
treeNodes[1] = new TreeNode(2, 1, "");
treeNodes[2] = new TreeNode(3, 1, "");
treeNodes[3] = new TreeNode(4, 2, "");
treeNodes[4] = new TreeNode(5, 3, "");
treeNodes[5] = new TreeNode(6, 4, "");
treeNodes[6] = new TreeNode(7, 3, "");
treeNodes[7] = new TreeNode(8, 5, "");
treeNodes[8] = new TreeNode(9, 6, "");
treeNodes[9] = new TreeNode(10, 9, "");
TreeNode treeNode = TreeNodeMerger.merge(treeNodes);
JSONArray jsonArray = JSONArray.fromObject(treeNode);
System.out.println(jsonArray);
}
}
输出结果:
[{
"children" : [{
"children" : [{
"children" : [{
"children" : [{
"children" : [{
"children" : [],
"pid" : 9,
"id" : 10,
"content" : ""
}
],
"pid" : 6,
"id" : 9,
"content" : ""
}
],
"pid" : 4,
"id" : 6,
"content" : ""
}
],
"pid" : 2,
"id" : 4,
"content" : ""
}
],
"pid" : 1,
"id" : 2,
"content" : ""
}, {
"children" : [{
"children" : [{
"children" : [],
"pid" : 5,
"id" : 8,
"content" : ""
}
],
"pid" : 3,
"id" : 5,
"content" : ""
}, {
"children" : [],
"pid" : 3,
"id" : 7,
"content" : ""
}
],
"pid" : 1,
"id" : 3,
"content" : ""
}
],
"pid" : 0,
"id" : 1,
"content" : ""
}
]
plus------------------------------------------------------森林的实现--------------------------------------------------
即:有多个根节点
森林管理类:
/**
* 森林节点管理类
*
* @author bearsmall
*
*/
public class ForestNodeManager {
private List<TreeNode> list;// 森林的所有节点
public ForestNodeManager(TreeNode[] items) {
list = new ArrayList<TreeNode>();
for (TreeNode treeNode : items) {
list.add(treeNode);
}
}
public ForestNodeManager(List<TreeNode> items) {
list = items;
}
/**
* 根据节点ID获取一个节点
*
* @param id
* 节点ID
* @return 对应的节点对象
*/
public TreeNode getTreeNodeAT(int id) {
for (TreeNode treeNode : list) {
if (treeNode.getId() == id)
return treeNode;
}
return null;
}
/**
* 获取树的根节点【一个森林对应多颗树】
*
* @return 树的根节点集合
*/
public List<TreeNode> getRoot() {
List<TreeNode> roots = new ArrayList<TreeNode>();
for (TreeNode treeNode : list) {
if (treeNode.getPid() == 0)
roots.add(treeNode);
}
return roots;
}
}
森林节点归并类:
/**
* 节点归并类
* @author bearsmall
*
*/
public class ForestNodeMerger {
/**
* 将节点数组归并为一个森林(多棵树)(填充节点的children域)
* 时间复杂度为O(n^2)
* @param items 节点域
* @return 多棵树的根节点集合
*/
public static List<TreeNode> merge(TreeNode[] items){
ForestNodeManager forestNodeManager = new ForestNodeManager(items);
for (TreeNode treeNode : items) {
if(treeNode.getPid()!=0){
TreeNode t = forestNodeManager.getTreeNodeAT(treeNode.getPid());
t.getChildren().add(treeNode);
}
}
return forestNodeManager.getRoot();
}
/**
* 将节点数组归并为一个森林(多棵树)(填充节点的children域)
* 时间复杂度为O(n^2)
* @param items 节点域
* @return 多棵树的根节点集合
*/
public static List<TreeNode> merge(List<TreeNode> items){
ForestNodeManager forestNodeManager = new ForestNodeManager(items);
for (TreeNode treeNode : items) {
if(treeNode.getPid()!=0){
TreeNode t = forestNodeManager.getTreeNodeAT(treeNode.getPid());
t.getChildren().add(treeNode);
}
}
return forestNodeManager.getRoot();
}
}
测一下:
public class Main2 {
public static void main(String[] args) {
TreeNode[] treeNodes = new TreeNode[10];
// 多个根节点
treeNodes[0] = new TreeNode(1, 0, "");
treeNodes[1] = new TreeNode(2, 0, "");
treeNodes[2] = new TreeNode(3, 1, "");
treeNodes[3] = new TreeNode(4, 2, "");
treeNodes[4] = new TreeNode(5, 3, "");
treeNodes[5] = new TreeNode(6, 4, "");
treeNodes[6] = new TreeNode(7, 3, "");
treeNodes[7] = new TreeNode(8, 5, "");
treeNodes[8] = new TreeNode(9, 6, "");
treeNodes[9] = new TreeNode(10, 9, "");
List<TreeNode> tns = ForestNodeMerger.merge(treeNodes);
JSONArray jsonArray = JSONArray.fromObject(tns);
System.out.println(jsonArray);
}
}
打印输出:
[{
"children" : [{
"children" : [{
"children" : [{
"children" : [],
"pid" : 5,
"id" : 8,
"content" : ""
}
],
"pid" : 3,
"id" : 5,
"content" : ""
}, {
"children" : [],
"pid" : 3,
"id" : 7,
"content" : ""
}
],
"pid" : 1,
"id" : 3,
"content" : ""
}
],
"pid" : 0,
"id" : 1,
"content" : ""
}, {
"children" : [{
"children" : [{
"children" : [{
"children" : [{
"children" : [],
"pid" : 9,
"id" : 10,
"content" : ""
}
],
"pid" : 6,
"id" : 9,
"content" : ""
}
],
"pid" : 4,
"id" : 6,
"content" : ""
}
],
"pid" : 2,
"id" : 4,
"content" : ""
}
],
"pid" : 0,
"id" : 2,
"content" : ""
}
]
实际使用:可以使用泛型,变成工具类
/**
* 将节点数组归并为一个森林(多棵树)(填充节点的children域)
* 时间复杂度为O(n^2)
*
* @param items 节点域
* @return 多棵树的根节点集合
*/
public static <T extends INode> List<T> merge(List<T> items) {
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
items.forEach(forestNode -> {
if (forestNode.getParentId() != 0) {
INode node = forestNodeManager.getTreeNodeAT(forestNode.getParentId());
if (node != null) {
node.getChildren().add(forestNode);
} else {
forestNodeManager.addParentId(forestNode.getId());
}
}
});
return forestNodeManager.getRoot();
}