如何从此查询中选择最新日期(不在现有表格中)?

时间:2017-07-27 02:40:14

标签: php mysql sql sql-server

我想展示每项任务的最新动作。这是表格(虚拟数据但结构相同):

//t_task
task_id             task_name
     A1              PC  Proc
     A2         Printer  Proc
     A3       Stationery Proc

//t_task_d
task_id           assigned_to
     A1                  John
     A1                 Sally
     A2                  John
     A3                 Sally

//t_act
no       act_id       act_date     task_id
 1           C1     2017-07-10          A1
 2           C2     2017-07-14          A1
 3           C3     2017-07-17          A1
 4           C1     2017-07-21          A2

//t_act_d
act_id       act_name
    C1      Surveying 
    C2       Contract
    C3      Execution

从上表中,我想创建某种报告。这是我的预期输出:

no            task_name   dates_of_act       status
 1              PC Proc     2017-07-17    Execution
 2         Printer Proc     2017-07-21    Surveying
 3      Stationery Proc         -           Pending /*if it's NULL, then it should be pending, but I can change this in the PHP section*/

这是我最接近的当前查询:

SELECT 
    t_task.task_name, 
    DATE(t_act.act_date) AS 'dates_of_act', 
    t_act_d.act_name 
FROM t_task 

INNER JOIN t_task_d ON t_task.task_id = t_task_d.task_id
LEFT OUTER JOIN t_act ON t_task.task_id = t_act.task_id 
LEFT OUTER JOIN t_act_d ON t_act.act_id = t_act_d.act_id

GROUP BY t_task.task_id 
ORDER BY t_act.act_date ASC

我的查询结果是:

no            task_name   dates_of_act       status
 1              PC Proc     2017-07-10    Surveying
 2         Printer Proc     2017-07-21    Surveying
 3      Stationery Proc         -           Pending

注意
我更喜欢速度,因为数据量很大。我也尽量避免使用子查询

1 个答案:

答案 0 :(得分:1)

我认为这样做。

select
  t_task.task_id,
  t_task.task_name,
  latest_action.act_date,
  IFNULL(t_act_d.act_name, 'Pending') as act_name
from
  t_task
  left outer join (
    select
      @row_num := IF(@prev_value=concat_ws('', t_act.task_id),@row_num+1, 1) as row_number,
      t_act.task_id,
      t_act.act_id,
      t_act.act_date,
      @prev_value := concat_ws('', t_act.task_id) as z
    from
      t_act,
      (select @row_num := 1) x,
      (select @prev_value := '') y
    order by
      t_act.task_id,
      t_act.act_date desc
  ) as latest_action on
    t_task.task_id = latest_action.task_id
  left outer join t_act_d on
    latest_action.act_id = t_act_d.act_id
where
  latest_action.row_number = 1 or
  latest_action.row_number is null
order by
  case when latest_action.act_date is null then '9999-01-01' else latest_action.act_date end

您提供的数据的结果是:

+---------+-----------------+------------+-----------+
| task_id | task_name       | act_date   | act_name  |
+---------+-----------------+------------+-----------+
| A1      | PC  Proc        | 2017-07-17 | Execution |
| A2      | Printer  Proc   | 2017-07-21 | Surveying |
| A3      | Stationery Proc | NULL       | Pending   |
+---------+-----------------+------------+-----------+

我更熟悉T-SQL,我在那里使用row_number()窗口函数。我们的想法是让row_number字段显示每行的排名,包括每个任务的最新(值1),第二个最近(值2)等操作。每个任务的最新操作都会以row_number为1结束,因此您可以通过从此latest_action子查询中对row_number = 1进行过滤来解决这些问题。

因为latest_action子查询整体运行一次,而不是每行运行一次,因此性能不会太大。不幸的是,我不能保证整个变量设置/递增的东西没有太大的性能损失,这是我第一次在MySQL中使用这个逻辑,我不知道它是多么高效。

如何重现T-SQL的row_number()功能的逻辑来自:ROW_NUMBER() in MySQL