我正在使用运行总计来查找加权集的中位数。由于 Hibernate 不支持 FROM 子句中的子选择,这在 sql 中可以正常工作,但在 hql 中则不行。我不能轻易地使用 sql,因为实际代码涉及到 hql 中已经存在的大量动态查询构建。
这是示例表:
score weight
2 1
5 1
5 1
6 1
7 1
10 2
10 2
总分是 9(我在这个查询之前就知道了)。9/2 = 4.5,所以这个查询应该返回 6 作为加权中值。
这是示例查询:
SET @runtot:=0;
SELECT
q1.score
FROM
(SELECT
score, (@runtot:=@runtot + weight) AS rt
FROM
tmp_stddev
ORDER BY score) as q1
WHERE
q1.rt <= (9 / 2)
ORDER BY q1.score DESC
LIMIT 1;
在子选择中按分数 ASC 排序使我能够继续添加权重,直到达到一半。在外部查询中排序 DESC 使我能够使用 LIMIT 只返回单个结果以获得最佳性能(这里可能有很多数据,所以我真的只想返回一个结果)。
这适用于 SQL,但不适用于 HQL。我可以创建一个自定义方言,我相信它支持在查询中设置用户变量(将其清除为 0 部分将在针对同一连接的单独 sql 查询中)。问题是子选择。
我可以这样做:
SET @runtot:=0;
SET @runtot2:=0;
SELECT
score,
(@runtot := @runtot + weight) AS rt
FROM
tmp_stddev
WHERE (@runtot2 := @runtot2 + weight) <= (9/2)
ORDER BY score;
但这会返回我所有的分数,我真的只想要一个(数据集可能非常大,速度很重要)。
有什么建议可以重新编写它以返回单个结果、快速并采用 hql 可以生成的 sql 形式吗?
更新:根据以下莫斯蒂·莫斯塔乔的建议,以及其他一些研究,这似乎一直有效:
SET @runtot:=0;
SELECT
score, weight, @val := score
FROM
tmp_stddev
WHERE
(@runtot := @runtot + weight) <= (9 / 2)
ORDER BY score;
在这里,通过将最后匹配的分数选择到变量中,我可以稍后通过选择它的值在同一连接中使用它,并获取排序列表中的最后一项,这就是我想要的。此外,我还缩小了用户定义变量的读/写范围,当我更改数据时,这似乎是不一致的。
问题:
- 这是用户定义变量的安全用法吗?我一直在阅读很多关于在同一个语句中读/写它们是多么不安全的文章,但是由于读/写都是 HAVING 子句中单个表达式的一部分,因此不必按顺序评估吗?换句话说,这可靠吗?
- 我怎样才能让它在 HQL 中工作?如果我使用自定义方言并创建一个执行“@val := score”部分的自定义函数,我会得到一个“无效的过滤器参数名称格式”异常(我认为是因为冒号,但不应该只是Hibernate 从 HQL 到 SQL 的直接传递替换?为什么它会关心有一个冒号?)
- 有没有我没有考虑过的更好的解决方案?