Mysql / Hql运行总需要单个结果但由于subselect限制而无法获取

时间:2013-10-30 04:09:44

标签: mysql hibernate

我正在使用总计来查找加权集的中位数。这在sql中工作正常,但由于Hibernate不支持FROM子句中的子选择,因此在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形式?

更新: 根据Mosty Mostacho的建议以及其他一些研究,这似乎始终如一:

SET @runtot:=0;
SELECT 
    score, weight, @val := score
FROM 
    tmp_stddev
WHERE
    (@runtot := @runtot + weight) <= (9 / 2)
ORDER BY score;

这里通过在变量中选择最后匹配的分数,我可以稍后通过选择它的值在同一连接中使用它,并获取排序列表中的最后一项,这就是我想要的。此外,我缩小了用户定义变量的读/写范围,当我更改数据时,这似乎是不一致的。

问题:

  • 这是用户定义变量的安全用法吗?我一直在阅读很多关于在同一个语句中读取/写入它们的不安全性,但是这不是必须按顺序进行评估,因为读/写都是HAVING子句中单个表达式的一部分吗?换句话说,这可靠吗?
  • 如何在HQL中使用它?如果我使用自定义方言并创建一个自定义函数来执行“@val:=得分”部分,我会得到一个'无效的过滤器 - 参数名称格式'异常(我想因为冒号,但不应该只是Hibernate从HQL到SQL的直接传递替换?为什么会关注冒号?)
  • 我上面的查询是否有更好的解决方案,我没有考虑过?

1 个答案:

答案 0 :(得分:1)

好的,我在数学上绝对迷失了:)

无论如何,我试图将你的第一个查询变成那种不使用FROM子句的东西。这就是我得到的:

SELECT score, (@runtot := @runtot + weight) rt
FROM t, (SELECT @runtot := 0) init
HAVING rt = FLOOR(9 / 2)
ORDER BY score

使用having子句实际上是一团糟但似乎是不需要派生表的唯一方法。唯一的问题是,尽管这会回答这个问题,但它对小数字段没有帮助。

现在,解决方案可能不如将having子句更改为

那么简单
HAVING rt <= 9 / 2

检查此fiddle,了解结果是如何混淆的。这就是当你弄乱用户定义的变量并且不使用派生表时你所做的。

要尝试的第二件事是,如果您可以在JOIN中拥有派生表。我的意思是:

SELECT * FROM t
JOIN (
  SELECT id FROM r
) s ON t.id = s.id

这是我能走多远,但可能会给你一些想法尝试:)

编辑(上次尝试):

在以下查询之后,我将不得不请求原谅SQL语言:

SELECT score
FROM t, (SELECT @runtot := 0.0) init
WHERE (@runtot := @runtot + weight) AND (9 / 2 >= @runtot)
ORDER BY score DESC
LIMIT 1

小提琴here