Oracle 11.2 SQL - 帮助压缩有序集中的数据
Oracle 11.2 SQL - help to condense data in ordered set
我有一个包含时间戳列和多个标识符列的数据集。当按时间戳排序时,我想将具有相同标识符的相邻行的每个 "block" 压缩为一行。需要每个块的最小和最大时间戳。
源数据:
TSTAMP ID1 ID2
t1 A B <= start of new block
t2 A B
t3 C D <= start of new block
t4 E F <= start of new block
t5 E F
t6 E F
t7 A B <= start of new block
t8 G H <= start of new block
期望的结果:
MIN_TSTAMP MAX_TSTAMP ID1 ID2
t1 t2 A B
t3 t3 C D
t4 t6 E F
t7 t7 A B
t8 t8 G H
我认为这对于 window-ing 分析函数来说已经成熟,但如果不对 IDn
的所有相等组合进行分组,我就无法进行分区 - 而不是仅按时间戳排序的相邻行中的组合。
一种解决方法是首先在一个内联视图中创建一个键列,我可以稍后对其进行分组,即块中的每一行具有相同的值,而每个块具有不同的值。我可以使用 LAG 分析函数来比较行值,然后将 PL/SQL 函数调用到序列的 return nextval/currval 值(直接在 [=43 中调用 nextval/currval =] 在此上下文中受到限制)。
select min(ilv.tstamp), max(ilv.tstamp), id1, id2
from (
select case when (id1 != lag(id1,1,'*') over (partition by (1) order by tstamp)
or id2 != lag(id2,1,'*') over (partition by (1) order by tstamp))
then
pk_seq_utils.gav_get_nextval
else
pk_seq_utils.gav_get_currval
end ident, t.*
from tab1 t
order by tstamp) ilv
group by ident, id1, id2
order by 1;
其中 gav_get_xxx
的功能只是 return currval/nextval 来自一个序列。
但我只想使用 SQL 并避免使用 PL/SQL(因为我也可以在 PL/SQL 中轻松地编写它并从管道函数中输出结果行) .
有什么想法吗?
谢谢。
你需要一步一步来:
- 用 LAG 检测 ID 变化,用标志 = 1 标记每个变化。
- 使用 SUM 对 ID 更改标志(总共 运行 个)为组(即具有相同 ID 的相邻记录)生成键。
- 按生成的组密钥分组并获得 min/max 时间戳。
查询:
select
min(tstamp) as min_tstamp,
max(tstamp) as max_tstamp,
min(id1) as id1,
min(id2) as id2
from
(
select
grouped.*,
sum(newgroup) over (order by tstamp) as groupkey
from
(
select
mytable.*,
case when id1 <> lag(id1) over (order by tstamp)
or id2 <> lag(id2) over (order by tstamp)
then 1 else 0 end as newgroup
from mytable
order by tstamp
) grouped
)
group by groupkey
order by groupkey;
Tabibitosan 救援!
with sample_data as (select 't1' tstamp, 'A' id1, 'B' id2 from dual union all
select 't2' tstamp, 'A' id1, 'B' id2 from dual union all
select 't3' tstamp, 'C' id1, 'D' id2 from dual union all
select 't4' tstamp, 'E' id1, 'F' id2 from dual union all
select 't5' tstamp, 'E' id1, 'F' id2 from dual union all
select 't6' tstamp, 'E' id1, 'F' id2 from dual union all
select 't7' tstamp, 'A' id1, 'B' id2 from dual union all
select 't8' tstamp, 'G' id1, 'H' id2 from dual)
select min(tstamp) min_tstamp, max(tstamp) max_tstamp, id1, id2
from (select tstamp,
id1,
id2,
row_number() over (order by tstamp) - row_number() over (partition by id1, id2 order by tstamp) grp
from sample_data)
group by id1,
id2,
grp
order by min(tstamp);
MIN_TSTAMP MAX_TSTAMP ID1 ID2
---------- ---------- --- ---
t1 t2 A B
t3 t3 C D
t4 t6 E F
t7 t7 A B
t8 t8 G H
您应该可以使用 row_number
window 函数来执行此操作,如下所示:
select
min(tstamp) mints, max(tstamp) maxts, id1, id2
from (
select
*,
row_number() over (order by tstamp)
- row_number() over (partition by id1, id2 order by tstamp) as rn
from t
) as subq
group by id1, id2, rn
order by rn
我无法使用任何 Oracle 数据库对其进行测试,但它可以与 MSSQL 一起使用并且应该也可以在 Oracle 中使用,因为 window 函数的工作方式相同。
您可以使用 an analytic 'trick' 来识别间隙和孤岛,比较所有行中每一行的位置与 tstamp
的位置及其与 tstamp
的位置只是为了 id2, id2
组合:
select tstamp, id1, id2,
row_number() over (partition by id1, id2 order by tstamp)
- row_number() over (order by tstamp) as block_id
from tab1;
TS I I BLOCK_ID
-- - - ----------
t1 A B 0
t2 A B 0
t3 C D -2
t4 E F -3
t5 E F -3
t6 E F -3
t7 A B -4
t8 G H -7
block_id
的实际值并不重要,只是它对于组合的每个块都是唯一的。然后您可以使用它进行分组:
select min(tstamp) as min_tstamp, max(tstamp) as max_tstamp, id1, id2
from (
select tstamp, id1, id2,
row_number() over (partition by id1, id2 order by tstamp)
- row_number() over (order by tstamp) as block_id
from tab1
)
group by id1, id2, block_id
order by min(tstamp);
MI MA I I
-- -- - -
t1 t2 A B
t3 t3 C D
t4 t6 E F
t7 t7 A B
t8 t8 G H
我有一个包含时间戳列和多个标识符列的数据集。当按时间戳排序时,我想将具有相同标识符的相邻行的每个 "block" 压缩为一行。需要每个块的最小和最大时间戳。
源数据:
TSTAMP ID1 ID2
t1 A B <= start of new block
t2 A B
t3 C D <= start of new block
t4 E F <= start of new block
t5 E F
t6 E F
t7 A B <= start of new block
t8 G H <= start of new block
期望的结果:
MIN_TSTAMP MAX_TSTAMP ID1 ID2
t1 t2 A B
t3 t3 C D
t4 t6 E F
t7 t7 A B
t8 t8 G H
我认为这对于 window-ing 分析函数来说已经成熟,但如果不对 IDn
的所有相等组合进行分组,我就无法进行分区 - 而不是仅按时间戳排序的相邻行中的组合。
一种解决方法是首先在一个内联视图中创建一个键列,我可以稍后对其进行分组,即块中的每一行具有相同的值,而每个块具有不同的值。我可以使用 LAG 分析函数来比较行值,然后将 PL/SQL 函数调用到序列的 return nextval/currval 值(直接在 [=43 中调用 nextval/currval =] 在此上下文中受到限制)。
select min(ilv.tstamp), max(ilv.tstamp), id1, id2
from (
select case when (id1 != lag(id1,1,'*') over (partition by (1) order by tstamp)
or id2 != lag(id2,1,'*') over (partition by (1) order by tstamp))
then
pk_seq_utils.gav_get_nextval
else
pk_seq_utils.gav_get_currval
end ident, t.*
from tab1 t
order by tstamp) ilv
group by ident, id1, id2
order by 1;
其中 gav_get_xxx
的功能只是 return currval/nextval 来自一个序列。
但我只想使用 SQL 并避免使用 PL/SQL(因为我也可以在 PL/SQL 中轻松地编写它并从管道函数中输出结果行) .
有什么想法吗?
谢谢。
你需要一步一步来:
- 用 LAG 检测 ID 变化,用标志 = 1 标记每个变化。
- 使用 SUM 对 ID 更改标志(总共 运行 个)为组(即具有相同 ID 的相邻记录)生成键。
- 按生成的组密钥分组并获得 min/max 时间戳。
查询:
select
min(tstamp) as min_tstamp,
max(tstamp) as max_tstamp,
min(id1) as id1,
min(id2) as id2
from
(
select
grouped.*,
sum(newgroup) over (order by tstamp) as groupkey
from
(
select
mytable.*,
case when id1 <> lag(id1) over (order by tstamp)
or id2 <> lag(id2) over (order by tstamp)
then 1 else 0 end as newgroup
from mytable
order by tstamp
) grouped
)
group by groupkey
order by groupkey;
Tabibitosan 救援!
with sample_data as (select 't1' tstamp, 'A' id1, 'B' id2 from dual union all
select 't2' tstamp, 'A' id1, 'B' id2 from dual union all
select 't3' tstamp, 'C' id1, 'D' id2 from dual union all
select 't4' tstamp, 'E' id1, 'F' id2 from dual union all
select 't5' tstamp, 'E' id1, 'F' id2 from dual union all
select 't6' tstamp, 'E' id1, 'F' id2 from dual union all
select 't7' tstamp, 'A' id1, 'B' id2 from dual union all
select 't8' tstamp, 'G' id1, 'H' id2 from dual)
select min(tstamp) min_tstamp, max(tstamp) max_tstamp, id1, id2
from (select tstamp,
id1,
id2,
row_number() over (order by tstamp) - row_number() over (partition by id1, id2 order by tstamp) grp
from sample_data)
group by id1,
id2,
grp
order by min(tstamp);
MIN_TSTAMP MAX_TSTAMP ID1 ID2
---------- ---------- --- ---
t1 t2 A B
t3 t3 C D
t4 t6 E F
t7 t7 A B
t8 t8 G H
您应该可以使用 row_number
window 函数来执行此操作,如下所示:
select
min(tstamp) mints, max(tstamp) maxts, id1, id2
from (
select
*,
row_number() over (order by tstamp)
- row_number() over (partition by id1, id2 order by tstamp) as rn
from t
) as subq
group by id1, id2, rn
order by rn
我无法使用任何 Oracle 数据库对其进行测试,但它可以与 MSSQL 一起使用并且应该也可以在 Oracle 中使用,因为 window 函数的工作方式相同。
您可以使用 an analytic 'trick' 来识别间隙和孤岛,比较所有行中每一行的位置与 tstamp
的位置及其与 tstamp
的位置只是为了 id2, id2
组合:
select tstamp, id1, id2,
row_number() over (partition by id1, id2 order by tstamp)
- row_number() over (order by tstamp) as block_id
from tab1;
TS I I BLOCK_ID
-- - - ----------
t1 A B 0
t2 A B 0
t3 C D -2
t4 E F -3
t5 E F -3
t6 E F -3
t7 A B -4
t8 G H -7
block_id
的实际值并不重要,只是它对于组合的每个块都是唯一的。然后您可以使用它进行分组:
select min(tstamp) as min_tstamp, max(tstamp) as max_tstamp, id1, id2
from (
select tstamp, id1, id2,
row_number() over (partition by id1, id2 order by tstamp)
- row_number() over (order by tstamp) as block_id
from tab1
)
group by id1, id2, block_id
order by min(tstamp);
MI MA I I
-- -- - -
t1 t2 A B
t3 t3 C D
t4 t6 E F
t7 t7 A B
t8 t8 G H