见this similar question。
另请参阅 this presentation Bill Karwin 关于数据库反模式的文章。从幻灯片 48 开始讨论“朴素树”反模式。
解决方案 #1:路径枚举。
存储祖先的路径(在您的情况下为经理):
ID, PATH, NAME
1, 1/, george
2, 2/, peter
3, 1/3, harry
4, 1/3/4, bertrand
轻松查询乔治的后代:
SELECT * from Employee WHERE path LIKE '1/%';
演示文稿还提到了我认为对您的情况不太有用的其他解决方案:
编辑:这是将两个数据库查询与递归内存搜索混合在一起的另一个想法。
public static class Employee {
private Long id;
private boolean isManager;
private Employee manager;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public boolean isManager() {
return isManager;
}
public void setIsManager(boolean isManager) {
this.isManager = isManager;
}
public Employee getManager() {
return manager;
}
public void setManager(Employee manager) {
this.manager = manager;
}
}
首先获取数据库中的所有管理器。如果员工总数是 16k,那么经理的数量应该是可控的(没有双关语)。
// gets all existing managers from the database
private static List<Employee> getAllManagers() {
// SELECT * FROM Employee WHERE isManager = true;
return new ArrayList<>();
}
然后遍历所有管理器,递归确定哪些管理器在查询管理器下工作。
private static Set<Employee> findSubordinateManagers(Employee queryManager) {
List<Employee> allManagers = getAllManagers();
Set<Employee> subordinateManagers = new HashSet<>();
for (Employee employee : allManagers) {
if (isSubordinateTo(employee, queryManager)) {
subordinateManagers.add(employee);
}
}
return subordinateManagers;
}
// determines if the given employee is subordinate to the given manager
private static boolean isSubordinateTo(Employee employee, Employee manager) {
if (employee.getManager() == null) {
return false;
}
if (employee.getManager().getId().equals(manager.getId())) {
return true;
}
return isSubordinateTo(employee, employee.getManager());
}
然后进行第二次 SQL 查询以获取所有员工直接由一组选定的经理管理:
// finds all employees directly subordinate to the given set of managers
private static Set<Employee> findEmployees(Set<Employee> managers) {
// SELECT * from Employee WHERE manager IN (managers);
return new HashSet<>();
}