【问题标题】:How to arrange columns based on a particular order of values present in the rows如何根据行中存在的特定值顺序排列列
【发布时间】:2015-02-02 08:57:49
【问题描述】:

示例: 表结构:

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

提前致谢!!

【问题讨论】:

  • 新格式表示,是否从主表插入到另一个表??
  • 我想要 select 语句中的新格式
  • 使用LEASTGREATEST函数。
  • @Lalit Kumar B 你能解释一下如何在选择查询中做到这一点吗?
  • 您应该使用 plsql 进行上述操作。

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


【解决方案1】:

适用于超过三列的更通用的解决方案是将列取消旋转到单独的行中,计算出值的顺序,然后将它们旋转回列中。从 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 标记问题时不清楚 - 您必须手动取消旋转和旋转,这有点冗长;取消透视:

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 子句中的行数和when 子句中的case 数是您拥有的列数;然后根据工资添加行号:

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

【讨论】:

  • 它会扼杀性能。是的,它是通用的,但是,明确编码 12 个月并不是什么大问题。
【解决方案2】:

对于所需输出中的第一列和最后一列,使用 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>

【讨论】:

  • 这只会给出第一个和最后一个,这里仍然不能处理其他值。
  • 谢谢@Lalit Kumar B
  • @Exhausted,不要说 cannot 。查看更新后的答案。
  • @LalitKumarB i 并不意味着实际不能,但是当你这样处理时,当有 n 列时代码会变得混乱。除非有通用脚本来处理所有这些事情。
  • 口头禅是,如果你可以在普通 SQL 中做同样的事情,那就永远不要在 PL/SQL 中做 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 2021-12-19
相关资源
最近更新 更多