如何根据行中存在的值的特定顺序排列列
How to arrange columns based on a particular order of values present in the rows
示例:
Table结构:
EMP_ID SAL_MONTHWISE(JAN) SAL_MONTHWISE(FEB) SAL_MONTHWISE(MAR)
1 10000 15000 8000
2 20000 2000 10000
3 50000 60000 40000
需要新格式:按每个员工的薪水递增顺序排列
EMP_ID SAL_MONTH1 SAL_MONTH2 SAL_MONTH3
1 8000 10000 15000
2 2000 10000 20000
3 40000 50000 60000
提前致谢!!
对于所需输出中的第一列和最后一列,使用 LEAST 和 GREATEST 函数很容易做到。对于其余的其他专栏,我会使用 CASE 结构。
EDIT CASE 的 WHEN 条件应该是 AND 而不是 OR。
SQL> WITH data AS
2 ( SELECT 1 emp_id, 10000 jan, 15000 feb, 8000 mar FROM dual
3 UNION ALL
4 SELECT 2 , 20000 , 2000 , 10000 FROM dual
5 UNION ALL
6 SELECT 3 , 50000 , 60000 , 40000 FROM dual
7 )
8 SELECT emp_id,
9 least(jan, feb, mar) jan,
10 CASE
11 WHEN jan > least(jan, feb, mar)
12 AND jan < greatest(jan, feb, mar)
13 THEN jan
14 WHEN feb > least(jan, feb, mar)
15 AND feb < greatest(jan, feb, mar)
16 THEN feb
17 WHEN mar > least(jan, feb, mar)
18 AND mar < greatest(jan, feb, mar)
19 THEN mar
20 END feb,
21 greatest(jan, feb, mar) mar
22 FROM DATA
23 /
EMP_ID JAN FEB MAR
---------- ---------- ---------- ----------
1 8000 10000 15000
2 2000 10000 20000
3 40000 50000 60000
SQL>
一个适用于三列以上的更通用的解决方案是将列取消旋转成单独的行,计算出值的顺序,然后将它们旋转回列中。从 11g 开始,您可以使用
取消旋转
select * from your_table
unpivot (sal for month in (sal_jan as 'Jan', sal_feb as 'Feb', sal_mar as 'Mar'));
这给你:
EMP_ID MONTH SAL
---------- ----- ----------
1 Jan 10000
1 Feb 15000
1 Mar 8000
2 Jan 20000
2 Feb 2000
2 Mar 10000
3 Jan 50000
3 Feb 60000
3 Mar 40000
您并不真正关心月份名称。然后按工资值添加行号伪列order:
select t.*, row_number() over (partition by emp_id order by sal) as rn
from your_table
unpivot (sal for month in (sal_jan as 'Jan', sal_feb as 'Feb', sal_mar as 'Mar')) t;
然后向后旋转:
select * from (
select t.emp_id, t.sal,
row_number() over (partition by emp_id order by sal) as rn
from your_table
unpivot (sal for month in (sal_jan as 'Jan', sal_feb as 'Feb', sal_mar as 'Mar')) t
)
pivot (max(sal) as sal for (rn) in (1 as "1", 2 as "2", 3 as "3"))
order by emp_id;
EMP_ID 1_SAL 2_SAL 3_SAL
---------- ---------- ---------- ----------
1 8000 10000 15000
2 2000 10000 20000
3 40000 50000 60000
SQL Fiddle demo。这很容易扩展到更多列,只需将值对添加到 pivot 和 unpivot 部分的 in
子句即可。
如果您使用的是较早的版本——当您用 11g 和 10g 标记问题时不清楚——您必须手动取消旋转和旋转,这有点冗长;逆轴旋转:
with unpivot_data as (
select level as unpivot_rn from dual connect by level <= 3
)
select t.emp_id,
case ud.unpivot_rn
when 1 then t.sal_jan
when 2 then t.sal_feb
when 3 then t.sal_mar
end as sal
from t42 t
cross join unpivot_data ud;
... connect by
中的行数和 case when
子句的数量是您拥有的列数;然后根据薪水添加行号:
with unpivot_data as (
select level as unpivot_rn from dual connect by level <= 3
),
tmp_data as (
select t.emp_id,
case ud.unpivot_rn
when 1 then t.sal_jan
when 2 then t.sal_feb
when 3 then t.sal_mar
end as sal
from t42 t
cross join unpivot_data ud
)
select td.emp_id, td.sal,
row_number() over (partition by td.emp_id order by td.sal) as rn
from tmp_data td;
然后使用 max(case ...)
和 group by
:
with unpivot_data as (
select level as unpivot_rn from dual connect by level <= 3
),
tmp_data as (
select t.emp_id,
case ud.unpivot_rn
when 1 then t.sal_jan
when 2 then t.sal_feb
when 3 then t.sal_mar
end as sal
from your_table t
cross join unpivot_data ud
),
tmp_with_rn as (
select td.emp_id, td.sal,
row_number() over (partition by td.emp_id order by td.sal) as rn
from tmp_data td
)
select twr.emp_id,
max(case when twr.rn = 1 then twr.sal end) as month_1,
max(case when twr.rn = 2 then twr.sal end) as month_2,
max(case when twr.rn = 3 then twr.sal end) as month_3
from tmp_with_rn twr
group by twr.emp_id
order by emp_id;
EMP_ID MONTH_1 MONTH_2 MONTH_3
---------- ---------- ---------- ----------
1 8000 10000 15000
2 2000 10000 20000
3 40000 50000 60000
SQL Fiddle demo。如果列数增加,现在需要更改三个地方,但它仍然相当简单。
示例: Table结构:
EMP_ID SAL_MONTHWISE(JAN) SAL_MONTHWISE(FEB) SAL_MONTHWISE(MAR)
1 10000 15000 8000
2 20000 2000 10000
3 50000 60000 40000
需要新格式:按每个员工的薪水递增顺序排列
EMP_ID SAL_MONTH1 SAL_MONTH2 SAL_MONTH3
1 8000 10000 15000
2 2000 10000 20000
3 40000 50000 60000
提前致谢!!
对于所需输出中的第一列和最后一列,使用 LEAST 和 GREATEST 函数很容易做到。对于其余的其他专栏,我会使用 CASE 结构。
EDIT CASE 的 WHEN 条件应该是 AND 而不是 OR。
SQL> WITH data AS
2 ( SELECT 1 emp_id, 10000 jan, 15000 feb, 8000 mar FROM dual
3 UNION ALL
4 SELECT 2 , 20000 , 2000 , 10000 FROM dual
5 UNION ALL
6 SELECT 3 , 50000 , 60000 , 40000 FROM dual
7 )
8 SELECT emp_id,
9 least(jan, feb, mar) jan,
10 CASE
11 WHEN jan > least(jan, feb, mar)
12 AND jan < greatest(jan, feb, mar)
13 THEN jan
14 WHEN feb > least(jan, feb, mar)
15 AND feb < greatest(jan, feb, mar)
16 THEN feb
17 WHEN mar > least(jan, feb, mar)
18 AND mar < greatest(jan, feb, mar)
19 THEN mar
20 END feb,
21 greatest(jan, feb, mar) mar
22 FROM DATA
23 /
EMP_ID JAN FEB MAR
---------- ---------- ---------- ----------
1 8000 10000 15000
2 2000 10000 20000
3 40000 50000 60000
SQL>
一个适用于三列以上的更通用的解决方案是将列取消旋转成单独的行,计算出值的顺序,然后将它们旋转回列中。从 11g 开始,您可以使用
取消旋转select * from your_table
unpivot (sal for month in (sal_jan as 'Jan', sal_feb as 'Feb', sal_mar as 'Mar'));
这给你:
EMP_ID MONTH SAL
---------- ----- ----------
1 Jan 10000
1 Feb 15000
1 Mar 8000
2 Jan 20000
2 Feb 2000
2 Mar 10000
3 Jan 50000
3 Feb 60000
3 Mar 40000
您并不真正关心月份名称。然后按工资值添加行号伪列order:
select t.*, row_number() over (partition by emp_id order by sal) as rn
from your_table
unpivot (sal for month in (sal_jan as 'Jan', sal_feb as 'Feb', sal_mar as 'Mar')) t;
然后向后旋转:
select * from (
select t.emp_id, t.sal,
row_number() over (partition by emp_id order by sal) as rn
from your_table
unpivot (sal for month in (sal_jan as 'Jan', sal_feb as 'Feb', sal_mar as 'Mar')) t
)
pivot (max(sal) as sal for (rn) in (1 as "1", 2 as "2", 3 as "3"))
order by emp_id;
EMP_ID 1_SAL 2_SAL 3_SAL
---------- ---------- ---------- ----------
1 8000 10000 15000
2 2000 10000 20000
3 40000 50000 60000
SQL Fiddle demo。这很容易扩展到更多列,只需将值对添加到 pivot 和 unpivot 部分的 in
子句即可。
如果您使用的是较早的版本——当您用 11g 和 10g 标记问题时不清楚——您必须手动取消旋转和旋转,这有点冗长;逆轴旋转:
with unpivot_data as (
select level as unpivot_rn from dual connect by level <= 3
)
select t.emp_id,
case ud.unpivot_rn
when 1 then t.sal_jan
when 2 then t.sal_feb
when 3 then t.sal_mar
end as sal
from t42 t
cross join unpivot_data ud;
... connect by
中的行数和 case when
子句的数量是您拥有的列数;然后根据薪水添加行号:
with unpivot_data as (
select level as unpivot_rn from dual connect by level <= 3
),
tmp_data as (
select t.emp_id,
case ud.unpivot_rn
when 1 then t.sal_jan
when 2 then t.sal_feb
when 3 then t.sal_mar
end as sal
from t42 t
cross join unpivot_data ud
)
select td.emp_id, td.sal,
row_number() over (partition by td.emp_id order by td.sal) as rn
from tmp_data td;
然后使用 max(case ...)
和 group by
:
with unpivot_data as (
select level as unpivot_rn from dual connect by level <= 3
),
tmp_data as (
select t.emp_id,
case ud.unpivot_rn
when 1 then t.sal_jan
when 2 then t.sal_feb
when 3 then t.sal_mar
end as sal
from your_table t
cross join unpivot_data ud
),
tmp_with_rn as (
select td.emp_id, td.sal,
row_number() over (partition by td.emp_id order by td.sal) as rn
from tmp_data td
)
select twr.emp_id,
max(case when twr.rn = 1 then twr.sal end) as month_1,
max(case when twr.rn = 2 then twr.sal end) as month_2,
max(case when twr.rn = 3 then twr.sal end) as month_3
from tmp_with_rn twr
group by twr.emp_id
order by emp_id;
EMP_ID MONTH_1 MONTH_2 MONTH_3
---------- ---------- ---------- ----------
1 8000 10000 15000
2 2000 10000 20000
3 40000 50000 60000
SQL Fiddle demo。如果列数增加,现在需要更改三个地方,但它仍然相当简单。