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

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

提前致谢!!

对于所需输出中的第一列和最后一列,使用 LEASTGREATEST 函数很容易做到。对于其余的其他专栏,我会使用 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。如果列数增加,现在需要更改三个地方,但它仍然相当简单。