【DB筆試面試467】Oracle中行列互換有哪些方法?

  • 2019 年 10 月 10 日
  • 筆記

題目部分

Oracle中行列互換有哪些方法?

答案部分

行列轉換包括以下六種情況:(1)列轉行。(2)行轉列。(3)多列轉換成字元串。(4)多行轉換成字元串。(5)字元串轉換成多列。(6)字元串轉換成多行。其中,重點是行轉列和字元串轉換成多行。

下面將分別對這幾種情況舉例來說明。

1、列轉行

列轉行就是將原表中的列名作為轉換後的表的內容。列轉行主要採用UNION ALL來完成。示例程式碼如下所示:

CREATE TABLE TEST_LHR  (    NAME     VARCHAR2(255),    JANUARY  NUMBER(18),    FEBRUARY NUMBER(18),    MARCH    NUMBER(18),    APRIL    NUMBER(18),    MAY      NUMBER(18)  );  INSERT INTO TEST_LHR (NAME, JANUARY, FEBRUARY, MARCH, APRIL, MAY)  VALUES ('長壽', 58, 12, 26, 18, 269);  INSERT INTO TEST_LHR (NAME, JANUARY, FEBRUARY, MARCH, APRIL, MAY)  VALUES ('璧山', 33, 18, 17, 16, 206);  INSERT INTO TEST_LHR (NAME, JANUARY, FEBRUARY, MARCH, APRIL, MAY)  VALUES ('楊家坪', 72, 73, 79, 386, 327);  INSERT INTO TEST_LHR (NAME, JANUARY, FEBRUARY, MARCH, APRIL, MAY)  VALUES ('巫溪', 34, 9, 7, 21, 33);  INSERT INTO TEST_LHR (NAME, JANUARY, FEBRUARY, MARCH, APRIL, MAY)  VALUES ('豐都', 62, 46, 39, 36, 91);  INSERT INTO TEST_LHR (NAME, JANUARY, FEBRUARY, MARCH, APRIL, MAY)  VALUES ('武隆', 136, 86, 44, 52, 142);  COMMIT;  SELECT * FROM TEST_LHR;  

查詢結果如下所示:

下面進行列轉換:

SELECT *    FROM (SELECT T.NAME, 'JANUARY' MONTH, T.JANUARY V_NUM            FROM TEST_LHR T          UNION ALL          SELECT T.NAME, 'FEBRUARY' MONTH, T.FEBRUARY V_NUM            FROM TEST_LHR T          UNION ALL          SELECT T.NAME, 'MARCH' MONTH, T.MARCH V_NUM            FROM TEST_LHR T          UNION ALL          SELECT T.NAME, 'APRIL' MONTH, T.APRIL V_NUM            FROM TEST_LHR T          UNION ALL          SELECT T.NAME, 'MAY' MONTH, T.MAY V_NUM            FROM TEST_LHR T)   ORDER BY NAME;  

查詢結果如下所示:

列轉行也可以使用unpivot函數,如下所示:

drop table test purge;  create table test as  SELECT *  FROM   (SELECT e.deptno,                 e.sal          FROM   scott.emp e)  pivot(COUNT(*) AS cnt, SUM(sal) AS s  FOR    deptno IN(10 AS d10, 20 AS d20, 30 AS d30))  ORDER  BY 1;    SELECT * FROM test;  

查詢結果:

SELECT * FROM test unpivot(人次 FOR deptno IN(d10_cnt, d20_cnt, d30_cnt));  

查詢結果如下所示:

SELECT deptno AS 部門編碼,         人次,         工資  FROM   test a  unpivot include nulls  (人次 FOR deptno IN(d10_cnt as 10, d20_cnt as 20, d30_cnt as 30))  unpivot include nulls  (工資 FOR deptno2 IN(d10_s as 10, d20_s as 20, d30_s as 30))   where  deptno= deptno2  ;  

查詢結果如下所示:

2、行轉列

行轉列就是將行數據內容作為列名。示例程式碼如下所示:

CREATE TABLE T_ROW_COL_LHR(  NUM VARCHAR2(15 CHAR),  NAME VARCHAR2(20 CHAR),  SEX VARCHAR2(2 CHAR),  CLASSES VARCHAR2(30 CHAR),  COURSE_NAME VARCHAR2(50 CHAR)  );    INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME)  VALUES ('206211','王藝','男','06-1班','保險學');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206212','肖薇','女','06-2','保險學');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206212','肖薇','女','06-2','財務管理');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206212','肖薇','女','06-2','財務會計');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206213','陳雅詩','女','06-2','電子商務');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206213','陳雅詩','女','06-2','公共經濟學');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206213','陳雅詩','女','06-2','公司理財');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206213','陳雅詩','女','06-2','管理學原理');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206213','陳雅詩','女','06-2','保險學');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206214','李丹陽','男','06-1','保險學');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206214','李丹陽','男','06-1','財務管理');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206214','李丹陽','男','06-1','財務會計');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206214','李丹陽','男','06-1','電子商務');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206214','李丹陽','男','06-1','公共經濟學');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206215','楊伊琳','女','06-3班','環境管理學');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206215','楊伊琳','女','06-3班','管理學原理');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206215','楊伊琳','女','06-3班','商務談判');  INSERT INTO T_ROW_COL_LHR(NUM,NAME,SEX,CLASSES,COURSE_NAME) VALUES ('206216','李佳琪','男','06-2','土地估計');  SELECT * FROM T_ROW_COL_LHR;  

查詢結果如下所示:

將COURSE_NAME進行行轉列:

SELECT NUM,NAME,SEX,CLASSES,              MAX(DECODE(RN,1,COURSE_NAME,NULL)) COURSE_NAME_1,              MAX(DECODE(RN,2,COURSE_NAME,NULL)) COURSE_NAME_2,              MAX(DECODE(RN,3,COURSE_NAME,NULL)) COURSE_NAME_3,              MAX(DECODE(RN,4,COURSE_NAME,NULL)) COURSE_NAME_4,              MAX(DECODE(RN,5,COURSE_NAME,NULL)) COURSE_NAME_5  FROM   (SELECT NUM,NAME,SEX,CLASSES,COURSE_NAME,  ROW_NUMBER() OVER(PARTITION BY NUM,NAME,SEX,CLASSES ORDER BY COURSE_NAME) RN                     FROM   T_ROW_COL_LHR)  GROUP  BY NUM,NAME,SEX,CLASSES;  

結果如下所示:

將COURSE_NAME列合併,示例程式碼如下所示:

SELECT NUM,         NAME,         SEX,         CLASSES,         (MAX(DECODE(RN, 1, COURSE_NAME, NULL)) ||         MAX(DECODE(RN, 2, ',' || COURSE_NAME, NULL)) ||         MAX(DECODE(RN, 3, ',' || COURSE_NAME, NULL)) ||         MAX(DECODE(RN, 4, ',' || COURSE_NAME, NULL)) ||         MAX(DECODE(RN, 5, ',' || COURSE_NAME, NULL))) NAME  FROM   (SELECT NUM,                 NAME,                 SEX,                 CLASSES,                 COURSE_NAME,                 ROW_NUMBER() OVER(PARTITION BY NUM, NAME, SEX, CLASSES ORDER BY COURSE_NAME) RN          FROM   T_ROW_COL_LHR)  GROUP  BY NUM,            NAME,            SEX,            CLASSES;  

也可以使用LISTAGG函數直接轉換:

SELECT NUM,NAME,         SEX,         CLASSES,         LISTAGG(COURSE_NAME, ',') WITHIN GROUP(ORDER BY COURSE_NAME DESC)  FROM   T_ROW_COL_LHR  GROUP  BY NUM,NAME,            SEX,            CLASSES;  

也可以使用VM_CONCAT函數直接轉換:

SELECT NUM,NAME,         SEX,         CLASSES,         WM_CONCAT(COURSE_NAME)  FROM   T_ROW_COL_LHR  GROUP  BY NUM,NAME,            SEX,            CLASSES;  

結果如下所示:

3、多列轉換成字元串

使用||或CONCAT函數實現,示例程式碼如下所示:

SELECT CONCAT('A','B') FROM DUAL;  

4、多行轉換成字元串

示例程式碼如下所示:

CREATE TABLE T_ROW_STR(  ID INT,  COL VARCHAR2(10)  );  INSERT INTO T_ROW_STR VALUES(1,'A');  INSERT INTO T_ROW_STR VALUES(1,'B');  INSERT INTO T_ROW_STR VALUES(1,'C');  INSERT INTO T_ROW_STR VALUES(2,'A');  INSERT INTO T_ROW_STR VALUES(2,'D');  INSERT INTO T_ROW_STR VALUES(2,'E');  INSERT INTO T_ROW_STR VALUES(3,'C');  COMMIT;  SELECT * FROM T_ROW_STR;  

查詢結果如下所示:

下面進行轉換:

SELECT ID,         MAX(DECODE(RN, 1, COL, NULL)) ||         MAX(DECODE(RN, 2, ',' || COL, NULL)) ||         MAX(DECODE(RN, 3, ',' || COL, NULL)) STR  FROM   (SELECT ID,                 COL,                 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY COL) AS RN          FROM   T_ROW_STR) T  GROUP  BY ID  ORDER  BY 1;  

也可以使用SYS_CONNECT_BY_PATH來實現:

SELECT T.ID ID,         MAX(SUBSTR(SYS_CONNECT_BY_PATH(T.COL, ','), 2)) STR  FROM   (SELECT ID,                 COL,                 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY COL) RN          FROM   T_ROW_STR) T  START  WITH RN = 1  CONNECT BY RN = PRIOR RN + 1      AND    ID = PRIOR ID  GROUP  BY T.ID;  

也可以使用LISTAGG函數直接轉換:

SELECT t.id,         LISTAGG(t.col,',') WITHIN GROUP (ORDER BY t.id desc )  FROM   T_ROW_STR t  GROUP  BY t.id;  

也可以使用VM_CONCAT函數直接轉換:

SELECT t.id,         WM_CONCAT(t.col)  FROM   T_ROW_STR t  GROUP  BY t.id;  

查詢結果如下所示:

5、字元串轉換成多列

實際上就是一個字元串拆分的問題。示例程式碼如下所示:

CREATE TABLE T_COL_ROW(  ID INT,  C1 VARCHAR2(10),  C2 VARCHAR2(10),  C3 VARCHAR2(10));    INSERT INTO T_COL_ROW VALUES (1, 'v11', 'v21', 'v31');  INSERT INTO T_COL_ROW VALUES (2, 'v12', 'v22', NULL);  INSERT INTO T_COL_ROW VALUES (3, 'v13', NULL, 'v33');  INSERT INTO T_COL_ROW VALUES (4, NULL, 'v24', 'v34');  INSERT INTO T_COL_ROW VALUES (5, 'v15', NULL, NULL);  INSERT INTO T_COL_ROW VALUES (6, NULL, NULL, 'v35');  INSERT INTO T_COL_ROW VALUES (7, NULL, NULL, NULL);  COMMIT;    SELECT * FROM T_COL_ROW;    CREATE TABLE T_STR_COL AS  SELECT ID,C1||','||C2||','||C3 AS C123  FROM T_COL_ROW;    SELECT * FROM T_STR_COL;  

查詢結果如下所示:

下面進行轉換:

SELECT ID,         C123,         SUBSTR(C123, 1, INSTR(C123 || ',', ',', 1, 1) - 1) C1,         SUBSTR(C123,                INSTR(C123 || ',', ',', 1, 1) + 1,                INSTR(C123 || ',', ',', 1, 2) - INSTR(C123 || ',', ',', 1, 1) - 1) C2,         SUBSTR(C123,                INSTR(C123 || ',', ',', 1, 2) + 1,                INSTR(C123 || ',', ',', 1, 3) - INSTR(C123 || ',', ',', 1, 2) - 1) C3  FROM   T_STR_COL  ORDER  BY 1;  

查詢結果如下所示:

6、字元串轉換成多行

示例程式碼如下所示:

CREATE TABLE T_STR_ROW AS  SELECT ID,         MAX(DECODE(RN, 1, COL, NULL)) ||                MAX(DECODE(RN, 2, ',' || COL, NULL)) ||                MAX(DECODE(RN, 3, ',' || COL, NULL)) STR    FROM (SELECT ID,                 COL,                 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY COL) AS RN            FROM T_ROW_STR) T    GROUP BY ID    ORDER BY 1;  SELECT * FROM T_STR_ROW;  

查詢結果如下所示:

轉換程式碼如下:

SELECT ID,         1 AS P,         SUBSTR(STR, 1, INSTR(STR || ',', ',', 1, 1) - 1) AS CV  FROM   T_STR_ROW  UNION ALL  SELECT ID,         2 AS P,         SUBSTR(STR,                INSTR(STR || ',', ',', 1, 1) + 1,                INSTR(STR || ',', ',', 1, 2) - INSTR(STR || ',', ',', 1, 1) - 1) AS CV  FROM   T_STR_ROW  UNION ALL  SELECT ID,         3 AS P,         SUBSTR(STR,                INSTR(STR || ',', ',', 1, 1) + 1,                INSTR(STR || ',', ',', 1, 2) - INSTR(STR || ',', ',', 1, 1) - 1) AS CV  FROM   T_STR_ROW  ORDER  BY 1,            2;  

查詢結果如下所示:

還有幾類特殊的轉換,如下所示:

CREATE OR REPLACE TYPE INS_SEQ_TYPE IS VARRAY(8) OF NUMBER;  SELECT * FROM TABLE(INS_SEQ_TYPE(1, 2, 3, 4, 5));  

結果:

COLUMN_VALUE  ------------             1             2             3             4             5  

若是字元串類型,則如下所示:

CREATE OR REPLACE TYPE INS_SEQ_TYPE2 IS VARRAY(80) OF VARCHAR2(32767);  SELECT * FROM TABLE(INS_SEQ_TYPE2('aadf,dea','cbc','d'));  

結果:

COLUMN_VALUE  -----------------  aadf,dea  cbc  d  

還有如下的形式:

先創建一個TYPE類型,程式碼如下:

CREATE OR REPLACE TYPE TYPE_STR_LHR IS TABLE OF VARCHAR2(32767);  

再創建FUN_SPLIT2_LHR函數,程式碼如下:

CREATE OR REPLACE FUNCTION FUN_SPLIT2_LHR(P_STR       VARCHAR2,                                   V_SPLIT VARCHAR2 DEFAULT ',') RETURN TYPE_STR_LHR IS    RS    TYPE_STR_LHR := TYPE_STR_LHR();    V_STR VARCHAR2(4000) := '';    V_LEN NUMBER := 0;  BEGIN    V_STR := P_STR;    V_LEN := LENGTH(V_SPLIT);    WHILE LENGTH(V_STR) > 0 LOOP      IF INSTR(V_STR, V_SPLIT) > 0 THEN        RS.EXTEND;        RS(RS.COUNT) := SUBSTR(V_STR, 1, INSTR(V_STR, V_SPLIT) - 1);        V_STR := SUBSTR(V_STR, INSTR(V_STR, V_SPLIT) + V_LEN);      ELSE        RS.EXTEND;        RS(RS.COUNT) := V_STR;        EXIT;      END IF;    END LOOP;    RETURN RS;    END;  

測試如下:

SQL> SELECT COLUMN_VALUE FROM TABLE(FUN_SPLIT2_LHR('101,102,103',','));    COLUMN_VALUE  ------------------  101  102  103  SQL> SELECT TO_NUMBER(COLUMN_VALUE) FROM TABLE(FUN_SPLIT2_LHR('101,102,103'));    TO_NUMBER(COLUMN_VALUE)  -----------------------                     101                     102                     103  SQL> SELECT COLUMN_VALUE FROM TABLE(FUN_SPLIT2_LHR('101@#102@#103','@#'));  COLUMN_VALUE  ---------------  101  102  103  

& 說明:

有關行列互換更多的案例可以參考我的BLOG:http://blog.itpub.net/26736162/viewspace-1272538/

真題1、資料庫中有一張如下所示的表,表名為SALES。

季度

銷售量

1991

1

11

1991

2

12

1991

3

13

1991

4

14

1992

1

21

1992

2

22

1992

3

23

1992

4

24

要求:寫一個SQL語句查詢出如下所示的結果。

一季度

二季度

三季度

四季度

1991

11

12

13

14

1992

21

22

23

24

答案:這是一道行轉列的題目,首先建立表SALES:

CREATE TABLE SALES(年 NUMBER,季度 NUMBER,銷售量 NUMBER);  INSERT INTO SALES VALUES(1991, 1 ,11);  INSERT INTO SALES VALUES(1991, 2 ,12);  INSERT INTO SALES VALUES(1991, 3 ,13);  INSERT INTO SALES VALUES(1991, 4 ,14);  INSERT INTO SALES VALUES(1992, 1 ,21);  INSERT INTO SALES VALUES(1992, 2 ,22);  INSERT INTO SALES VALUES(1992, 3 ,23);  INSERT INTO SALES VALUES(1992, 4 ,24);  SELECT * FROM SALES;  

此題若使用聚合函數+DECODE或CASE來回答,如下所示:

SELECT 年,      SUM(CASE WHEN 季度=1 THEN 銷售量 ELSE 0 END) AS 一季度,      SUM(CASE WHEN 季度=2 THEN 銷售量 ELSE 0 END) AS 二季度,      SUM(CASE WHEN 季度=3 THEN 銷售量 ELSE 0 END) AS 三季度,      SUM(CASE WHEN 季度=4 THEN 銷售量 ELSE 0 END) AS 四季度  FROM SALES  GROUP BY 年  ORDER BY T.教師號;  

此題若使用PIVOT函數,如下所示:

SELECT *    FROM SALES  PIVOT(SUM(銷售量)     FOR 季度 IN(1 AS "一季度", 2 AS "二季度", 3 AS "三季度", 4 AS "四季度"))   ORDER BY 1;  

此題若使用臨時表的方式,如下所示:

SELECT T.年,         NVL(SUM(T1.一季度),0) AS "一季度",         NVL(SUM(T2.二季度),0) AS "二季度",         NVL(SUM(T3.三季度),0) AS "三季度",         NVL(SUM(T4.四季度),0) AS "四季度"    FROM (SELECT 年,銷售量 AS "一季度" FROM SALES A WHERE A.季度 = '1') T1,         (SELECT 年,銷售量 AS "二季度" FROM SALES A WHERE A.季度 = '2') T2,         (SELECT 年,銷售量 AS "三季度" FROM SALES A WHERE A.季度 = '3') T3,         (SELECT 年,銷售量 AS "四季度" FROM SALES A WHERE A.季度 = '4') T4,         (SELECT DISTINCT 年 FROM SALES) T   WHERE T.年 = T1.年(+)     AND T.年 = T2.年(+)     AND T.年 = T3.年(+)     AND T.年 = T4.年(+)   GROUP BY T.年   ORDER BY 1;  

真題2、有如下的表格:

create table test(     id number(10) primary key,     type number(10) ,     t_id number(10),     value varchar2(6)  );    insert into test values(100,1,1,'張三');  insert into test values(200,2,1,'男');  insert into test values(300,3,1,'50');    insert into test values(101,1,2,'劉二');  insert into test values(201,2,2,'男');  insert into test values(301,3,2,'30');    insert into test values(102,1,3,'劉三');  insert into test values(202,2,3,'女');  insert into test values(302,3,3,'10');    select * from test;  

查詢結果如下所示:

根據以上程式碼生成的表寫出一條查詢語句,查詢結果如下:

姓名 性別 年齡  張三  男  50  劉二  男  30  劉三  女  10  

答案:根據表格可以分析出TYPE列中1代表姓名、2代表性別、3代表年齡,而T_ID中ID一樣的為同一個人的屬性。查詢結果中列依次為姓名、性別、年齡,而TYPE列決定姓名、性別、年齡。

方法一:使用分組,先對T_ID進行分組,然後用DECODE函數過濾數據:

SELECT MAX(decode(TYPE, 1, VALUE)) "姓名",         MAX(decode(TYPE, 2, VALUE)) "性別",         MAX(decode(TYPE, 3, VALUE)) "年齡"  FROM   test  GROUP  BY t_id;  

方法二:使用連表,通過WHERE過濾生成3張TYPE分別等於1(姓名)、2(性別)、3(年齡)的3張虛擬表,再通過WHERE連接條件三張表T_ID相等的為同一個人或者說同一條記錄(行):

SELECT t1.value "姓名",         t2.value "性別",         t3.value "年齡"  FROM   (SELECT VALUE,                 t_id          FROM   test          WHERE  TYPE = 1) t1,         (SELECT VALUE,                 t_id          FROM   test          WHERE  TYPE = 2) t2,         (SELECT VALUE,                 t_id          FROM   test          WHERE  TYPE = 3) t3  WHERE  t1.t_id = t2.t_id  AND    t1.t_id = t3.t_id;  

結果如下所示:

真題3、有如下表內容:

2005-05-09 勝  2005-05-09 勝  2005-05-09 負  2005-05-09 負  2005-05-10 勝  2005-05-10 負  2005-05-10 負  

如果要生成下列結果,那麼該如何編寫SQL語句?

            勝 負  2005-05-09  2  2  2005-05-10  1  2  

其中,建表語句如下所示:

create table tmp(rq varchar2(10),shengfu varchar2(5));  insert into tmp values('2005-05-09','勝');  insert into tmp values('2005-05-09','勝');  insert into tmp values('2005-05-09','負');  insert into tmp values('2005-05-09','負');  insert into tmp values('2005-05-10','勝');  insert into tmp values('2005-05-10','負');  insert into tmp values('2005-05-10','負');  select * from tmp;  

答案:方法一:使用分組。按日期分組,用conut函數計算次數。

SELECT rq "日期",         COUNT(decode(shengfu, '勝', 1)) "勝",         COUNT(decode(shengfu, '負', 1)) "負"  FROM   tmp  GROUP  BY rq  ORDER  BY rq;  

方法二:使用連表。

SELECT t1.rq,         t1.勝,         t2.負  FROM   (SELECT COUNT(decode(shengfu, '勝', 1)) "勝",                 rq          FROM   tmp          GROUP  BY rq) t1  JOIN   (SELECT COUNT(decode(shengfu, '負', 1)) "負",                 rq          FROM   tmp          GROUP  BY rq) t2  ON     t1.rq = t2.rq;  

真題4、有如下的表:

create table STUDENT_SCORE  (    name    VARCHAR2(20),    subject VARCHAR2(20),    score   NUMBER(4,1)  );  insert into student_score (NAME, SUBJECT, SCORE) values ('張三', '語文', 78.0);  insert into student_score (NAME, SUBJECT, SCORE) values ('張三', '數學', 88.0);  insert into student_score (NAME, SUBJECT, SCORE) values ('張三', '英語', 98.0);  insert into student_score (NAME, SUBJECT, SCORE) values ('李四', '語文', 89.0);  insert into student_score (NAME, SUBJECT, SCORE) values ('李四', '數學', 76.0);  insert into student_score (NAME, SUBJECT, SCORE) values ('李四', '英語', 90.0);  insert into student_score (NAME, SUBJECT, SCORE) values ('王五', '語文', 99.0);  insert into student_score (NAME, SUBJECT, SCORE) values ('王五', '數學', 66.0);  insert into student_score (NAME, SUBJECT, SCORE) values ('王五', '英語', 91.0);  select * from STUDENT_SCORE;  

表STUDENT_SCORE中數據:

如要得到類似下面的結果,那麼該如何編寫SQL語句:

答案:方法一:使用分組

SELECT NAME "姓名",         MAX(decode(subject, '語文' ,score)) "語文",         MAX(decode(subject, '數學' ,score)) "數學",         MAX(decode(subject, '英語' ,score)) "英語"  FROM   STUDENT_SCORE  GROUP  BY NAME;  方法二:使用連表  SELECT t1.name  姓名,         t1.score 語文,         t2.score 數學,         t3.score 英語  FROM   (SELECT NAME,                 score          FROM   STUDENT_SCORE          WHERE  subject = '語文') t1  JOIN   (SELECT NAME,                 score          FROM   STUDENT_SCORE          WHERE  subject = '數學') t2  ON     t1.name = t2.name  JOIN   (SELECT NAME,                 score          FROM   STUDENT_SCORE          WHERE  subject = '英語') t3  ON     t1.name = t3.name;  

如果大於或等於80表示優秀,大於或等於60表示及格,小於60分表示不及格,那麼請繼續給出SQL語句:

SELECT t.姓名,         (CASE             WHEN t.語文 >= 80 THEN              '優秀'             WHEN t.語文 >= 60 THEN              '及格'             ELSE              '不及格'         END) 語文,         (CASE             WHEN t.數學 >= 80 THEN              '優秀'             WHEN t.數學 >= 60 THEN              '及格'             ELSE              '不及格'         END) 數學,         (CASE             WHEN t.英語 >= 80 THEN              '優秀'             WHEN t.英語 >= 60 THEN              '及格'             ELSE              '不及格'         END) 英語  FROM   (SELECT t1.name  姓名,                 t1.score 語文,                 t2.score 數學,                 t3.score 英語          FROM   (SELECT NAME,                         score                  FROM   STUDENT_SCORE                  WHERE  subject = '語文') t1          JOIN   (SELECT NAME,                        score                 FROM   STUDENT_SCORE                 WHERE  subject = '數學') t2          ON     t1.name = t2.name          JOIN   (SELECT NAME,                        score                 FROM   STUDENT_SCORE                 WHERE  subject = '英語') t3          ON     t1.name = t3.name) t;  

結果:

真題5、有如下2張表:

yj01表:

月份mon 部門dep 業績yj  -----------------------  一月份      1      10  一月份      2      10  一月份      3      5  二月份      2      8  二月份      4      9  三月份      3      8  

yjdept表:

部門dep      部門名稱dname  --------------------   1    中國業務一部   2    中國業務二部   3    中國業務三部   4    國際業務部  

建表語句分別如下所示:

create table yj01(         month varchar2(10),         deptno number(10),         yj number(10)  );    insert into yj01(month,deptno,yj) values('一月份',1,10);  insert into yj01(month,deptno,yj) values('二月份',2,10);  insert into yj01(month,deptno,yj) values('二月份',3,5);  insert into yj01(month,deptno,yj) values('三月份',2,8);  insert into yj01(month,deptno,yj) values('三月份',4,9);  insert into yj01(month,deptno,yj) values('三月份',3,8);    create table yjdept(         deptno number(10),         dname varchar2(20)  );    insert into yjdept(deptno,dname) values(1,'中國業務一部');  insert into yjdept(deptno,dname) values(2,'中國業務二部');  insert into yjdept(deptno,dname) values(3,'中國業務三部');  insert into yjdept(deptno,dname) values(4,'國際業務部');  select * from yj01;  select * from yjdept;  

請按照月份和部門進行分組計算。

答案:使用分組:

SELECT deptno,         MAX(decode(MONTH, '一月份', yj)) 一月份,         MAX(decode(MONTH, '二月份', yj)) 二月份,         MAX(decode(MONTH, '三月份', yj)) 三月份  FROM   yj01  GROUP  BY deptno  ORDER  BY deptno;  

使用連接:

SELECT t1.deptno,         t1.yj     一月份,         t2.yj     二月份,         t3.yj     三月份  FROM   (SELECT y2.deptno,                 y1.yj          FROM   (SELECT yj,                         deptno                  FROM   yj01                  WHERE  MONTH = '一月份') y1          RIGHT  JOIN yjdept y2          ON     y1.deptno = y2.deptno) t1  JOIN   (SELECT y2.deptno,                 y1.yj          FROM   (SELECT yj,                         deptno                  FROM   yj01                  WHERE  MONTH = '二月份') y1          RIGHT  JOIN yjdept y2          ON     y1.deptno = y2.deptno) t2  ON     t1.deptno = t2.deptno  JOIN   (SELECT y2.deptno,                 y1.yj          FROM   (SELECT yj,                         deptno                  FROM   yj01                  WHERE  MONTH = '三月份') y1          RIGHT  JOIN yjdept y2          ON     y1.deptno = y2.deptno) t3  ON     t1.deptno = t3.deptno  ORDER  BY t1.deptno;  

結果:

真題6、有表T1,其數據如下所示:

STUDENT  SUBJECT GRADE  ---------------------------  student1  語文  80  student1  數學  70  student1  英語  60  student2  語文  90  student2  數學  80  student2  英語  100  

現需要轉換為如下的形式:

student   語文   數學   英語  student1   80    70     60  student2   90    80     100  

試寫出其SQL語句。

答案:該題屬於固定列數的行轉列,環境如下:

DROP TABLE T1;  CREATE TABLE T1(STUDENT VARCHAR2(30),SUBJECT VARCHAR2(30)  ,GRADE NUMBER);  INSERT INTO T1 VALUES('student1', '語文', 80 );  INSERT INTO T1 VALUES('student1', '數學', 70 );  INSERT INTO T1 VALUES('student1', '英語', 60 );  INSERT INTO T1 VALUES('student2', '語文', 90 );  INSERT INTO T1 VALUES('student2', '數學', 80 );  INSERT INTO T1 VALUES('student2', '英語', 100);  SELECT * FROM t1;  

若採用MAX或CASE+DECODE或CASE來回答,如下所示:

SELECT STUDENT,         SUM(DECODE(SUBJECT, '語文', GRADE, NULL)) "語文",         SUM(DECODE(SUBJECT, '數學', GRADE, NULL)) "數學",         SUM(DECODE(SUBJECT, '英語', GRADE, NULL)) "英語"    FROM T1   GROUP BY STUDENT;  

此題若使用PIVOT函數,如下所示:

SELECT *    FROM T1  PIVOT(SUM(GRADE)     FOR SUBJECT IN('語文' AS "語文", '數學' AS "數學", '英語' AS "英語"))   ORDER BY 1;  

此題若使用臨時表的方式,如下所示:

SELECT T.STUDENT,         NVL(SUM(T1.語文),0) AS "語文",         NVL(SUM(T2.數學),0) AS "數學",         NVL(SUM(T3.英語),0) AS "英語"    FROM (SELECT STUDENT,GRADE AS "語文" FROM T1 A WHERE A.SUBJECT = '語文') T1,         (SELECT STUDENT,GRADE AS "數學" FROM T1 A WHERE A.SUBJECT = '數學') T2,         (SELECT STUDENT,GRADE AS "英語" FROM T1 A WHERE A.SUBJECT = '英語') T3,         (SELECT DISTINCT STUDENT FROM T1) T   WHERE T.STUDENT = T1.STUDENT(+)     AND T.STUDENT = T2.STUDENT(+)     AND T.STUDENT = T3.STUDENT(+)   GROUP BY T.STUDENT   ORDER BY 1;  

真題7、有表T1結構如下:

c1     c2  ---- ---------  1      我  1      是  1      誰  2      知  2      道  3      不  ……  

現需要轉換為如下形式:

1   我是誰  2   知道  3   不  

試寫出其SQL語句。

答案:此題可以使用listagg函數或者訂製的WM_CONCAT函數(參考:【DB筆試面試459】ORA-00904: "wm_concat":invalid identifier錯誤如何解決?)來實現。listagg函數如下所示:

WITH TEMP AS(    SELECT '1' c1 ,'我' c2 FROM DUAL UNION ALL    SELECT '1' c1 ,'是' c2 FROM DUAL UNION ALL    SELECT '1' c1 ,'誰' c2 FROM DUAL UNION ALL    SELECT '2' c1 ,'知' c2 FROM DUAL UNION ALL    SELECT '2' c1 ,'道' c2 FROM DUAL UNION ALL    SELECT '3' c1 ,'不' c2 FROM DUAL  )  SELECT c1,LISTAGG(c2,'') WITHIN GROUP (ORDER BY c2 desc )  FROM TEMP  GROUP BY c1;  

另外,也可以藉助於PL/SQL來完成,這裡給一個簡單的例子,其中默認原表名為T:

CREATE OR REPLACE FUNCTION GET_C2(TMP_C1 NUMBER) RETURN VARCHAR2 IS    COL_C2 VARCHAR2(4000);  BEGIN    FOR CUR IN (SELECT C2 FROM T WHERE C1 = TMP_C1) LOOP      COL_C2 := COL_C2 || CUR.C2;    END LOOP;    COL_C2 := RTRIM(COL_C2, 1);    RETURN COL_C2;  END;  /  SQL> SELECT DISTINCT C1 ,GET_C2(C1) CC2 FROM TABLE;  

& 說明:

有關行列互換更多的案例可以參考我的BLOG:http://blog.itpub.net/26736162/viewspace-1272538/

本文選自《Oracle程式設計師面試筆試寶典》,作者:小麥苗。