【发布时间】:2021-04-17 06:54:51
【问题描述】:
我有一个表格,它使用标签作为过滤器来查询网格视图。
架构:
项目: id, col_a
标签: ID、名称、类型
label_project: id、label_id、project_id
我的问题是我想获取所有带有用户正在使用的标签的项目记录,但对于某些标签需要做 OR,
这是查询需要执行的工作示例:
SELECT DISTINCT gd.*
FROM project p
JOIN label_project lp1 ON lp1.label_id=306
JOIN label_project lp2 ON lp2.label_id=135
JOIN label_project lp3 ON lp3.label_id=285
JOIN label_project lp4 ON lp4.label_id=173
WHERE ( lp1.project_id=p.id
OR lp2.project_id=p.id
) -- labels of lp1 and lp2 have the same type
AND lp3.project_id=p.id
AND lp4.project_id=p.id;
-- labels of (lp1, lp2), lp3 and lp4 have different types
假设有 6 个标签“类型”,对于相同类型的标签,需要在它们之间进行 OR(参见查询中的第一个 where 子句),其余使用 AND(参见 where 子句的其余部分)
示例查询的问题在于它在 QueryDSL 中的显示非常明显,单个查询大约需要 10 秒。我读这主要是因为查询使用了 distinct。
有人知道用 QueryDSL 编写这个查询的方法吗?或者在 SQL 中
添加标签过滤前查询:
query.distinct().from(PROJECT)
.leftJoin(FAVORITE_PROJECT)
.on(PROJECT.eq(FAVORITE_PROJECT.project).and(FAVORITE_PROJECT.employee.eq(employee)))
.where(ProjectService.restrictedProjectWhereClause(context.getEmployee()));
}
/**
* Returns a predicate that filters out results of restricted projects where the employee has no rights for
* @param employee The logged in employee
* @return The predicate
*/
public static Predicate restrictedProjectWhereClause(Employee employee) {
return PROJECT.restricted.isFalse()
.or(PROJECT.restricted.isTrue()
.and(PROJECT.employee.eq(employee)
.or(PROJECT.leaderEmployee.eq(employee)
.or(PROJECT.managerEmployee.eq(employee)
.or(hasRestrictedRoleAccess(employee).exists())))));
}
private static JPQLQuery<Integer> hasRestrictedRoleAccess(Employee employee) {
return JPAExpressions.selectFrom(USER_SECURITY_ROLE)
.join(USER)
.on(USER_SECURITY_ROLE.user.eq(USER))
.join(EMPLOYEE)
.on(USER_SECURITY_ROLE.user.eq(EMPLOYEE.user))
.where(USER_SECURITY_ROLE.securityRole.in(ESecurityRole.RESTRICTED_SECURITY_ROLES)
.and(EMPLOYEE.eq(employee)))
.select(USER_SECURITY_ROLE.id);
}
如何将标签过滤添加到 QueryDSL 中的查询中:
// First add necessary joins
for (int i = 0; i < labels.size(); i++) {
QLabelProject lp = new QLabelProject(String.format("lp%d", i));
labelMap.computeIfAbsent(labels.get(i).getSystemLabelType(), k -> new HashMap<>());
labelMap.get(labels.get(i).getSystemLabelType()).put(labels.get(i), lp);
query = query.join(lp)
.on(lp.project.eq(qProject));
}
// Decide where clause
BooleanExpression expression = null;
for (Map.Entry<ESystemLabelType, Map<Label, QLabelProject>> entry : labelMap.entrySet()) {
BooleanExpression subExpression = null;
for (Map.Entry<Label, QLabelProject> lp : entry.getValue().entrySet()) {
if (entry.getKey() == null) {
subExpression = subExpression == null ? lp.getValue().label.id.eq(lp.getKey().getId()) :
subExpression.and(lp.getValue().label.id.eq(lp.getKey().getId()));
} else {
subExpression = subExpression == null ? lp.getValue().label.id.eq(lp.getKey().getId()) :
subExpression.or(lp.getValue().label.id.eq(lp.getKey().getId()));
}
}
expression = expression == null ? (BooleanExpression)new BooleanBuilder().and(subExpression).getValue() :
expression.and(subExpression);
}
【问题讨论】:
-
QueryDSL 生成的 SQL 查询与底层 SQL 查询一样快。所以如果你有一个高性能的 SQL 查询,它应该很容易转换为 QueryDSL。如果 SQL 查询甚至没有性能,QueryDSL 也不会让它更快......你是如何将 SQL 片段转换为 QueryDSL 的?
-
@Jan-WillemGmeligMeyling 嗨,我已经用 QueryDSL 代码更新了我的帖子。尽管根据我的经验,SQL 查询比 QueryDSL 代码快得多。我应该提到,在添加标签过滤器之前,在代码中添加了一些其他连接和 where 子句。所以我想那里有区别
-
所以您是说另一个查询比您与我们共享的查询慢?您希望我们在这里做什么 ;-) 我们需要完整的查询来提供一些相关的 SQL 优化。
-
请重新排列查询,以便
ON子句说明表是如何相关的 (lgd4.grid_data_id=gd.id) 并且WHERE子句执行过滤 (lgd2.label_id=135) .这样做时,您可能会发现 cmets 不同意该代码。之后,我可能对如何加快查询有一些想法。 -
@Jan-WillemGmeligMeyling 是的,我的错...我再次更新了帖子,显示了在添加标签过滤之前的查询。
标签: java mysql performance querydsl