2008年8月25日 星期一

Find the least values of columns using Oracle10g


/*
  朋友負責的專案裡有個Table是這樣的:
  在同一筆record中存在三個不同的欄位組,
  報表則須要從這三個欄位組找出最接近系統日期的那一組。
  她希望是用最精簡的SQL來處理掉這個需求,於是我就借來玩玩了。
  1) 不使用 least function
  2) 使用 least function
*/
  
/* ----- 1)不使用 least function ----- */
-- 1) 建立一個Table裡面存在三組不同的欄位組

DROP TABLE TRIPLE_REC
CREATE TABLE TRIPLE_REC
(
   KEYNO    INTEGER PRIMARY KEY,
   TYPE1    INTEGER,
   TYPEUSER1  VARCHAR2(5),
   TYPEDT1   DATE,
   TYPE2    INTEGER,
   TYPEUSER2  VARCHAR2(5),
   TYPEDT2   DATE,
   TYPE3    INTEGER,
   TYPEUSER3  VARCHAR2(5),
   TYPEDT3   DATE
);

--測試新增兩筆資料

INSERT INTO TRIPLE_REC VALUES (1,1,'ADMIN',TO_DATE('20070831','YYYYMMDD'),
                   2,'XC001',TO_DATE('20080701','YYYYMMDD'),
                   3,'XC019',TO_DATE('20050729','YYYYMMDD'));
INSERT INTO TRIPLE_REC VALUES (2,1,'XC005',TO_DATE('20020821','YYYYMMDD'),
                   2,'XC008',TO_DATE('20080501','YYYYMMDD'),
                   3,'XC001',TO_DATE('20060729','YYYYMMDD'));

COMMIT;

--看一下資料內容

SELECT * FROM TRIPLE_REC ORDER BY KEYNO;

KEYNO TYPE1 TYPEUSER1 TYPEDT1 TYPE2 TYPEUSER2 TYPEDT2 TYPE3 TYPEUSER3 TYPEDT3
----- ----- --------- ------- ----- --------- ------- ----- --------- --------
1   1  ADMIN  2007/8/31  2  XC001  2008/7/1  3  XC019  2005/7/29
2   1  XC005  2002/8/21  2  XC008  2008/5/1  3  XC001  2006/7/29


--從三組不同的資料欄位中選一組日期最接近系統日的

SELECT *
FROM
(SELECT KEYNO,TYPE,TYPEUSER,TYPEDT,DIFF,
     ROW_NUMBER() OVER(PARTITION BY KEYNO ORDER BY DIFF) CLOSEDAYDIFF
 FROM  (SELECT KEYNO,TYPE1 TYPE,TYPEUSER1 TYPEUSER,TYPEDT1 TYPEDT,
          ABS(TRUNC(TYPEDT1) - TRUNC(SYSDATE)) DIFF
      FROM TRIPLE_REC
      UNION ALL
      SELECT KEYNO,TYPE2 TYPE,TYPEUSER2 TYPEUSER,TYPEDT2 TYPEDT,
          ABS(TRUNC(TYPEDT2) - TRUNC(SYSDATE)) DIFF
      FROM  TRIPLE_REC
      UNION ALL
      SELECT KEYNO,TYPE3 TYPE,TYPEUSER3 TYPEUSER,TYPEDT3 TYPEDT,
          ABS(TRUNC(TYPEDT3) - TRUNC(SYSDATE)) DIFF
      FROM  TRIPLE_REC) TMP
) TMP2
WHERE CLOSEDAYDIFF = 1;


--得到以下結果

KEYNO TYPE  TYPEUSER TYPEDT     DIFF   CLOSEDAYDIFF
------ ------ --------- ------------- --------- ------------
1   2    XC001  2008/7/1    56     1
2   2    XC008  2008/5/1    117     1


/* ----- 2)使用 least function ----- */
/* 建立測試table */

create table least_test
(
  seqno integer,
  id1  char(5),
  col1 date,
  id2  char(5),
  col2 date,
  id3  char(5),
  col3 date,
  id4  char(5),
  col4 date
);

/* 建立測試資料 */

insert into least_test values (1,'00123',to_date('20080115','yyyymmdd'),
                   '00576',to_date('20080222','yyyymmdd'),
                   '00123',to_date('20080305','yyyymmdd'),
                   '00456',to_date('20080510','yyyymmdd'));
insert into least_test values (2,'00123',to_date('20080530','yyyymmdd'),
                   '00123',to_date('20080202','yyyymmdd'),
                   '00576',to_date('20071230','yyyymmdd'),
                   '00576',to_date('20080620','yyyymmdd'));
insert into least_test values (3,'00456',to_date('20081030','yyyymmdd'),
                   '00456',to_date('20080622','yyyymmdd'),
                   '00456',to_date('20081105','yyyymmdd'),
                   '00123',to_date('20080430','yyyymmdd'));
commit;

/* 用 least 找 col1,col2,col3,col4 中最接近系統日期的欄位*/

select seqno,case least(diff1,diff2,diff3,diff4)
         when diff1 then id1
         when diff2 then id2
         when diff3 then id3
         when diff4 then id4 end,
       case least(diff1,diff2,diff3,diff4)
         when diff1 then col1
         when diff2 then col2
         when diff3 then col3
         when diff4 then col4 end
from (select seqno,id1,id2,id3,id4,col1,col2,col3,col4,
        abs(trunc(col1) - trunc(sysdate)) diff1,
        abs(trunc(col2) - trunc(sysdate)) diff2,
        abs(trunc(col3) - trunc(sysdate)) diff3,
        abs(trunc(col4) - trunc(sysdate)) diff4
   from least_test) t

/* 結果 */

1   00456   2008/5/10
2   00576   2008/6/20
3   00456   2008/11/5