如何根据行中存在的特定值顺序排列列

时间:2015-02-02 08:57:49

标签: sql oracle oracle11g oracle10g sql-order-by

实施例: 表结构:

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

提前致谢!!

2 个答案:

答案 0 :(得分:2)

对于所需输出中的第一列和最后一列,使用 LEAST GREATEST 功能很容易。对于其他列,我会使用 CASE 构造。

编辑 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>

答案 1 :(得分:2)

可以使用三列以上的更通用的解决方案是将列拆分为单独的行,计算出值应该处于什么顺序,然后将它们转回到列中。从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 

您并不关心月份名称。然后按工资值添加行号伪列顺序:

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标记问题时,这一点并不清楚 - 您必须手动取消转动和转动,这有点啰嗦; to unpivot:

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。如果列数增加,现在需要更改三个位置,但这仍然相当简单。