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

时间:2011-03-17 05:35:16

标签: mysql sql

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

用户

  • ID

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

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

audit_tasks

  • audit_period_id
  • audit_task_type_id
  • created_at
  • 得分

audit_task_types

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

ER Diagram

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

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

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

  • 审计周期至少部分落在所需窗口中? (使用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

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

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

我想要的时候:

user_id   |   processing    |   shipping  |  break  |  scan  |  place_in_pallet  |  score
1             1800s             1000s        500s      1        2                   400

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

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

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

非常感谢任何帮助,我可以澄清上述内容是否完全无意义。

1 个答案:

答案 0 :(得分:1)

您需要将其分解为两个交叉表查询,这些查询通过用户提供有关audit_periods的信息,另一个查询将由用户提供audit_task信息,然后将其连接到Users表。目前尚不清楚如何在每种情况下汇总信息。例如,如果给定用户有10 audit_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