【问题标题】:Hibernate HQL inner join subselectHibernate HQL 内连接子选择
【发布时间】:2019-07-13 04:50:48
【问题描述】:

我得到了以下 SQL,我想将其转换为有效的 HQL。这个问题是,它不允许加入每个docs 的子查询。即使这些是旧文档 (v3.3),这部分似乎仍然适用于 hibernate 5.3。

SELECT f.date,
    f.name,
    SUM(f.seats) 
FROM Foo f 
        INNER JOIN (SELECT fl.date,
                        fl.start + fl.end AS code 
                    FROM Foo fl 
                    WHERE fl.date >= (SELECT MAX(earliestDate) 
                                            FROM Bar) 
                        AND fl.name = :name) fl 
        ON f.start + f.end = code 
        AND f.date = fl.date 
WHERE f.date >= (   SELECT MAX(earliestDate) 
                        FROM Bar)   
GROUP BY f.date,
    f.name 
ORDER BY f.date ASC,
    SUM(f.seats) DESC

我想出了这个 HQL:

SELECT NEW com.company.project.model.FooRepresentation( f.fooId.date,
    f.fooId.name,
    SUM(f.seats)) 
FROM Foo f 
        INNER JOIN (SELECT fl.fooId.date,
                        fl.fooId.start + fl.fooId.end AS code 
                    FROM Foo fl 
                    WHERE fl.fooId.date >= (SELECT MAX(earliestDate) 
                                            FROM FooConfig) 
                        AND fl.fooId.name = :name) fl 
        ON f.fooId.start + f.fooId.end = code 
        AND f.fooId.date = fl.fooId.date 
WHERE f.fooId.date >= ( SELECT MAX(earliestDate) 
                        FROM FooConfig) 
GROUP BY f.fooId.date,
    f.fooId.name 
ORDER BY f.fooId.date ASC,
    SUM(f.seats) DESC

尝试执行此 HQL 查询,导致此异常

org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 169

暗示(INNER JOIN (SELECT

有没有办法加入 HQL 中的子查询?如果没有,用 HQL 实现与 SQL 相同结果的最佳方法是什么?


我不太擅长将 inner join on <sub-query> 转换为允许子查询的实际 where 子句

【问题讨论】:

  • 您为什么要这样做而不是坚持使用 SQL 的原因是什么?
  • 主要是为了方便将结果转换为非 @Entity 类 (NEW com.company.project.model.FooRepresentation())
  • 哦,NEW com.company.project.model.FooRepresentation 语法?但我的意思是,与重写所有代码相比,这是从Object[] 映射到构造函数调用的一行代码:) 无论如何 - 我不确定这将是一个简单的重写......
  • 如果没有不允许加入子查询的不便,这实际上并不难转换。它基本上只是替换列名。所以你会建议我使用原生查询并自己映射它?

标签: java hibernate hql


【解决方案1】:

我认为你的 SQL 查询可以改写成这样:

SELECT f.date, f.name, SUM(f.seats) 
FROM Foo f 
WHERE EXISTS (
  SELECT 1
  FROM Foo f1
  WHERE f.start + f.end = f1.start + f1.end
  AND f.date = f1.date
  AND f1.date >= (SELECT MAX(earliestDate) FROM Bar)
  AND f1.name = :name  
)
AND f.date >= (SELECT MAX(earliestDate) FROM Bar)
GROUP BY f.date, f.name
ORDER BY f.date ASC, SUM(f.seats) DESC

这应该更容易转换为 HQL,甚至可能更正确,因为您的原始查询似乎在自联接行之间创建了不需要的笛卡尔积。

在 SQL 中可能有更好的查询方法,无需自行加入 Foo 表,使用单次遍历表。但我需要更多地了解您的实际用例,以及您使用的 RDBMS。

【讨论】:

  • 这应该真的更容易翻译成 HQL。到目前为止,结果是相同的,而我的查询似乎要快一些(大约 5-10 毫秒,尽管我的基准测试技能很低)。我正在使用 MSSQL 2017。我需要为每个日期获取 start+end 在 start+end 中的每个名称以获取特定名称。 sum(f.seats) 仅用于订购(我希望这是可以理解的)。目前我使用了ResultTransformer,但可能会切换回来并使用您的查询
  • 您描述这个查询的方式肯定是错误的,但您可能没有注意到,因为该错误可能仅在边缘情况下的轻微错误排序中表现出来。为了加快速度,您可以在start + end 上创建一个computed column start_plus_end,并在(date, name, start_plus_end) 上添加一个索引
  • 嗯,你是内连接而不是半连接... :) 内连接创建笛卡尔积,这就是它们的设计目的。但是你不想要那个笛卡尔积。我已经在博客上写过几次关于这些事情的文章,例如blog.jooq.org/2015/10/13/…blog.jooq.org/2017/01/12/…。简而言之,当内部连接时,您重复f 表中的行数次以匹配f1 表中的行。这很慢并且会产生错误的结果。
  • ...在特定情况下它可能会更快,但在许多其他情况下它肯定很慢。您的解决方案可能更快的原因是我在外部查询中省略了不必要的f.date >= (SELECT ...) 过滤器。 SQL Server 似乎无法自行推断出这一点。我将编辑我的答案并重新添加该过滤器。
  • 感谢所有来源。我将查询转换为 HQL,目前正在对其进行测试以检查结果是否匹配。
【解决方案2】:

在@LukasEder 的回答的帮助下,我创建了这个 HQL:

SELECT NEW com.company.project.model.FooRepresentation( f.fooId.date,
    f.fooId.name,
    SUM(f.seats) ) 
FROM Foo f 
WHERE EXISTS (  SELECT 1 
                FROM Foo f1 
                WHERE f.fooId.start + f.fooId.end = f1.fooId.start + f1.fooId.end 
                    AND f.fooId.date = f1.fooId.date 
                    AND f1.fooId.date >= (  SELECT MAX(earliestDate) 
                                            FROM FooConfig) 
                    AND f1.fooId.name = :name ) 
    AND f.fooId.date >= (   SELECT MAX(earliestDate) 
                            FROM FooConfig) 
GROUP BY f.fooId.date,
    f.fooId.name 
ORDER BY f.fooId.date ASC,
    SUM(f.seats) DESC

之前我使用 ResultTransformer 运行这个原生 SQL 查询:

SELECT f.date,
    f.name,
    SUM(f.seats) 
FROM Foo f 
        INNER JOIN (SELECT fl.date,
                        fl.start + fl.end AS code 
                    FROM Foo fl 
                    WHERE fl.date >= (  SELECT MAX(earliestDate) 
                                        FROM FooConfig) 
                        AND fl.name = :name) fl 
        ON f.start + f.end = code 
        AND f.date = fl.date 
WHERE f.date >= (   SELECT MAX(earliestDate) 
                    FROM FooConfig) 
GROUP BY f.date,
    f.name 
ORDER BY f.date ASC,
    SUM(f.seats) DESC

这两个查询的结果实际上存在细微差别,但并不一致。在这种情况下,我的意思是一致的,并不是每个可能的date 结果都不同,只是对某些人来说。同样对于提供的参数:name,结果是一致的。我会更多地评估哪些结果更合适,但如果新的结果是正确的,我不会感到惊讶。

【讨论】:

    猜你喜欢
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多