Sql Oracle 11g-从列到行

Sql Oracle 11g-从列到行,sql,oracle11g,Sql,Oracle11g,我的系统格式中的数据无法更改,如下所示: Row, C001, C002, C003, to C029 (Columns for FY values) 1, Name, 0910, 1011 2, Eqt1 (Speed), 60, 100 3, Eqt1 (Cost), 20, 30 4, Eqt2 (Speed), 50, 60 5, Eqt2 (Cost), 30, 45 我需要将此更改为: Name, Sta

我的系统格式中的数据无法更改,如下所示:

Row, C001,         C002, C003, to C029  (Columns for FY values)
1,   Name,         0910, 1011 
2,   Eqt1 (Speed), 60,   100
3,   Eqt1 (Cost),  20,   30 
4,   Eqt2 (Speed), 50,   60
5,   Eqt2 (Cost),  30,   45
我需要将此更改为:

Name, Start_Date,  End_Date,    Speed, Cost
Eqt1, 01-APR-2009, 30-MAR-2010, 60,    20
Eqt1, 01-APR-2010, 30-MAR-2011, 100,   30
Eqt2, 01-APR-2009, 30-MAR-2010, 50,    30
Eqt2, 01-APR-2010, 30-MAR-2011, 60,    45
我可以使用一个子选择来分割日期,其中row=1。 我可以用这个名字来代替速度成本。 但我不能把它弄对

  WITH survey_query AS  (
      SELECT    *
      FROM  tbl_data
  )
  SELECT    (CASE WHEN upper(sq.c001) LIKE '%FLEET SIZE%' THEN TRIM(REPLACE(upper(sq.c001), 'FLEET SIZE', ''))
                WHEN upper(sq.c001) LIKE '%FLYING HOURS%' THEN TRIM(REPLACE(upper(sq.c001), 'FLYING HOURS', ''))
           END) equipment_name
         ,(select TO_DATE(2000+dbms_lob.substr(c002,2,1)||'0101', 'yymmdd') FROM survey_query where line = 1) start_date
         ,(select TO_DATE(2000+dbms_lob.substr(c002,2,4)||'0330', 'yymmdd') FROM survey_query where line = 1) end_date
          ,(case when UPPER(sq.c001) like '%FLEET SIZE%' THEN sq.c002 END) fleet_size
          ,(case when UPPER(sq.c001) like '%FLYING HOURS%' THEN sq.c002 END) flying_hours
  FROM  survey_query sq
  WHERE line > 1
 UNION
   SELECT   (CASE WHEN upper(sq.c001) LIKE '%FLEET SIZE%' THEN TRIM(REPLACE(upper(sq.c001), 'FLEET SIZE', ''))
                WHEN upper(sq.c001) LIKE '%FLYING HOURS%' THEN TRIM(REPLACE(upper(sq.c001), 'FLYING HOURS', ''))
           END) equipment_name
         ,(select TO_DATE(2000+dbms_lob.substr(c003,2,1)||'0101', 'yymmdd') FROM survey_query where line = 1) start_date
         ,(select TO_DATE(2000+dbms_lob.substr(c003,2,4)||'0330', 'yymmdd') FROM survey_query where line = 1) end_date
          ,(case when UPPER(sq.c001) like '%FLEET SIZE%' THEN sq.c003 END) fleet_size
          ,(case when UPPER(sq.c001) like '%FLYING HOURS%' THEN sq.c003 END) flying_hours
  FROM  survey_query sq
  WHERE line > 1;
有什么想法吗?必须有一个更好的方法,因为我有28列数据,所以27个工会会一团糟


谢谢

它不是很优雅,因为它使用了旧式的旋转,但我无法通过11g PIVOT功能解决这一问题:

with sample_data as (select 1 row#, 'Name' c001, 0910 c002, 1011 c003, 1112 c004 from dual union all
                     select 2 row#, 'Eqt1 (Speed)' c001, 60 c002, 100 c003, 140 c004 from dual union all
                     select 3 row#, 'Eqt1 (Cost)' c001, 20 c002, 30 c003, 80 c004 from dual union all
                     select 4 row#, 'Eqt2 (Speed)' c001, 50 c002, 60 c003, 70 c004 from dual union all
                     select 5 row#, 'Eqt2 (Cost)' c001, 30 c002, 45 c003, 56 c004 from dual),
-- end of mimicking your table as a subquery called "sample_data"
-- you wouldn't need this subquery, since you would have your own table/query to use in place
-- change the table name referred to in the res subquery below as appropriate
             res as (select row#,
                            case when c001 like '%(Speed)' then substr(c001, 1, length(c001) - 8)
                                 when c001 like '%(Cost)' then substr(c001, 1, length(c001) - 7)
                                 else c001
                            end name,
                            case when c001 like '%(Speed)' then 'Speed'
                                 when c001 like '%(Cost)' then 'Cost'
                                 else c001
                            end type,
                            to_date('01/04'||substr(first_value(lpad(c002, 4, 0)) over (order by row#), 1, 2), 'dd/mm/rr') fy1_start_date,
                            to_date('31/03'||substr(first_value(lpad(c002, 4, 0)) over (order by row#), 3, 2), 'dd/mm/rr') fy1_end_date,
                            to_date('01/04'||substr(first_value(lpad(c003, 4, 0)) over (order by row#), 1, 2), 'dd/mm/rr') fy2_start_date,
                            to_date('31/03'||substr(first_value(lpad(c003, 4, 0)) over (order by row#), 3, 2), 'dd/mm/rr') fy2_end_date,
                            to_date('01/04'||substr(first_value(lpad(c004, 4, 0)) over (order by row#), 1, 2), 'dd/mm/rr') fy3_start_date,
                            to_date('31/03'||substr(first_value(lpad(c004, 4, 0)) over (order by row#), 3, 2), 'dd/mm/rr') fy3_end_date,
                            c002,
                            c003,
                            c004
                     from   sample_data),
           dummy as (select level id
                     from   dual
                     connect by level <= 3 -- num fyears to consider
                     )
select   name,
         case when d.id = 1 then res.fy1_start_date
              when d.id = 2 then res.fy2_start_date
              when d.id = 3 then res.fy3_start_date
         end start_date,
         case when d.id = 1 then res.fy1_end_date
              when d.id = 2 then res.fy2_end_date
              when d.id = 3 then res.fy3_end_date
         end end_date,
         max(case when d.id = 1 and res.type = 'Speed' then c002
                  when d.id = 2 and res.type = 'Speed' then c003
                  when d.id = 3 and res.type = 'Speed' then c004
             end) speed,
         max(case when d.id = 1 and res.type = 'Cost' then c002
                  when d.id = 2 and res.type = 'Cost' then c003
                  when d.id = 3 and res.type = 'Cost' then c004
             end) cost
from     res
         cross join dummy d
where    res.row# != 1
group by name,
         case when d.id = 1 then res.fy1_start_date
              when d.id = 2 then res.fy2_start_date
              when d.id = 3 then res.fy3_start_date
         end,
         case when d.id = 1 then res.fy1_end_date
              when d.id = 2 then res.fy2_end_date
              when d.id = 3 then res.fy3_end_date
         end
order by name, start_date;

NAME  START_DATE  END_DATE         SPEED       COST
----- ----------- ----------- ---------- ----------
Eqt1  01-APR-2009 31-MAR-2010         60         20
Eqt1  01-APR-2010 31-MAR-2011        100         30
Eqt1  01-APR-2011 31-MAR-2012        140         80
Eqt2  01-APR-2009 31-MAR-2010         50         30
Eqt2  01-APR-2010 31-MAR-2011         60         45
Eqt2  01-APR-2011 31-MAR-2012         70         56

谢谢你的回答,他们让我思考。也很抱歉在回复中等待了很长时间,我已经下班两周了

我同意以下观点

WITH ss_query AS  (
      SELECT    *
      FROM  tbl_data
  )
  , rowgen as (select /*+materialize()*/level yr from dual connect by level <= 3)
  , dates as (select /*+materialize()*/to_date(substr(c001,1,2)||'0401','yymmdd') fy_start from ss_query where line = 2)
select equipment_name
, date_start
, date_end
, max(cost) cost
, max(speed) speed
from (
  select 
    case when upper(c001) like '%COST%' then substr(c001,1,instr(upper(c001),'COST')-2)
      when upper(c001) like '%SPEED%' then substr(c001,1,instr(upper(c001),'SPEED') -2)
    end equipment_name,
    add_months(fy_start,12 * (yr - 1)) date_start,
    add_months(fy_start,12 * (yr))-1 date_end,
    case when upper(c001) like '%SPEED%' then
      case when yr = 1 then c002
          when yr = 2 then c003
          when yr = 3 then c004
        end 
      end speed,
    case when upper(c001) like '%COST%' then
      case when yr = 1 then c002
          when yr = 2 then c003
          when yr = 3 then c004
        end 
      end cost
  from ss_query ,
        rowgen,
        dates
  where line > 2
)
where speed is not null or cost is not null
group by equipment_name, date_start, date_end
order by equipment_name, date_start;

看起来像是和的复杂组合