此餐桌之旅在汽车行驶时对其进行跟踪。
Journey Chkpt1 Chkpt2
51 AAA BBB
51 BBB CCC
51 DDD CCC
51 EEE DDD
51 EEE FFF
从段AAA-BBB到段BBB-CCC,然后到DDD-CCC,依此类推。
但是列Chkpt1和Chkpt2的顺序不正确。 我想生成一个重新排序的表,在同一旅程中,其中一列始终表示“发件人”,另一列始终表示“发件人”。因此我们可以很容易地看到这辆车正在从AAA到BBB,从BBB到CCC,从CCC到DDD(而不是从DDD到CCC)。
所需的结果将是:
Reordered Chkpt1 Chkpt2
51 AAA BBB
51 BBB CCC
51 CCC DDD *
51 DDD EEE *
51 EEE FFF
(星号仅用于标记已更改的行。)
在下一个示例中,汽车开始从Chkpt2移动到Chkpt1。
示例2:
Journey Chkpt1 Chkpt2
52 NNN MMM
52 OOO NNN
52 PPP OOO
52 PPP QQQ
52 RRR QQQ
从Chkpt2到Chkpt1,下面所有行应以相同的方式重新排序。
Reordered Chkpt1 Chkpt2
52 NNN MMM
52 OOO NNN
52 PPP OOO
52 QQQ PPP *
52 RRR QQQ
SQL是怎么做到的?
注意:以上结果对我来说足够了。 但是,或者,结果始终可以显示在“从”和“到”两列中。
Reordered2 From To
51 AAA BBB
51 BBB CCC
51 CCC DDD *
51 DDD EEE *
51 EEE FFF
52 NNN MMM
52 OOO NNN
52 PPP OOO
52 QQQ PPP *
52 RRR QQQ
预先感谢,
艾默生
答案 0 :(得分:2)
这里是执行此操作的一种方法-使用递归查询(因此需要Oracle 11.2或更高版本)。对于较早的版本,使用分层查询(CONNECT BY
)重写它应该不会太困难。
该查询标识每个旅程的两个端点,并假定“起点”是第一个终点(按字母顺序);因此,如果将端点标识为AAA和FFF,则查询将选择AAA作为起点,并选择FFF作为旅程的终点。可以在origins
分解子查询(CTE)中轻松更改。
输出按行程排序,然后按航段号(第一个,第二个等)排序。保留CHKPT列,最后两列显示相应航段的FROM和TO端点。
with
simulated_data (journey, chkpt1, chkpt2) as (
select 51, 'AAA', 'BBB' from dual union all
select 51, 'BBB', 'CCC' from dual union all
select 51, 'DDD', 'CCC' from dual union all
select 51, 'EEE', 'DDD' from dual union all
select 51, 'EEE', 'FFF' from dual union all
select 52, 'NNN', 'MMM' from dual union all
select 52, 'OOO', 'NNN' from dual union all
select 52, 'PPP', 'OOO' from dual union all
select 52, 'PPP', 'QQQ' from dual union all
select 52, 'RRR', 'QQQ' from dual
) -- select * from simulated_data; /*
, endpoints (journey, endpoint) as (
select journey, chkpt
from (
select journey, chkpt
from simulated_data
unpivot (chkpt for col in (chkpt1, chkpt2))
)
group by journey, chkpt
having count(*) = 1
) -- select * from endpoints; /*
, origins (journey, orig) as (
select journey, min(endpoint)
from endpoints
group by journey
) -- select * from origins; /*
, rec (journey, leg, chkpt1, chkpt2, leg_start, leg_end) as (
select o.journey, 1, s.chkpt1, s.chkpt2, o.orig,
case o.orig when s.chkpt1 then chkpt2 else chkpt1 end
from origins o join simulated_data s
on o.journey = s.journey
and o.orig in (s.chkpt1, chkpt2)
union all
select r.journey, leg + 1, s.chkpt1, s.chkpt2, r.leg_end,
case r.leg_end when s.chkpt1 then s.chkpt2 else s.chkpt1 end
from rec r join simulated_data s
on r.journey = s.journey
and r.leg_end in (s.chkpt1, s.chkpt2)
and r.leg_start not in (s.chkpt1, s.chkpt2)
)
select journey, leg, chkpt1, chkpt2, leg_start, leg_end
from rec
order by journey, leg
;
输出:
JOURNEY LEG CHKPT1 CHKPT2 LEG_START LEG_END
---------- ---------- ---------- ---------- ---------- ----------
51 1 AAA BBB AAA BBB
51 2 BBB CCC BBB CCC
51 3 DDD CCC CCC DDD
51 4 EEE DDD DDD EEE
51 5 EEE FFF EEE FFF
52 1 NNN MMM MMM NNN
52 2 OOO NNN NNN OOO
52 3 PPP OOO OOO PPP
52 4 PPP QQQ PPP QQQ
52 5 RRR QQQ QQQ RRR
答案 1 :(得分:2)
您将需要一列来定义顺序。我只是“发明”了一个名为LEG
的人。将其更改为您的时间戳记或任何内容。
然后,您可以使用按旅程划分的LEAD()
来检查下一行的值。如果chkpt1
等于下一行的任何检查点,则必须将其交换为chkpt2
。如果chkpt2
等于以下两个值中的任何一个,则检查点的顺序已经正确。
如有必要,将其放在CASE ... END
中以交换列。
但是,如果没有下一行,则LEAD()
将返回NULL
,并且所有相等性检查都将不匹配。但是有可能旅程的最后一行中的列也必须互换。要处理这种情况,请添加检查,并使用LAG()
和模拟逻辑将检查点与上一行的检查点进行比较。
SELECT JOURNEY,
CASE
WHEN CHKPT1 IN (LEAD(CHKPT1, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG),
LEAD(CHKPT2, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG)) THEN
CHKPT2
WHEN CHKPT2 IN (LEAD(CHKPT1, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG),
LEAD(CHKPT2, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG)) THEN
CHKPT1
WHEN CHKPT1 IN (LAG(CHKPT1, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG),
LAG(CHKPT2, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG)) THEN
CHKPT1
WHEN CHKPT2 IN (LAG(CHKPT1, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG),
LAG(CHKPT2, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG)) THEN
CHKPT2
END CHKPT1,
CASE
WHEN CHKPT1 IN (LEAD(CHKPT1, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG),
LEAD(CHKPT2, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG)) THEN
CHKPT1
WHEN CHKPT2 IN (LEAD(CHKPT1, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG),
LEAD(CHKPT2, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG)) THEN
CHKPT2
WHEN CHKPT1 IN (LAG(CHKPT1, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG),
LAG(CHKPT2, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG)) THEN
CHKPT2
WHEN CHKPT2 IN (LAG(CHKPT1, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG),
LAG(CHKPT2, 1) OVER (PARTITION BY JOURNEY
ORDER BY LEG)) THEN
CHKPT1
END CHKPT2
FROM JOURNEY;
注意:
仅在两个检查点之间的周期内失败。但是,我认为,跨越两个以上检查点的周期应该起作用。
通常可能只需要在两个检查点之间进行额外的检查就可以处理一个循环,但是在这种情况下无法推断出方向,因此必须随机采取其中的一个,不一定是正确的。
对于仅跨越两个检查点(表中仅一行)的旅程也将失败,因为LEAD()
和LAG()
都将返回NULL
。这可能需要额外检查。但是,就像只有两个检查点的循环一样,无法确定顺序。该表的数据可能是正确的,也可能是错误的,仅靠手头的给定数据是不可能的。