MySQL Order By和Limit 1子查询返回最近的记录

时间:2015-06-12 20:49:33

标签: mysql sql ruby-on-rails ruby-on-rails-4

我注意到StackOverflow上有一些类似的问题,但到目前为止我没有任何工作。我会尽量保持这个尽可能短。

我正在构建一个需要返回许多可能有或没有合同的问题的查询,这些问题可能会也可能不会完成(completed_at将被设置为DateTime,而不是nil)。每行需要包括:

  • 包含all记录字段
  • issue
  • description
  • 中的budget_item
  • 已完成的最近 completed_at的{​​{1}}日期(一个contract可能有0 budget_item,1 contracts },或5 + contract,其中任意数量可以打开(completed_at:nil)或关闭(completed_at:DateTime)

这是我到目前为止(返回正确的行数,但它返回最近创建的合同,而不是最新的合同

contracts

模型中的代码如下:

BaseItem.issues
   .joins('LEFT JOIN budget_items 
           ON issues.id = budget_items.issue_id 
           LEFT JOIN contracts 
           ON budget_items.id = contracts.budget_item_id')
   .select('issues.*, budget_items.description, contracts.completed_at AS resolved_at')
   .group('issues.id')
   .order('contracts.completed_at')

最终结果必须是:

可能会有多个class BaseItem < ActiveRecord::Base has_many :issues ... end class Issue < ActiveRecord::Base belongs_to :base_item has_many :budget_items ... end class BudgetItem < ActiveRecord::Base belongs_to :issue has_many :contracts ... end class Contract < ActiveRecord::Base belongs_to :budget_item ... end 组成不同的行。每个issues至少有四个issue,仅用于需要出现在最终查询中的budget_items,然后用于将每个budget_item.description加入其中issue contracts {1}} {每个budget_item可以有2个或3个contracts,因此issue最终可能会有8到12 contractscontracts来自completed_at查询需要根据其AS resolved_at属性对其进行排序,并仅返回contract最近completed_atcompleted_at: nil日期。如果有4个合同,则有{{1} }},查询应该返回剩余的两个completed_at日期中最近的一个作为该特定resolved_at的{​​{1}}字段。

任何帮助都会非常赞赏,如果我需要提供任何其他信息,请与我们联系。

-Dave

结果查询(来自评论):

issue

2 个答案:

答案 0 :(得分:1)

.order('contracts.completed_at DESC LIMIT 1')

(或者我不明白你的代码中缺少什么?)

答案 1 :(得分:1)

您不需要在示例中显示实际数据,以使其有用......

这就是我的意思。您可以在下面的示例数据中提出您的问题:

<强>问题

id    base_item_id
--    ------------
10               6
20               6
30               6
99             123

<强> budget_items

id    issue_id    description
--    --------    -----------
 1          10    'one contract, none completed'
 2          20    'one contract, one completed'
 3          30    'two contracts, one completed'
 4          30    'three contracts, two completed'

<强>合同

id    budget_item_id    completed_at
--    --------------    ------------
 1                 1            NULL
 2                 2      2015-01-02
 3                 3      2015-01-03
 4                 3            NULL
 5                 4      2015-01-05
 6                 4            NULL
 7                 4      2015-01-07

预期结果

issues.id    contracts.completed_at    budget_item.description
---------    ----------------------    -----------------------
       10                      NULL    NULL
       20                2015-01-02    one contract, one completed
       30                2015-01-07    three contracts, two completed

Here is SQL Fiddle

这是你想要的吗?我的样本数据是否涵盖所有可能的边缘情况?如果没有,请向其添加更多行,并显示它对结果的影响。

这是最终查询的样子。 MySQL不具备CROSS APPLYLATERAL JOINS之类的东西,因此效率低于其他数据库 - 子查询将运行两次。

我不知道如何将这个SQL翻译成Ruby - 我从未使用过Ruby。

SELECT
    issues.*
    ,(
        SELECT contracts.completed_at
        FROM
            budget_items
            INNER JOIN contracts ON contracts.budget_item_id = budget_items.id
        WHERE
            budget_items.issue_id = issues.id
            AND contracts.completed_at IS NOT NULL
        ORDER BY contracts.completed_at DESC
        LIMIT 1
    ) AS resolved_at
    ,(
        SELECT budget_items.description
        FROM
            budget_items
            INNER JOIN contracts ON contracts.budget_item_id = budget_items.id
        WHERE
            budget_items.issue_id = issues.id
            AND contracts.completed_at IS NOT NULL
        ORDER BY contracts.completed_at DESC
        LIMIT 1
    ) AS description
FROM issues
WHERE issues.base_item_id = 6

主要想法很简单。我们为每个issue返回一行。对于每个问题,我们会找到一个最新的contract,使用您需要的任何条件(例如contracts.completed_at IS NOT NULL仅查找已完成的合同)。

如果contracts根本没有完成issue,则NULLdescription会返回resolved_at。您可以在主SELECT中添加额外的过滤条件,以便在您想要的时候移除此类行(WHERE issues.base_item_id = 6 AND resolved_at IS NOT NULL)。