SQL查询返回基于多个日期列的最新记录更新(棘手?)

时间:2016-05-13 05:50:19

标签: sql ms-access-2010 access

我有一张表来跟踪一组设备的各个完成阶段的日期。我需要识别最近更新的设备(即具有最新日期戳的设备),无论哪个阶段更新,并返回设备ID,更新日期和阶段。容易,对吗?嗯,它听起来有点棘手。这是一个问题:每个阶段都表示为表中的一个不同的字段,重新设计表结构不是一个选项(事务表会使这个变得简单。)这是表结构的样子。

equip_id (text)
stage_1_process (date)
stage_2_process (date)
stage_3_process (date)
stage_4_process (date)
stage_5_process (date)

如果该字段包含日期,则认为该阶段已完成;如果该字段为空,则该阶段被视为未完成。

使用Max()函数的联合查询可以获得最新日期:

SELECT Max(dt) AS latest 
FROM (
    SELECT Max(stage_1_process) AS dt, 'Stage 1' as stage
    FROM equip_status
    UNION
    SELECT Max(stage_2_process) AS dt, 'Stage 2' as stage
    FROM equip_status
    UNION
    SELECT Max(stage_3_process) AS dt, 'Stage 3' as stage
    FROM equip_status
    UNION
    SELECT Max(stage_4_process) AS dt, 'Stage 4' as stage
    FROM equip_status
    UNION
    SELECT Max(stage_5_process) AS dt, 'Stage 5' as stage
    FROM equip_status) AS U;

添加“阶段”别名以捕获与最新日期相关联的阶段。但是,我不知道如何在顶级查询中返回它,也不知道如何捕获equip_id,因为它不能从聚合函数派生。

以下是我需要返回的结果集示例:

equip_id | stage   | latest
--------------------------------------------
A12345   | Stage 3 | 4/21/2016 3:56:39 PM

感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

您可以使用帖子的查询作为派生表加入:

SELECT t1.equip_id, 
       t2.lastest,
       CASE t2.latest
          WHEN t1.stage_1_process THEN 'Stage 1'
          WHEN t1.stage_2_process THEN 'Stage 2'
          WHEN t1.stage_3_process THEN 'Stage 3'
          WHEN t1.stage_4_process THEN 'Stage 4'
          WHEN t1.stage_5_process THEN 'Stage 5'
       END AS stage
FROM equip_status AS t1
JOIN (
  SELECT Max(dt) AS latest 
  FROM (
    SELECT Max(stage_1_process) AS dt, 'Stage 1' as stage
    FROM equip_status
    UNION
    SELECT Max(stage_2_process) AS dt, 'Stage 2' as stage
    FROM equip_status
    UNION
    SELECT Max(stage_3_process) AS dt, 'Stage 3' as stage
    FROM equip_status
    UNION
    SELECT Max(stage_4_process) AS dt, 'Stage 4' as stage
    FROM equip_status
    UNION
    SELECT Max(stage_5_process) AS dt, 'Stage 5' as stage
    FROM equip_status) AS U
) AS t2 ON t2.latest IN (t1.stage_1_process, 
                         t1.stage_2_process,
                         t1.stage_3_process,
                         t1.stage_4_process,
                         t1.stage_5_process)

这看起来相当丑陋,但这些是未正确规范化表结构的后果。

答案 1 :(得分:1)

一种方法是实现一个函数,该函数返回给定行的5个阶段的最大阶段的值:

  

来自http://allenbrowne.com/func-09.html

Function MaxOfList(ParamArray varValues()) As Variant
  Dim i As Integer        'Loop controller.
  Dim varMax As Variant   'Largest value found so far.

  varMax = Null           'Initialize to null

  For i = LBound(varValues) To UBound(varValues)
      If IsNumeric(varValues(i)) Or IsDate(varValues(i)) Then
          If varMax >= varValues(i) Then
              'do nothing
          Else
              varMax = varValues(i)
          End If
        End If
  Next

  MaxOfList = varMax 
End Function

现在您可以使用案例陈述和MaxOfList

select top 1 
    equip_id,
    latest,
    case when stage_1_process = latest then 'stage 1'
    when stage_2_process = latest then 'stage 2'
    when stage_3_process = latest then 'stage 3'
    when stage_4_process = latest then 'stage 4'
    when stage_5_process = latest then 'stage 5'
    else 'none' end
 from (
    select *, 
        MaxOfList(stage_1_process,stage_2_process,stage_3_process,
                stage_4_process,stage_5_process) as latest
    from equip_status
) t order by latest desc

答案 2 :(得分:0)

SQL Server具有PIVOT和UNPIVOT,可帮助您分别将行转换为行和列。所以,我尝试了一种UNPIVOT方法来查询你的查询,发现它产生了一个优雅的解决方案(如下所示)。它是否会比明确编写的查询提供更好的性能我不知道,但我一般都相信SQL Server开发人员的智慧,如果他们提供了简洁的手段实现特定结果,它可能已经做得很好(并且可能利用了最终用户/查询编写者无法获得的低级优化)。

这个稍微陈旧的TechNet页面(SQL Server 2008)提供了很好的解释:Using PIVOT and UNPIVOT

所以,试试这个:

 $this->load->model('user_model');
        $data => array (
          'first_name'      => $this->input->post('first_name'),
          'last_name'       => $this->input->post('last_name'),
          'active'          => $this->input->post('active'),
          'date_registered' => date('Y/m/d h:i:sa')
      );