Grails/GORM/HQL:消除 FROM 部分中的内部查询

发布于 2024-11-26 17:39:18 字数 1488 浏览 0 评论 0原文

我有一个与此类似的表“Table”:

id     user_id      owner_id      due_date
------+------------+-------------+--------------------------
1     | 1          | 1           | 2011-07-26 12:28:50
2     | 1          | 1           | 2011-07-26 15:32:11
3     | 1          | 1           | 2011-07-27 08:11:58
4     | 2          | 1           | 2011-07-26 15:19:44
5     | 2          | 1           | 2011-07-23 12:00:50

如您所见,由 FK user_id 标识的用户可以拥有多个具有不同 due_date 的条目。

我想获取按 user_id 分组的所有最新 Table 实体。

在普通的 MySQL 中,它可能看起来像这样:

SELECT * FROM (
    SELECT * FROM TABLE
      WHERE owner_id = xxx
      ORDER BY due_date DESC
  ) AS sorted
GROUP BY user_id

我首先使用 ORDER BY 进行选择,然后应用 GROUP BY(不幸的是,所有的 SELECT 都不起作用。请参见此处 http://www.cafewebmaster.com/mysql-order-sort-group)。

现在我想将其实现为 HQL(使用 Grails)。问题是 HQL 不支持 FROM 部分中的内部 SELECT,因此以下方法不起作用:

def findMostRecentPerUser( Owner owner ) {
    def result = Table.executeQuery("""
                from Table as t1
                where t1.id in (
                    select sorted.id from (select * from Table as t2 where t2.owner_id = ${owner.id} order by t2.due_date desc) as sorted group by sorted.user_id
            )
        """)
    return result
}

另一种方法是 Criteria,但我不知道如何以这种方式实现它。

有人能指出我正确的方向吗?非常感谢您的帮助。

I have a table "Table" similar to this one:

id     user_id      owner_id      due_date
------+------------+-------------+--------------------------
1     | 1          | 1           | 2011-07-26 12:28:50
2     | 1          | 1           | 2011-07-26 15:32:11
3     | 1          | 1           | 2011-07-27 08:11:58
4     | 2          | 1           | 2011-07-26 15:19:44
5     | 2          | 1           | 2011-07-23 12:00:50

As you can see, a user identified by FK user_id can have multiple entries with different due_date.

I would like to fetch all most recent Table entities grouped by user_id.

In plain MySQL, it could look like this:

SELECT * FROM (
    SELECT * FROM TABLE
      WHERE owner_id = xxx
      ORDER BY due_date DESC
  ) AS sorted
GROUP BY user_id

I would first select with ORDER BY, then apply a GROUP BY (all in one SELECT doesn't work, unfortunately. see e.g. here http://www.cafewebmaster.com/mysql-order-sort-group).

Now I would like to implement this as HQL (with Grails). The problem is that HQL does not support inner SELECT's in the FROM part, so the following approach won't work:

def findMostRecentPerUser( Owner owner ) {
    def result = Table.executeQuery("""
                from Table as t1
                where t1.id in (
                    select sorted.id from (select * from Table as t2 where t2.owner_id = ${owner.id} order by t2.due_date desc) as sorted group by sorted.user_id
            )
        """)
    return result
}

Another way would be a Criteria but I've no idea how to implement it that way.

Could someone point me into the right direction? Your help is greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

没有你我更好 2024-12-03 17:39:18

这种方法应该有效。改进表示赞赏。

def findMostRecentPerUser( Owner owner ) {
    // Inner query sorts maximum due_date per user
    // Outer query fetches all Table entries with this due_date
    def result = Table.executeQuery("""
            from Table as t1
            where t1.due_date in (
                select max(t2.due_date) from Table as t2 where t2.owner = :owner group by t2.user
            )
        """,
        [ owner: owner ] )
    return result
}

This way should work. Improvements appreciated.

def findMostRecentPerUser( Owner owner ) {
    // Inner query sorts maximum due_date per user
    // Outer query fetches all Table entries with this due_date
    def result = Table.executeQuery("""
            from Table as t1
            where t1.due_date in (
                select max(t2.due_date) from Table as t2 where t2.owner = :owner group by t2.user
            )
        """,
        [ owner: owner ] )
    return result
}
甜味超标? 2024-12-03 17:39:18

为什么不直接将 MySql 排除在外,只获取结果并使用 比较器进行排序。这样,无论数据库如何实现,您都可以保证排序是相同的。

List<Table> results = query();
Collections.sort(results, YourCustomComparator.getInstance());

这样做还会减少数据库的负载,因为它变成了一个没有 order by 的简单查询。

请参阅此处的集合文档。

Why not just leave MySql out of it and just get the results back and use a Comparator to sort. This way you can garentee sort will be the same regardless of how a database implements it.

List<Table> results = query();
Collections.sort(results, YourCustomComparator.getInstance());

Doing this will also decrease the load on your database because it turns into a simple query without an order by.

See the Collections doc here.

鲸落 2024-12-03 17:39:18

我不确定您的意图是否可行(我什至不确定您应该能够使用原始 SQL 来完成它)。

您想要使用 Group By 语句,该语句(根据我的理解)仅在与聚合函数结合使用时才有意义,但您没有使用聚合函数。我假设您的目标是获得如下所示的数据结构: [user_id_1: [t1, t5, t2], user_id_2: [t3, t4, t8]...] (其中 tX 是Table 中的实例)是否正确?

如果是这种情况,我认为您只需要获取您关心的一组实例并自行组织它们:

def findMostRecentPerUser( Owner owner ) {
  def someDate = new Date() - 5 // 5 Days ago
  def result = [:].withDefault{[]}
  Table.withCriteria {
    eq('owner.id', owner.id)
    gt('due_date', someDate)
    order('due_date', 'desc')
  }.each{result[it.user.id] << it}
  return result
}

但是,正如您所看到的,这只会为您提供在某个日期之后发生的实例(或者删除 gt 语句,它为您提供所有实例,这可能不是您想要的)。您可以使用 distinct('user.id') 将其限制为每个用户的最近一个实例,

但是如果您想要每个用户的 5 个最近实例,我认为您会为每个用户执行查询(当然,将结果集限制为 5 个)。如果有人有这个案例的解决方案,我非常有兴趣找到解决方案。

I'm not sure that what you're intending is possible (I'm not even sure you should be able to do it with raw SQL).

You want to use a Group By statement which (in my understanding) only has meaning when used in conjunction with aggregate functions, but you're not using an aggregate function. I assume your goal is to get a data structure looking like this: [user_id_1: [t1, t5, t2], user_id_2: [t3, t4, t8]...] (where tX is an instance out of Table) is that correct?

If that's the case I think you just need to get the set of instances you care about back and organize them yourself:

def findMostRecentPerUser( Owner owner ) {
  def someDate = new Date() - 5 // 5 Days ago
  def result = [:].withDefault{[]}
  Table.withCriteria {
    eq('owner.id', owner.id)
    gt('due_date', someDate)
    order('due_date', 'desc')
  }.each{result[it.user.id] << it}
  return result
}

However, as you can see, this only gives you instances that occurred after some date (or, removing the gt statement, it gives you all instances, which is probably not what you want). You could restrict it down to the single most recent instance per user by using distinct('user.id')

But if you wanted, say, the 5 most recent instances for each user I think you'd be stuck doing a query for each user (and limiting the result set to 5, of course). I'm very interested in seeing a solution for this case if someone has one.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文