从自连接表中获取最短持续时间

Posted

技术标签:

【中文标题】从自连接表中获取最短持续时间【英文标题】:get minimum duration from self join table 【发布时间】:2017-06-22 09:56:28 【问题描述】:

在构建查询方面需要一些帮助。我在bookings 表和booking_versions 表之间存在一对多的关系。每次预订状态更改时,都会生成一个新的booking_versions 行,其中指定了statuscreated_at 列。我的目标是获得状态状态更改之间的最短持续时间。这是我从现在开始尝试的方法

def self.min( filter )
  query = <<-SQL
    SELECT bv1.item_id AS 'obj_id', MIN(
    bv2.created_at - bv1.created_at) AS 'mintime'
    FROM bus_booking_versions AS bv1
    INNER JOIN bus_booking_versions AS bv2
    ON bv1.item_id = bv2.item_id
    WHERE bv1.status = ?
    AND bv1.created_at >= ?
    AND bv1.created_at <= ?
    AND bv2.status IN (?)
    AND bv2.created_at >= ?
    AND bv2.created_at <= ?
  SQL
  self.find_by_sql([query, filter.start_status, filter.from_start, filter.to_start, filter.end_statuses, filter.from_end, filter.to_end])
end

那是 Rails 代码,它仍然使用纯 sql。此查询无法正常工作。可能您可以建议如何获得运行最短时间()的预订。

巴士预订版本架构(item_id 是预订的外键)

  create_table "bus_booking_versions", force: :cascade do |t|
    t.string   "item_type",  limit: 255,        null: false
    t.integer  "item_id",    limit: 4,          null: false
    t.string   "event",      limit: 255,        null: false
    t.string   "whodunnit",  limit: 255
    t.text     "object",     limit: 4294967295
    t.datetime "created_at"
    t.integer  "status",     limit: 4
  end

paper tail gem 以这种方式创建版本

has_paper_trail on: [:create, :update],
                class_name: "Bus::BookingVersion",
                meta:  status: Proc.new  |t| t.status if t.status_changed?  

【问题讨论】:

如果您要添加表结构和具有预期输出的两三个样本数据,这将很容易提供帮助。 提示:您可以添加数据库标签,提供预期结果示例,您将有机会从 dba 中获得答案。 【参考方案1】:

由于我不知道您使用的是哪个数据库引擎,所以我将编写一个通用的 SQL 语句。您选择的数据库引擎可能有更好的方法来做到这一点。

您可以使用子查询来获取表中的“下一个”行,而不是将同一个表连接在一起(由于您的bv1.item_id = bv2.item_id,您将只比较相同的行)。然后,您只需遍历表的一个实例。

具体来说,像这样的子查询,它会找到最早的 created_at,它晚于当前行的 created_at(即“下一行”),这是您想要比较的:

select min(created_at) from bus_booking_versions bv2 where bv1.item_id=bv2.item_id and created_at &gt; bv1.created_at

这是一般查询。我使用了 mysql 的 timediff 函数,但您可以将其更改为特定数据库引擎的计算日期时间差异的方法。根据您的数据库引擎,可能还有更好的方法来执行此操作。

select
    bv1.item_id as 'obj_id',
    mintime = timediff
        ( 
            (
                select 
                    min(created_at) 
                from 
                    bus_booking_versions bv2 
                where 
                    bv1.item_id=bv2.item_id
                    and created_at > bv1.created_at
            ),
            bv1.created_at
        )
from
    #tmp1 bv1
where
    timediff((select min(created_at) from bus_booking_versions bv2 where bv1.item_id=bv2.item_id and created_at > bv1.created_at), bv1.created_at) > 0
    and timediff((select min(created_at) from bus_booking_versions bv2 where bv1.item_id=bv2.item_id and created_at > bv1.created_at), bv1.created_at) is not null
order by
    timediff

希望它看起来很简单。我已经扩展了选择中的时间差异/子查询,以便更清楚地了解正在发生的事情。 where 子句中的 timediff() 与 select 中的完全相同,只是它们被压缩为一行。这是为了清除两个 bus_booking_version 具有相同 created_at 的实例,或者正在比较的时间戳是最后/唯一行(没有后面的行)。

您可以添加其余的 where 子句来检查您的其他参数;我只是将它们排除在外,以保持查询清晰易懂。如果您只想返回第一行,则可以将 .first 方法链接到您的 ruby​​ 语句。

【讨论】:

就是这样,非常感谢。专门为详细解答。这东西有效

以上是关于从自连接表中获取最短持续时间的主要内容,如果未能解决你的问题,请参考以下文章

从自引用表中获取“根记录”

从自引用表中获取层次结构数据

如何从自定义 UITableViewCell 获取值到 ViewController?

如何从自定义用户控件 WPF、C# 中的枚举自定义属性中获取值?

如何从自定义表格视图单元类中获取对表格视图控制器的引用

Laravel,如何从自定义表的 jwt 令牌获取登录用户