Oracle SELECT 查询:配对日期时折叠空值
Posted
技术标签:
【中文标题】Oracle SELECT 查询:配对日期时折叠空值【英文标题】:Oracle SELECT query: collapsing null values when pairing up dates 【发布时间】:2010-05-20 19:29:00 【问题描述】:我有以下 Oracle 查询:
SELECT id,
DECODE(state, 'Open', state_in, NULL) AS open_in,
DECODE(state, 'Not Open', state_in, NULL) AS open_out,
FROM (
SELECT id,
CASE WHEN state = 'Open'
THEN 'Open'
ELSE 'Not Open'
END AS state,
TRUNC(state_time) AS state_in
FROM ...
)
这给了我如下数据:
id open_in open_out
1 2009-03-02 00:00:00
1 2009-03-05 00:00:00
1 2009-03-11 00:00:00
1 2009-03-26 00:00:00
1 2009-03-24 00:00:00
1 2009-04-13 00:00:00
我想要的是这样的数据:
id open_in open_out
1 2009-03-02 00:00:00 2009-03-05 00:00:00
1 2009-03-11 00:00:00 2009-03-24 00:00:00
也就是说,保留所有唯一的id
/open_in
对,并与open_in
之后最早的open_out
配对。对于给定的id
,可以有任意数量的唯一open_in
值,以及任意数量的唯一open_out
值。唯一的 id
/open_in
可能没有匹配的 open_out
值,在这种情况下,open_out
应该是该行的 null
。
我觉得一些分析函数,可能是LAG
或LEAD
,在这里会很有用。也许我需要MIN
与PARTITION
一起使用。
【问题讨论】:
我使用有限的接口(没有直接的数据库访问),所以我不确定确切的版本,但根据我已经能够使用的功能,我猜可能有 10 个。我无权访问EXPLAIN PLAN
,只能访问SELECT
。
您的预期输出中可能存在拼写错误 - 不应该是 2009-03-26
而不是 24 号吗?
如果你发出“select * from v$version”并且你有权限这样做,你可以看到确切的版本。
@OMG Ponies:不,3/24 是正确的,因为它是 3/11 之后的最早日期。 3/26 在 3/11 之后首先列出,是的,但随后是 3/24 和 4/13。 @Rob:所以请帮助我,我以前尝试过,但它从来没有奏效,但它现在就奏效了。也许我们的数据库管理员很慷慨!我有 10 克。
【参考方案1】:
它可以做得更简单一点。首先让我们创建一个示例表:
SQL> create table mytable (id,state,state_time)
2 as
3 select 1, 'Open', date '2009-03-02' from dual union all
4 select 1, 'Closed', date '2009-03-05' from dual union all
5 select 1, 'Open', date '2009-03-11' from dual union all
6 select 1, 'Shut down', date '2009-03-26' from dual union all
7 select 1, 'Wiped out', date '2009-03-24' from dual union all
8 select 1, 'Demolished', date '2009-04-13' from dual
9 /
Table created.
数据等于您的选择语句的输出:
SQL> SELECT id,
2 DECODE(state, 'Open', state_in, NULL) AS open_in,
3 DECODE(state, 'Not Open', state_in, NULL) AS open_out
4 FROM (
5 SELECT id,
6 CASE WHEN state = 'Open'
7 THEN 'Open'
8 ELSE 'Not Open'
9 END AS state,
10 TRUNC(state_time) AS state_in
11 FROM mytable
12 )
13 /
ID OPEN_IN OPEN_OUT
---------- ------------------- -------------------
1 02-03-2009 00:00:00
1 05-03-2009 00:00:00
1 11-03-2009 00:00:00
1 26-03-2009 00:00:00
1 24-03-2009 00:00:00
1 13-04-2009 00:00:00
6 rows selected.
下面是稍微简单的查询:
SQL> select id
2 , min(case when state = 'Open' then state_time end) open_in
3 , min(case when state != 'Open' then state_time end) open_out
4 from ( select id
5 , state
6 , state_time
7 , max(x) over (partition by id order by state_time) grp
8 from ( select id
9 , state
10 , state_time
11 , case state
12 when 'Open' then
13 row_number() over (partition by id order by state_time)
14 end x
15 from mytable
16 )
17 )
18 group by id
19 , grp
20 order by id
21 , open_in
22 /
ID OPEN_IN OPEN_OUT
---------- ------------------- -------------------
1 02-03-2009 00:00:00 05-03-2009 00:00:00
1 11-03-2009 00:00:00 24-03-2009 00:00:00
2 rows selected.
问候, 抢。
【讨论】:
哦,那好多了!实际上,我发现了我的一个错误,如果有多个唯一的id
's,LAG
的东西就会消失并且日期不会正确对齐。 :( 我将在完整的、不受限制的查询中试用您的查询,但我认为它会起作用。谢谢!
您能添加您对查询的解释吗?我正在研究它,试图了解它在做什么,但很高兴听到你的想法。
要了解它在做什么,我建议您从内到外执行这三个部分。换句话说:最里面的查询只为“打开”记录分配不同的数字。这些数字代表组。现在应该将“未打开”记录分配给这些组之一。这是通过最大分析函数完成的。现在这六条记录的 grp 值为 1、1、3、3、3 和 3,它们在最后的 group by 中使用。希望这会有所帮助。【参考方案2】:
我认为 Stack Overflow 一定是鼓舞人心的,或者至少它可以帮助我更清晰地思考。折腾了一天,终于搞定了:
SELECT id,
open_in,
open_out
FROM (
SELECT id,
open_in,
LAG(open_out, times_opened) OVER (PARTITION BY id
ORDER BY open_out DESC
NULLS LAST) AS open_out
FROM (
SELECT id,
open_in,
open_out,
COUNT(DISTINCT open_in) OVER (PARTITION BY id)
AS times_opened
FROM (
SELECT id,
DECODE(state, 'Open', state_in, NULL) AS open_in,
DECODE(state, 'Not Open', state_in, NULL)
AS open_out
FROM (
SELECT id,
CASE WHEN state = 'Open'
THEN 'Open'
ELSE 'Not Open'
END AS state,
TRUNC(au_time) AS state_in
FROM ...
)
)
)
)
WHERE open_in IS NOT NULL
更新: 看起来这并不完全有效。它适用于我的问题中的示例,但是当有多个唯一的 id
时,LAG
的东西会发生变化并且日期并不总是对齐。 :(
【讨论】:
以上是关于Oracle SELECT 查询:配对日期时折叠空值的主要内容,如果未能解决你的问题,请参考以下文章