【问题标题】:SQL Show Dates Per StatusSQL 显示每个状态的日期
【发布时间】:2016-08-15 20:51:57
【问题描述】:

我有一张看起来像这样的表格:

id    animal_id     transfer_date     status_from       status_to
-----------------------------------------------------------------
100   5265          01-Jul-2016       NULL              P
101   5265          22-Jul-2016       P                 A
102   5265          26-Jul-2016       A                 B
103   5265          06-Aug-2016       B                 A

我想创建一个视图来显示动物的运动,开始和结束日期如下:

animal_id      status      start_date        end_date
---------------------------------------------------------
5265           NULL        NULL              30-Jun-2016
5265           P           01-Jul-2016       21-Jul-2016
5265           A           22-Jul-2016       25-Jul-2016
5265           B           26-Jul-2016       05-Aug-2016
5265           A           06-Aug-2016       SYSDATE OR NULL (current status)

尽管我想提供一个我尝试过的查询,但我没有。我什至不知道要搜索什么。

【问题讨论】:

  • 假设您有一个行数较少的表,将表连接到自身并查看结果。尝试从那里解决。
  • @NoChance 大约 10k-20k 是否足够小?不过这张表增长很快。
  • 试验如何用小数据构建查询是很常见的。我并不是建议 OP 在生产中运行。
  • 考虑 LEAD/LAG 解析函数,它存在于 SQL Server 中,也许 ORACLE 也是如此。对于 SQL Server:databasejournal.com/features/mssql/…

标签: sql oracle datetime


【解决方案1】:

这样的事情可能比联接更有效。唉,我没有找到避免扫描表两次的方法。

注意:我没有使用 ORDER BY 子句(事实上,如果我有排序会很奇怪,因为我在日期上使用了 to_char 来格式化它们)。如果您在进一步处理中需要它,最好不要将日期包装在 to_char 中。

with
   input_data ( id, animal_id, transfer_date, status_from, status_to) as (
     select 100, 5265, to_date('01-Jul-2016', 'dd-Mon-yyyy'), null, 'P' from dual union all
     select 101, 5265, to_date('22-Jul-2016', 'dd-Mon-yyyy'), 'P' , 'A' from dual union all
     select 102, 5265, to_date('26-Jul-2016', 'dd-Mon-yyyy'), 'A' , 'B' from dual union all
     select 103, 5265, to_date('06-Aug-2016', 'dd-Mon-yyyy'), 'B' , 'A' from dual
   )
select animal_id,
       lag (status_to) over (partition by animal_id order by transfer_date) as status,
       to_char(lag (transfer_date) over (partition by animal_id order by transfer_date),
                                                             'dd-Mon-yyyy') as start_date,
       to_char(transfer_date - 1, 'dd-Mon-yyyy') as  end_date
  from input_data
union all
select     animal_id, 
           max(status_to) keep (dense_rank last order by transfer_date), 
           to_char(max(transfer_date), 'dd-Mon-yyyy'), 
           null
  from     input_data
  group by animal_id
;

 ANIMAL_ID STATUS START_DATE           END_DATE           
---------- ------ -------------------- --------------------
      5265                             30-Jun-2016         
      5265 P      01-Jul-2016          21-Jul-2016         
      5265 A      22-Jul-2016          25-Jul-2016         
      5265 B      26-Jul-2016          05-Aug-2016         
      5265 A      06-Aug-2016

添加:说明其工作原理。首先,有一个“WITH 子句”来从 OP 的消息中创建输入数据;这是一种标准技术,任何不熟悉 Oracle 11.1 中引入的因式子查询(CTE、WITH 子句)的人都会通过阅读它/它们来为自己(以及我们其他人!)带来很多好处。

查询将来自两个来源的行连接在一起。在一个分支中,我使用lag() 解析函数;它根据“order by”子句中的列排序,按“partition by”子句中的列对每个组内的行进行排序。因此,例如,lag(status_to) 将查看同一 animal_id 中的所有行,它会按 transfer_date 对它们进行排序,并且对于每一行,它将从 PREVIOUS 行中选择 status_to(因此“lag ”)。工会的其余部分的工作方式类似。

我对联合有第二部分...正如您在原始帖子中看到的那样,有四行,但输出必须有五行。一般来说,这表明解决方案中的某处将需要某种联合(直接且明显地如在我的解决方案中,或通过自联接或以任何其他方式)。在这里,我只是最后一个状态的另一行(仍然是“当前”)。我使用dense_rank last,在每个组中(如何在 GROUP BY 中显示),只选择transfer_date 的最后一行。

要了解查询的工作原理,首先注释掉union allselect... group by animal_id 行并运行剩下的内容可能会有所帮助。这将显示查询的第一部分的作用。然后取消注释这些行,而是注释第一部分,从第一个 select animal_idunion all(注释这两行以及两者之间的所有内容)。再次运行查询,这将只显示每个 animal_id 的最后一行。

当然,在示例中提供的 OP 只有一个animal_id;如果您愿意,您可以添加更多具有不同animal_id 的行(例如在 WITH 子句中)。只是现在partition by animal_idgroup by animal_id 变得重要了;只有一个 animal_id 就不需要它们(例如,如果所有行都已被 WHERE animal_id = 5265 在子查询中的其他位置过滤)。

添加 #2 - OP 已请求此版本的另一个版本 - 如果不需要第一行怎么办?然后查询更容易编写和阅读。下面我将不再复制 CTE(WITH 子句),并且不再将日期包含在 to_date() 中。不需要GROUP BY,而且我没有对行进行排序(但如果需要,OP 可以这样做)。

select animal_id,
       status_to as status,
       transfer_date as start_date,
       lead(transfer_date) over (partition by animal_id order by transfer_date) - 1
                                                                             as end_date
  from input_data
;

【讨论】:

  • 我尝试了您的查询,但我没有包含with 子句,我收到了这个错误:ORA-30484: missing window specification for this function,它突出显示了lag。老实说,我不知道您的查询是做什么的或问题是什么。
  • 我还尝试使用包含with 子句的查询,它似乎有效,但现在我希望将源数据作为我的实际数据。
  • @PatrickGregorio - 嗨帕特里克, - 那么您将需要阅读并熟悉“分解子查询”(CTE,“WITH 子句”)。要使用您的实际数据,请删除 select animal_id 以上的所有内容 - 这是查询实际开始的地方。我在两个地方写了input_data,这是我在 WITH 子句中的分解子查询的别名;用您的实际表名替换这两个实例。确保您的列名与您在帖子中所写的完全一致,因为我完全使用了它们。不确定您遇到的错误; lag() 有一个“默认”窗口
  • 我将在答案中添加对查询的解释。
  • 感谢详细的解释!我创建了一个Pastebin 供您查看我正在使用的内容。你能检查一下我在那里做错了什么吗?
猜你喜欢
  • 1970-01-01
  • 2016-03-04
  • 1970-01-01
  • 2011-09-30
  • 2013-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多