为多个表中的数据创建摘要行

Kyl*_*yle 9 mysql sql

我正在尝试编写SQL查询以生成给定时间段内给定用户执行的操作的摘要行.我有以下相关的表结构:

用户

  • ID
  • 球队

audit_periods(可以处理,运输,休息等)

  • 用户身份
  • period_type(可以是"处理","运送"等 - 目前尚未规范化)
  • started_at
  • finished_at(对于当前时期可以为null,因此逻辑在下面的时间)

audit_tasks

  • audit_period_id
  • audit_task_type_id
  • created_at
  • 得分了

audit_task_types

  • 名称("扫描","place_in_pallet"等)
  • 得分(似乎多余,但我们需要保持audit_task在执行时收到的分数,因为audit_task_type分数可以在以后更改)

ER图

对于给定时期内的每个用户,我想创建类似以下数据行的内容:

users.id users.email time_spent_processing time_spent_shipping ... number_of_scans number_of_pallets

这将通过计算每个用户来计算:

  • audit_periods至少部分落在期望的窗口中?(使用started_at和finished_at.)
  • 用户在每种audit_period中花费了多长时间?(应该通过audit_periods.period_type参与分组,我想.)
  • audit_tasks属于所需窗口的范围?(使用created_at - 不在下面的代码中.)
  • 用户在窗口期间完成了每种类型的audit_task中有多少个?(加入audit_task_type,可能涉及audit_task_types.name上的一个组.)
  • 在这段时间内赚了多少积分?(汇总窗口中所有audit_tasks的分数.)

我已经筋疲力尽了所有我知道的SQL技巧(并不多),并提出了类似以下的内容:

select 
    u.id as user_id,
    u.email as email,
    u.team as team,
    ap.period_type as period_type,
    att.name,
    time_to_sec(
      timediff(least("2011-03-17 00:00:00", ifnull(ap.finished_at, utc_timestamp())), greatest("2011-03-16 00:00:00", ap.started_at))
    ) as period_duration,
    sum(at.score) as period_score
  from audit_periods as ap
  inner join users as u on ap.user_id = u.id
  left join audit_tasks as at on at.audit_period_id = ap.id
  left join audit_task_types as att on at.audit_task_type_id = att.id
  where (ap.started_at >= "2011-03-16 00:00:00" or (ap.finished_at >= "2011-03-17 00:00:00" and ap.finished_at <= "2011-03-17 00:00:00"))
    and (ap.finished_at <= "2011-03-17 00:00:00" or (ap.started_at >= "2011-03-16 00:00:00" and ap.started_at <= "2011-03-16 00:00:00"))
    and u.team in ("Foo", "Bar")
  group by u.id, ap.id, at.id
Run Code Online (Sandbox Code Playgroud)

但这似乎在功能上等同于最终选择所有审计任务.我也试过一些子查询,但收效甚微.更直接地,这会生成类似(跳过不太重要的列):

user_id   |   period_type   |   period_duration  |  name            |   score
1             processing        1800s               scan                200
1             shipping          1000s               place_in_pallet     100
1             shipping          1000s               place_in_pallet     100
1             break             500s                null                null
Run Code Online (Sandbox Code Playgroud)

当我想要的时候:

user_id   |   processing    |   shipping  |  break  |  scan  |  place_in_pallet  |  score
1             1800s             1000s        500s      1        2                   400
Run Code Online (Sandbox Code Playgroud)

我可以轻松地获取给定用户的所有audit_tasks并将其汇总到代码中,但我可能会在给定的时间段内获取数十万个audit_tasks,因此需要在SQL中完成.

只是要清楚 - 我正在寻找一个查询来为每个用户生成一行,包含在其他3个表中收集的摘要数据.因此,对于每个用户,我想知道他花了多少时间在每种类型的audit_period(3600秒处理,3200秒发货等),以及他执行的每个audit_task中有多少(5次扫描,放入10个项目)托盘等).

我认为我有一个解决方案的元素,我只是将它们拼凑在一起.我确切地知道如何在Ruby/Java/etc中实现这一点,但我认为我不太了解SQL以便知道我缺少哪个工具.我需要临时桌吗?工会?其他一些构造完全?

非常感谢任何帮助,我可以澄清上述内容是否完全无稽之谈.

Tho*_*mas 1

您需要将其分解为两个交叉表查询,其中一个查询为您提供有关用户的audit_periods 信息,另一个查询将为您提供用户的audit_task 信息,然后将其连接到Users 表。目前尚不清楚您希望如何汇总每种情况下的信息。例如,如果给定用户有 10audit_period行,查询应如何汇总这些持续时间?我在这里假设了持续时间的总和,但您可能需要最小值或最大值,甚至可能是整体增量。

Select U.user_id
    , AuditPeriodByUser.TotalDuration_Processing As processing
    , AuditPeriodByUser.TotalDuration_Shipping As shipping
    , AuditPeriodByUser.TotalDuration_Break As break
    , AuditTasksByUser.TotalCount_Scan As scan
    , AuditTasksByUser.TotalCount_Place_In_Pallet As place_in_pallet
    , AuditTasksByUser.TotalScore As score
From users As U
    Left Join   (
                Select AP.user_id
                    , Sum( Case When AP.period_type = 'processing' 
                                Then Time_To_Sec( 
                                        TimeDiff( 
                                            Coalesce(AP.started_at, UTC_TIMESTAMP()), AP.finished_at ) ) ) 
                        As TotalDuration_Processing
                    , Sum( Case When AP.period_type = 'shipping' 
                                Then Time_To_Sec( 
                                        TimeDiff( 
                                            Coalesce(AP.started_at, UTC_TIMESTAMP()), AP.finished_at ) ) ) 
                        As TotalDuration_Shipping
                    , Sum( Case When AP.period_type = 'break' 
                                Then Time_To_Sec( 
                                        TimeDiff( 
                                            Coalesce(AP.started_at, UTC_TIMESTAMP()), AP.finished_at ) ) ) 
                        As TotalDuration_Break
                From audit_periods As AP
                Where AP.started_at >= @StartDate 
                    And AP.finished_at <= @EndDate
                Group by AP.user_id
                ) As AuditPeriodByUser
            On AuditPeriodByUser.user_id = U.user_id
    Left Join   (
                Select AP.user_id
                    , Sum( Case When AT.Name = 'scan' Then 1 Else 0 End ) As TotalCount_Scan
                    , Sum( Case When AT.Name = 'place_in_pallet' Then 1 Else 0 End ) As TotalCount_Place_In_Pallet
                    , Sum( AT.score ) As TotalScore
                From audit_tasks As AT
                    Join audit_task_types As ATT
                        On ATT.id = AT.audit_task_type_id
                    Join audit_periods As AP
                        On AP.audit_period_id = AP.id
                Where AP.started_at >= @StartDate 
                    And AP.finished_at <= @EndDate
                Group By AP.user_id
                ) As AuditTasksByUser
        On AuditTasksByUser.user_id = U.user_id
Run Code Online (Sandbox Code Playgroud)