我真的比较比较Oracle SQL中同一表中的行。我需要获得样式前后的所有更改。
我有这样的桌子:
id date name action
1 01-01-2011 Alex smth
1 05-01-2011 Alexx smth
1 07-01-2011 Alexa smth2
2 02-01-2012 Leo smth3
2 05-01-2012 Leon smth3
我需要得到这个:
id date field before after
1 05-01-2011 name Alex Alexx
1 07-01-2011 name Alexx Alexa
1 07-01-2011 action smth smth2
2 05-01-2012 name Leo Leon
我试图将表本身与其内部连接。我发现的方法(请在这里找到)可以帮助我将内在行与下一行连接起来,返回“无效数字”错误。
Mb有执行此任务的更简单方法吗? 你能帮我吗?
select t1.id from tablename t1
inner join tablename t3 on t1.id = t3.id + 1
答案 0 :(得分:3)
我首先要根据日期(按id)为每一行分配一个序数,然后将每一行与其下一行自动连接起来。一旦有了这些,就可以使用几个case
语句来生成前后数据:
WITH cte AS (
SELECT id, date, name, action,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY date ASC) AS rn
FROM mytable
)
SELECT a.id,
a.date
CASE WHEN b.name != a.name THEN 'name' ELSE 'action' END AS field,
CASE WHEN b.name != a.name THEN b.name ELSE b.action END AS before,
CASE WHEN b.name != a.name THEN a.name ELSE a.action END AS after
FROM cte b
JOIN cte a ON b.id = a.id AND b.rn = a.rn + 1
答案 1 :(得分:3)
您可以使用lag()
分析函数:
with t( id, "date", name, action ) as
(
select 1, date'2011-01-01','Alex','smth' from dual union all
select 1, date'2011-01-05','Alexx','smth' from dual union all
select 1, date'2011-01-07','Alexa','smth2' from dual union all
select 2, date'2012-01-02','Leo','smth3' from dual union all
select 2, date'2012-01-05','Leon','smth3' from dual
), t2 as
(
select t.*,
lag(name,1,null) over (partition by id order by id, "date") as lg_name,
lag(action,1,null) over (partition by id order by id, "date") as lg_action
from t
), t3 as
(
select id, "date", 'name' as field, lg_name as before, name as after
from t2 where name != lg_name
union all
select id, "date", 'action', lg_action, action
from t2 where action != lg_action
)
select * from t3 order by id, "date";
ID date FIELD BEFORE AFTER
-- --------- ----- ------ ------
1 05-JAN-11 name Alex Alexx
1 07-JAN-11 action smth smth2
1 07-JAN-11 name Alexx Alexa
2 05-JAN-12 name Leo Leon
答案 2 :(得分:3)
LAG()
显然是正确的使用方法。但是,我将首先取消枢纽:
select id, date, field, prev_value as before, value as after
from (select id, date, field, value,
lag(value) over (partition by id, field order by date) as prev_value
from ((select id, "date", 'name' as field, name as value
from t
) union all
(select id, "date", 'action' as field, action as value
from t
)
) t
) t
where prev_value <> value;
在Oracle的最新版本中,可以使用横向联接来简化此操作:
select id, date, field, prev_value as before, value as after
from (select t.id, t.date, x.field, x.value,
lag(x.value) over (partition by t.id, x.field order by date) as prev_value
from t cross join lateral
(select 'name' as field, name as value from dual union all
select 'action', action from dual
) x
) t
where prev_value <> value;
答案 3 :(得分:1)
由于您有两个要比较的字段,因此我使用UNION ALL进行了以下操作:
WITH CTE AS (
SELECT
ID,
DATE1,
NAME1,
ACTION,
ROW_NUMBER() OVER(
PARTITION BY ID
ORDER BY
DATE1 ASC
) AS RN
FROM
MYTABLE
)
--
SELECT
A.ID,
B.DATE1,
'name' AS FIELD,
A.NAME1 AS BEFORE,
B.NAME1 AS AFTER
FROM
CTE A
JOIN CTE B ON B.ID = A.ID
AND B.RN = A.RN + 1
AND B.NAME1 != A.NAME1
UNION ALL
SELECT
A.ID,
B.DATE1,
'action' AS FIELD,
A.ACTION AS BEFORE,
B.ACTION AS AFTER
FROM
CTE A
JOIN CTE B ON B.ID = A.ID
AND B.RN = A.RN + 1
AND B.ACTION != A.ACTION
ORDER BY
ID,
DATE1
输出:
干杯!