Oracle 10g SQL MODEL Clase.pdf

참고 : http://radiocom.kunsan.ac.kr/lecture/oracle/statement_select/model.html
         http://www.oracleclub.com/article/26172


위 첨부파일을 꼭 참고하세요

다음과 같은 seq, amt 자료가 있을때 result를 구하는 쿼리를 작성하세요.
순차적으로 amt값을 누적합산하되 그 값이 음수일경우엔 0이 되어야 합니다.

WITH t AS
(
SELECT 1 seq, -2000 amt FROM dual
UNION ALL SELECT  2,   4000 FROM dual
UNION ALL SELECT  3,  -5000 FROM dual
UNION ALL SELECT  4,  -2000 FROM dual
UNION ALL SELECT  5,   3000 FROM dual
UNION ALL SELECT  6,   1500 FROM dual
UNION ALL SELECT  7,   -250 FROM dual
UNION ALL SELECT  8,    320 FROM dual
UNION ALL SELECT  9,  -4000 FROM dual
UNION ALL SELECT 10,  10000 FROM dual
UNION ALL SELECT 11, -20000 FROM dual
)
SELECT * FROM t

 

SEQ

AMT

RESULT

계산 방법 참고

1

-2000 0  -2000 이 음수이므로 0

2

4000 4000  0 + 4000 = 4000

3

-5000 0  4000 - 5000 = -1000 = 0

4

-2000 0  0 - 2000 = -2000 = 0

5

3000 3000  0 + 3000 = 3000

6

1500 4500  3000 + 1500 = 4500

7

-250 4250  4500 - 250 = 4250

8

320 4570  4250 + 320 = 4570

9

-4000 570  4570 - 4000 = 570

10

10000 10570  570 + 10000 = 10570

11

-20000 0  10570 - 20000 = -9430 = 0

 

WITH T AS
(
SELECT 1 seq, -2000 amt FROM dual
UNION ALL SELECT  2,   4000 FROM dual
UNION ALL SELECT  3,  -5000 FROM dual
UNION ALL SELECT  4,  -2000 FROM dual
UNION ALL SELECT  5,   3000 FROM dual
UNION ALL SELECT  6,   1500 FROM dual
UNION ALL SELECT  8,    320 FROM dual
UNION ALL SELECT  7,   -250 FROM dual
UNION ALL SELECT  9,  -4000 FROM dual
UNION ALL SELECT 10,  10000 FROM dual
UNION ALL SELECT 11, -20000 FROM dual
)
SELECT SEQ, AMT, RESULT FROM T
MODEL
DIMENSION BY (SEQ)
MEASURES (AMT, 0 RESULT)
IGNORE NAV
RULES
AUTOMATIC ORDER
(
      RESULT[SEQ] = GREATEST(0, RESULT[CV(SEQ) - 1] + AMT[CV(SEQ)])
)
ORDER BY SEQ

================================================================================================================
================================================================================================================

참고로 누적 구하는 쿼리

WITH TEST AS
    (
    SELECT '1' SEQ , '입고' INOUT_STAT , 10 CNT FROM DUAL
    UNION ALL SELECT '2' , '출고' , 2 FROM DUAL
    UNION ALL SELECT '3' , '입고' , 3 FROM DUAL
    UNION ALL SELECT '4' , '입고' , 5 FROM DUAL
    UNION ALL SELECT '5' , '출고' , 7 FROM DUAL
    UNION ALL SELECT '6' , '입고' , 11 FROM DUAL
    )
SELECT SEQ
   , INOUT_STAT
   , CNT
   , SUM (DECODE (INOUT_STAT, '입고', CNT, CNT * -1)) OVER (ORDER BY SEQ) CNT2
   , SUM(CNT) OVER(ORDER BY SEQ) CNT3
 FROM TEST 

 

출처 : http://blog.naver.com/rainbow8830?Redirect=Log&logNo=70119741931

 

1. EXTRACT
   - date type 의 값에서 지정한 field(년, 월, 일) 항목을 추출
   + 사용법
     - SELECT EXTRACT(YEAR FROM SYSDATE) FROM DUAL : 현재 날짜에서 년도만 추출
     - SELECT EXTRACT(MONTH FROM SYSDATE) FROM DUAL : 현재 날짜에서 월만 추출
     - SELECT EXTRACT(DAY FROM SYSDATE) FROM DUAL : 현재 날짜에서 일 수만 추출 

2. NUMTOYMINTERVAL(NUMBER, TYPE_OF_INTERVAL)
   - 년, 월에 대한 시간 간격을 구함
   + 사용법
     - SELECT SYSDATE - NUMTOYMINTERVAL (1, 'YEAR') FROM DUAL : 현재 날짜에서 1년을 뺌
     - SELECT SYSDATE - NUMTOYMINTERVAL (1, 'MONTH') FROM DUAL : 현재 날짜에서 1달을 뺌

 3. NUMTODSINTERVAL(NUMBER, TYPE_OF_INTERVAL)
   - 날짜, 시, 분, 초에 대한 시간 간격을 구함
   + 사용법
     - SELECT SYSDATE - NUMTODSINTERVAL (1, 'DAY') FROM DUAL : 현재 날짜에서 1일을 뺌
     - SELECT SYSDATE - NUMTODSINTERVAL (1, 'HOUR') FROM DUAL : 현재 날짜에서 1시간을 뺌
     - SELECT SYSDATE - NUMTODSINTERVAL (1, 'MINUTE') FROM DUAL : 현재 날짜에서 10분을 뺌
     - SELECT SYSDATE - NUMTODSINTERVAL (1, 'SECOND') FROM DUAL : 현재 날짜에서 100초를 뺌

 

WITH TBL AS (
SELECT  '201211051000' STM , '201211051100' ETM FROM DUAL UNION ALL
SELECT  '201211051100' STM , '201211051110' ETM FROM DUAL UNION ALL
SELECT  '201211051200' STM , '201211061420' ETM FROM DUAL
)
SELECT  CHA_1
       ,SUBSTR(CHA_1, 9,2) || '일 ' ||
        SUBSTR(CHA_1,12,2) || '시 ' ||
        SUBSTR(CHA_1,15,2) || '분' CHA_1C
       ,CHA_2
       ,TO_NUMBER(SUBSTR(CHA_2, 2, 9)) || '일 ' ||
        SUBSTR(CHA_2,12,2) || '시 ' ||
        SUBSTR(CHA_2,15,2) || '분' CHA_2C
       ,CHA_3
       ,SUBSTR(CHA_3, 8,3) || '일 ' ||
        SUBSTR(CHA_3,12,2) || '시 ' ||
        SUBSTR(CHA_3,15,2) || '분' CHA_3C
  FROM (
    SELECT  NUMTODSINTERVAL(SUM(
            TO_DATE(ETM,'YYYYMMDDHH24MI') - TO_DATE(STM,'YYYYMMDDHH24MI'))
                ,'DAY') CHA_1
           ,NUMTODSINTERVAL(SUM(
            TO_DATE(ETM,'YYYYMMDDHH24MI') - TO_DATE(STM,'YYYYMMDDHH24MI')) + 0.00000000001
                ,'DAY') CHA_2
           ,NUMTODSINTERVAL(SUM(
            TO_DATE(ETM,'YYYYMMDDHH24MI') - TO_DATE(STM,'YYYYMMDDHH24MI')) + 0.000694444444444444,'DAY') CHA_3
      FROM  TBL
    ) 

 

 

WITH TB AS (
SELECT MOD(TO_DATE('201211051100', 'YYYYMMDDHH24MI') - TO_DATE('201211051000', 'YYYYMMDDHH24MI'), 1) TIM FROM DUAL
UNION ALL
SELECT MOD(TO_DATE('201211051100', 'YYYYMMDDHH24MI') - TO_DATE('201211051000', 'YYYYMMDDHH24MI'), 1) TIM FROM DUAL
)
SELECT TIM
        , TO_CHAR(TO_DATE(ROUND(SUM(TIM) * 86400), 'SSSSS'), 'HH24:MI') BBB
  FROM TB

 

 

 

출처 : http://raltigue.tistory.com/m/11

 

LISTAGG 함수가 도입되기 전에 동일 기능을 구현하기 위해 다양한 기법들이 사용되었다. 정리해보자.

 

아래와 같이 데이터를 생성하자.

CREATE TABLE t1 (c1 NUMBER(1), c2 VARCHAR2(2));

INSERT INTO t1 VALUES (1, '01');
INSERT INTO t1 VALUES (2, '02');
INSERT INTO t1 VALUES (2, '03');
INSERT INTO t1 VALUES (3, '04');
INSERT INTO t1 VALUES (3, '04');
INSERT INTO t1 VALUES (3, '05');
INSERT INTO t1 VALUES (3, '06');

 

① 11g를 사용한다면 LISTAGG 함수를 사용하면 된다. 집계함수(1번)와 분석함수(2번) 형태로 사용이 가능하다.

-- 1
SELECT   a.c1,
         LISTAGG (a.c2, ',') WITHIN GROUP (ORDER BY a.c2) AS c2
    FROM t1 a
GROUP BY a.c1;

 C1 C2          
--- ------------
  1 01          
  2 02,03       
  3 04,04,05,06 

3 rows selected.

-- 2
SELECT a.c1,
       LISTAGG (a.c2, ',') WITHIN GROUP (ORDER BY a.c2) OVER (PARTITION BY A.c1) AS c2
  FROM t1 a;

 C1 C2          
--- ------------
  1 01          
  2 02,03       
  2 02,03       
  3 04,04,05,06 
  3 04,04,05,06 
  3 04,04,05,06 
  3 04,04,05,06 

7 rows selected.

 

② WM_CONCAT 함수는 WMSYS 유저가 내부적으로 사용한다. (SQL Reference에 없다...--;) LISTAGG보다 성능은 떨어지지만 추가 기능(DISTINCT 구문, 분석함수 누적, KEEP 절)을 지원한다. 4번 방식을 이용하면 정렬도 가능하다. 

-- 1
SELECT   a.c1,
         wmsys.wm_concat (a.c2) AS c2
    FROM t1 a
GROUP BY a.c1;

 C1 C2          
--- ------------
  1 01          
  2 02,03       
  3 04,06,05,04 

3 rows selected.

-- 2
SELECT   a.c1,
         wmsys.wm_concat (DISTINCT a.c2) AS c2
    FROM t1 a
GROUP BY a.c1;

 C1 C2          
--- ------------
  1 01          
  2 02,03       
  3 04,05,06    

3 rows selected.

-- 3
SELECT a.c1,
       wmsys.wm_concat (a.c2) OVER (ORDER BY a.c2) AS c2
  FROM t1 a;

 C1 C2                    
--- ----------------------
  1 01                    
  2 01,02                 
  2 01,02,03              
  3 01,02,03,04,04        
  3 01,02,03,04,04        
  3 01,02,03,04,04,05     
  3 01,02,03,04,04,05,06  

7 rows selected.

-- 4
SELECT   a.c1,
         MAX (CAST (a.c2 AS VARCHAR2 (4000))) as c2
    FROM (SELECT a.c1,
                 wmsys.wm_concat (a.c2) OVER (PARTITION BY a.c1 ORDER BY a.c2) AS c2
            FROM t1 a) a
GROUP BY a.c1;

 C1 C2          
--- ------------
  1 01          
  2 02,03       
  3 04,04,05,06 

3 rows selected.

-- 5
SELECT   a.c1,
         wmsys.wm_concat (a.c2) KEEP (DENSE_RANK FIRST ORDER BY a.c2) AS c2
    FROM t1 a
GROUP BY a.c1;

 C1 C2          
--- ------------
  1 01          
  2 02          
  3 04,04       

3 rows selected.

 

③ 10g에서는 XMLAGG 함수를 사용해도 된다. 

SELECT   a.c1,
         SUBSTR (XMLAGG (XMLELEMENT (a, ',', a.c2) ORDER BY a.c2).EXTRACT ('//text()'), 2) AS c2
    FROM t1 a
GROUP BY a.c1;

 C1 C2          
--- ------------
  1 01          
  2 02,03       
  3 04,04,05,06 

3 rows selected.

 

④ 후임자를 괴롭히고 싶다면 MODEL 절을 사용해도 된다...--;

SELECT   a.c1,
         RTRIM (a.c2, ',') as c2
    FROM (SELECT c1,
                 c2,
                 rn
            FROM t1 a
           MODEL PARTITION BY (a.c1)
                 DIMENSION BY (ROW_NUMBER() OVER (PARTITION BY a.c1 ORDER BY a.c2) AS rn)
                 MEASURES (CAST (a.c2 AS VARCHAR2(4000)) AS c2)
                 RULES (c2[ANY] ORDER BY rn DESC = c2[CV()] || ',' || c2[CV()+1])) a
   WHERE a.rn = 1
ORDER BY a.c1;

 C1 C2          
--- ------------
  1 01          
  2 02,03       
  3 04,04,05,06 

3 rows selected.

 

⑤ 9i에서는 전통적 방식인 ROW_NUMBER와 SYS_CONNECT_BY_PATH 조합을 사용하면 된다. 

SELECT     a.c1,
           SUBSTR (MAX (SYS_CONNECT_BY_PATH (a.c2, ',')), 2) AS c2
      FROM (SELECT a.c1,
                   a.c2,
                   ROW_NUMBER () OVER (PARTITION BY a.c1 ORDER BY a.c2) AS rn
              FROM t1 a) a
START WITH a.rn = 1
CONNECT BY a.c1 = PRIOR a.c1
       AND a.rn - 1 = PRIOR a.rn
  GROUP BY a.c1
  ORDER BY a.c1;

 C1 C2          
--- ------------
  1 01          
  2 02,03       
  3 04,04,05,06 

3 rows selected.

 

1. listagg function in 11g release 2

2. ORACLE-BASE - String Aggregation Techniques

출처 : http://database.sarang.net/?inc=read&aid=25686&criteria=oracle&subcrit=&id=&limit=20&page=3

 

MONTHS_BETWEEN 과 ADD_MONTHS 함수는 잘 이해하고 사용하셔야 합니다.

오라클 매뉴얼을 보시면 아시겠지만...

위의 경우를 놓고 설명하자면,

 

1. 첫번째 v_a_dt : 20020228

두번째 v_b_dt : 20011128

-> 위의 경우와 같이 뒤의 일자부분이 동일한 경우에, MONTHS_BETWEEN 함수는 두 일자의 월간격 즉, 3을 리턴합니다.

 

3. 첫번째 v_a_dt : 20020228

두번째 v_b_dt : 20011130

-> 이 경우는, 달력을 보시면 아시겠지만, 두 날짜 모두 그 날짜가 속한 달의 말일입니다. 이 경우에도 위의 1의 경우와 마찬가지로 두 일자의 월간격 3을 리턴합니다.

 

2. 첫번째 v_a_dt : 20020228

두번째 v_b_dt : 20011129

-> 마지막으로 1번도 3번도 해당하지 않는 경우에는 다음과 같이 계산됩니다.

2001년 11월 29일 + 1달 = 2001년 12월 29일

2001년 11월 29일 + 2달 = 2002년 01월 29일

2001년 11월 29일 + 3달 = 2002년 02월 29일 인데, 28일까지 밖에 없으므로 3달 보다는 적은 2.xxxxxxx 달이 될 것입니다.

그러면 뒤에 소수부분은 어떻게 계산할까요?

2002년 01월 29일과 2002년 02월 28일은 30일의 차이가 나므로, 30일을 무조건 31로 나눈 30/31이 됩니다.

따라서 2번의 경우에는 MONTHS_BETWEEN 함수는 2+30/31을 리턴합니다.

계산해 보시면 아시겠지만, 확실히 30/31=0.96774193548387096774193548387096774194....... 인 값이 나옵니다.

 

이해가 되셨는지 모르겠네요.

 

-- SQL 쿼리 질문은 SQL 까페에서... http://cafe.daum.net/oraclesqltuning

 

WITH T AS
(
SELECT TO_DATE('20120229', 'YYYYMMDD') SDT, TO_DATE('20120329', 'YYYYMMDD') EDT FROM DUAL
UNION ALL SELECT TO_DATE('20120229', 'YYYYMMDD') , TO_DATE('20120330', 'YYYYMMDD')  FROM DUAL
UNION ALL SELECT TO_DATE('20120129', 'YYYYMMDD'), TO_DATE('20120228', 'YYYYMMDD') FROM DUAL
UNION ALL SELECT TO_DATE('20120130', 'YYYYMMDD'), TO_DATE('20120228', 'YYYYMMDD') FROM DUAL
UNION ALL SELECT TO_DATE('20120201', 'YYYYMMDD'), TO_DATE('20120229', 'YYYYMMDD') FROM DUAL
UNION ALL SELECT TO_DATE('20120131', 'YYYYMMDD'), TO_DATE('20120229', 'YYYYMMDD') FROM DUAL
UNION ALL SELECT TO_DATE('20111130', 'YYYYMMDD'), TO_DATE('20120331', 'YYYYMMDD') FROM DUAL
)
SELECT SDT
   , EDT
   , SMM
   , EMM
   , MM
         , CEIL(MONTHS_BETWEEN (EDT+1, SDT)) MM2
         , CEIL(MONTHS_BETWEEN (EDT, SDT)) MM3
   , D1
   , D2
   , DD
   , MM + ROUND (DD / 30) RESULT
         , MM + CEIL (DD / 30) RESULT2
 FROM (SELECT SDT
       , EDT
       , SMM
       , EMM
       , MONTHS_BETWEEN (EMM, SMM) MM
       , (SMM - SDT) D1
       , (EDT - EMM + 1) D2
       , (SMM - SDT) + (EDT - EMM + 1) DD
     FROM (SELECT SDT
           , EDT
           , TRUNC (ADD_MONTHS (SDT - 1, 1), 'MM') SMM
           , TRUNC (EDT + 1, 'MM') EMM
         FROM T))

 

1. 한달하고도 하루가 더 지났기 떄문에 2개월 이라면.

SELECT
CEIL
(
MONTHS_BETWEEN
(
TO_DATE('20050201', 'YYYYMMDD') + 1,
TO_DATE('20050101', 'YYYYMMDD')
)
) MOM
FROM DUAL


 

2. 날짜가 두달에 걸쳐서 있기 때문에 2개월이라면

SELECT
MONTHS_BETWEEN
(
TRUNC(TO_DATE('20050201', 'YYYYMMDD'),'MM'),
TRUNC(TO_DATE('20050131', 'YYYYMMDD'),'MM')
) + 1 MOM
FROM DUAL

 

=======================================================================================================

오라클의 MONTHS_BETWEEN과 비슷하게 처리한 예제

출처 : http://www.phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=60334&sca=%BD%BA%C5%A9%B8%B3%C6%AE&page=17

오라클의 add_months, months_between 함수를 자바스크립트로 만들일이 있어서 구현한 것입니다.

처음엔 만들기 귀찮아서.. 스쿨에서 비슷한 함수를 찾아봤는데 제가 못찾아서 그런건지 원래부터 없어서 그런건지 결국 못찾았습니다


간단히 코드 동작에 대해서 설명하자면....

add_months 함수의 경우
2008.01.31에 한달을 더하면 2008.02.29 가 나옵니다.
2008.04.30에 한달을 더하면 2008.05.31 이 나오고요
한달 더 한다고 단순히 기존 날짜에 30일을 더한 것이 아니라
마지막 날짜 여부까지 체크해줍니다.

months_between 함수의 경우
몇개월 차이인가 구하는 함수인데 여기저기 검색해보니
시작날짜, 종료날짜가 몇일인지 구한 후 단순히 30일로 나눠서 몇개월인지 구합니다.
이 경우 몇년 정도의 날짜 차이를 구하면 괜찮지만 몇십년 단위로
날짜 차이를 두면 당연히 몇개월의 오차가 생깁니다.(31일 날도 있어서)
그래서 이런 오차를 없애고자 1개월씩 더해서 루프 돌렸습니다 -_-
자세한 건 코드 보심 될듯싶습니다.

 

<script language="javascript" type="text/javascript">
/*
    오라클의 개월수 더하는 함수와 동일
    ex) add_months('2008.04.04',10);
*/
function add_months(pdate, diff_m) {
    var add_m;
    var lastDay;    // 마지막 날(30,31..)
    var pyear, pmonth, pday;
   
    pdate = makeDateFormat(pdate); // javascript 날짜형변수로 변환
    if (pdate == "") return "";

    pyear = pdate.getYear();
    pmonth= pdate.getMonth() + 1;
    pday  = pdate.getDate();
   
    add_m = new Date(pyear, pmonth + diff_m, 1);    // 더해진 달의 첫날로 셋팅

    lastDay = new Date(pyear, pmonth, 0).getDate(); // 현재월의 마지막 날짜를 가져온다.
    if (lastDay == pday) {  // 현재월의 마지막 일자라면 더해진 월도 마지막 일자로
        pday = new Date(add_m.getYear(), add_m.getMonth(), 0).getDate();
    }

    add_m = new Date(add_m.getYear(), add_m.getMonth()-1, pday);

    return add_m;
}

/*
    오라클의 개월수 차이 구하는 함수와 동일
    ex) months_between('20080404','20060101');
*/
function months_between(edate, sdate) {
    var syear, smonth, sday;
    var eyear, emonth, eday;
    var diff_month = 1;

    sdate = makeDateFormat(sdate); // javascript 날짜형변수로 변환
    edate = makeDateFormat(edate); // javascript 날짜형변수로 변환

    if (sdate == "") return "";
    if (edate == "") return "";

    syear = sdate.getYear();
    eyear = edate.getYear();
    smonth= sdate.getMonth() + 1;
    emonth= edate.getMonth() + 1;
    sday  = sdate.getDate();
    eday  = edate.getDate();

    while (sdate < edate) { // 한달씩 더해서 몇개월 차이 생기는지 검사
        sdate = new Date(syear, smonth - 1 + diff_month, 0);
        diff_month++;
    }

    if (sday > eday) diff_month--;

    diff_month = diff_month - 2;
   
    return diff_month;
}

/* yyyymmdd, yyyy-mm-dd, yyyy.mm.dd 를 javascript 날짜형 변수로 변환 */
function makeDateFormat(pdate) {
    var yy, mm, dd, yymmdd;
    var ar;
    if (pdate.indexOf(".") > -1) {  // yyyy.mm.dd
        ar = pdate.split(".");
        yy = ar[0];
        mm = ar[1];
        dd = ar[2];

        if (mm < 10) mm = "0" + mm;
        if (dd < 10) dd = "0" + dd;
    } else if (pdate.indexOf("-") > -1) {// yyyy-mm-dd
        ar = pdate.split("-");
        yy = ar[0];
        mm = ar[1];
        dd = ar[2];

        if (mm < 10) mm = "0" + mm;
        if (dd < 10) dd = "0" + dd;
    } else if (pdate.length == 8) {
        yy = pdate.substr(0,4);
        mm = pdate.substr(4,2);
        dd = pdate.substr(6,2);
    }

    yymmdd = yy+"/"+mm+"/"+dd;

    yymmdd = new Date(yymmdd);
   
    if (isNaN(yymmdd)) {
        return false;
    }

    return yymmdd;
}

// 4월30일에 1개월 더하기 => 5월31일
alert(add_months('2008.04.30',1));

// 몇개월 차이인지 구하기 => 1104 개월 차이
alert(months_between('2100.01.01','2008.01.01'));


</script>

 


출처 : http://acidsound.blogspot.com/2007/10/oracle10g-xmlagg-group-by-query.html

C군의 질문

A | B
------
A | 가
B | 가
C | 가
a | 나
B | 나
C | 다

일때

가 | ABC
나 | aB
다 | C

를 뽑아낼 수 없을까?

라고 하여 요렇게 조렇게 실험해봤음.
일종의 String Aggregation 인데 http://www.oracle-base.com/articles/10g/StringAggregationTechniques.php 이런 방법도 있지만 왠지 복잡하고 10g 기능을 사용하고 싶어서 뒤져보기 시작.

(10g 가 아니라면 sys_connect_by_path 를 사용하는 방법이 무난. 비용이 좀 든다는 단점이 있다고 한다.)
SELECT deptno,
LTRIM(MAX(SYS_CONNECT_BY_PATH(ename,','))
KEEP (DENSE_RANK LAST ORDER BY curr),',') AS employees
FROM (SELECT deptno,
ename,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) AS curr,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) -1 AS prev
FROM emp)
GROUP BY deptno
CONNECT BY prev = PRIOR curr AND deptno = PRIOR deptno
START WITH curr = 1;

DEPTNO EMPLOYEES
---------- --------------------------------------------------
10 CLARK,KING,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD


XMLAGG 로 GROUP BY를 할 수 있다는 사실을 발견

SELECT B, XMLAGG(A) FROM (
SELECT 'A' A, '가' B FROM DUAL UNION ALL
SELECT 'B' , '가' FROM DUAL UNION ALL
SELECT 'C' , '가' FROM DUAL UNION ALL
SELECT 'a' , '나' FROM DUAL UNION ALL
SELECT 'B' , '나' FROM DUAL UNION ALL
SELECT 'C' , '다' FROM DUAL)
GROUP BY B

하지만 이렇게 하면 오류가 난다.
타입이 맞지 않기 때문이다. 인자를 XML형으로 바꾸기 위해 XMLELEMENT를 사용한다.
컬럼에 C라는 Tag 명을 붙여서 보자.

SELECT B, XMLAGG(XMLELEMENT(C,A)) A FROM (
SELECT 'A' A, '가' B FROM DUAL UNION ALL
SELECT 'B' , '가' FROM DUAL UNION ALL
SELECT 'C' , '가' FROM DUAL UNION ALL
SELECT 'a' , '나' FROM DUAL UNION ALL
SELECT 'B' , '나' FROM DUAL UNION ALL
SELECT 'C' , '다' FROM DUAL)
GROUP BY B

결과는 나오지만 충분하지 않다. 실제로 Application 에서 사용하려면 XML 타입이라 맞지 않기 때문이다.
GETSTRINGVAL()을 붙여서 형변환을 해준다.

SELECT B, XMLAGG(XMLELEMENT(C,A)).GETSTRINGVAL() A
FROM (
SELECT 'A' A, '가' B FROM DUAL UNION ALL
SELECT 'B' , '가' FROM DUAL UNION ALL
SELECT 'C' , '가' FROM DUAL UNION ALL
SELECT 'a' , '나' FROM DUAL UNION ALL
SELECT 'B' , '나' FROM DUAL UNION ALL
SELECT 'C' , '다' FROM DUAL)
GROUP BY B

B | A
가 | ABC
나 | aB
다 | C

원하는 결과가 나왔다.
Tag 을 제거하자 기왕 xml도 건든 김에 정규표현식으로 제거해보자.


SELECT B, REGEXP_REPLACE(XMLAGG(XMLELEMENT(C,A)).GETSTRINGVAL(), '<[C/]+>', '') A
FROM (
SELECT 'A' A, '가' B FROM DUAL UNION ALL
SELECT 'B' , '가' FROM DUAL UNION ALL
SELECT 'C' , '가' FROM DUAL UNION ALL
SELECT 'a' , '나' FROM DUAL UNION ALL
SELECT 'B' , '나' FROM DUAL UNION ALL
SELECT 'C' , '다' FROM DUAL)
GROUP BY B

B | A
------
가 | ABC
나 | aB
다 | C


정규식을 사용하지 않고 태그 제거하기
WITH
TABLE AS (
SELECT 이름 FROM 유저목록
)
SELECT
LTRIM(SUBSTR(XMLAGG(XMLELEMENT(이름,'|'||이름 )).EXTRACT('//text()').GetStringVal(), 2))
FROM TABLE)

======================================================================================================


출처 : http://database.sarang.net/?inc=read&aid=38771&criteria=oracle&subcrit=&id=&limit=20&keyword=SYS_CONNECT_BY_PATH&page=1

 

with tbl as

(
select 1 seq , '20110801' ymd, 't' gubun, 'input1' title from dual
union select 2 seq , '20110802' ymd, 't' gubun, 'input2' title from dual
union select 3 seq , '20110802' ymd, 't' gubun, 'input3' title from dual
union select 4 seq , '20110802' ymd, 't' gubun, 'input4' title from dual
union select 5 seq , '20110802' ymd, 't' gubun, 'input5' title from dual
union select 6 seq , '20110804' ymd, 'c' gubun, 'input6' title from dual
)
select
ymd,wm_concat(seq||'||'||title) as seq_title
from
tbl
group by ymd

 

제가 할려는게 같은 날짜의 title 를 seq 순서대로 뽑아내는건대요

위에 쿼리를 돌려보면

20110801 1||input1
20110802 2||input2,4||input4,5||input5,3||input3 <== 여기가 문제임
20110804 6||input6

이런식으로 나옵니다. 즉 2,3,4,5 이런순으로 나와야 하는대 엉뚱하게 3이 젤 나중에 나와버립니다.

어떻게 해야 할까요? 도움 주시면 감사하겠습니다...

 

wm_concat 간단하면서도 강력한 기능 정말 좋은데...
한가지 아쉬운 점이 정렬 기능이 없다는 거죠...
XmlAgg(9i) 나 ListAgg(11g) 를 이용하시면 됩니다.

SELECT ymd
, SUBSTR(XMLAGG(XMLELEMENT(x, ',', seq, '||', title) ORDER BY seq)
.EXTRACT('//text()'), 2) v_9i
, wm_concat(seq||'||'||title) v_10g
, LISTAGG(seq||'||'||title, ',') WITHIN GROUP(ORDER BY seq) v_11g
FROM tbl
GROUP BY ymd
ORDER BY ymd
;

wm_concat를 이용해 정렬하고자 한다면..
인라인뷰에서 분석함수의 정렬기능을 이용하신후 밖에서 걸러내시면 됩니다.

SELECT ymd
, seq_title
FROM (SELECT ymd, seq
, wm_concat(seq||'||'||title)
OVER(PARTITION BY ymd ORDER BY seq) seq_title
, MAX(seq) OVER(PARTITION BY ymd) max_seq
FROM tbl
)
WHERE seq = max_seq
ORDER BY ymd
;

Connect_By_Path(9i)를 이용하는 방법도 있는데 더 복잡하고 성능도 안좋아요.

SELECT ymd
, SUBSTR(SYS_CONNECT_BY_PATH(seq||'||'||title, ','), 2) seq_title
FROM (SELECT ymd, seq, title
, ROW_NUMBER() OVER(PARTITION BY ymd ORDER BY seq) rn
, COUNT(*) OVER(PARTITION BY ymd) cnt
FROM tbl
)
WHERE rn = cnt
START WITH rn = 1
CONNECT BY PRIOR ymd = ymd
AND PRIOR rn + 1 = rn
;
 

 

 


출처 : http://www.uengine.org:8088/wiki/index.php/Facebook_Open_API_%ED%99%9C%EC%9A%A9(Javascript_SDK)

Facebook Open API 활용(Javascript SDK)

Uwiki

App ID를 발급받기 위한 application 등록
먼저 페이스북 OPEN API를 사용하기 위해서는 Facebook Platfrom API key(App ID)를 발급받아야 한다. Key를 만들는 과정은 다음의 과정을 따른다.

1. http://www.facebook.com/developers/createapp.php 로 가서 new application을 생성한다. 페이스북 id및 인증을 위한 모바일 폰 번호를 필요함.
2. 어플리케이션 이름을 입력한다.
3. Basic탭에서 모두 기본값을 사용하고 callback URL(자신의 도메인)은 반드시 넣도록 한다.
4. 생성된 API key를 적당히 메모해 둔다. 

자신의 application 정보는 http://www.facebook.com/developers/apps.php 에서 확인할 수 있다.



API 활용
다음은 자신의 페이지에서 Facebook OPEN API를 사용하기 위해 SDK를 로딩하는 과정으로 반드시 페이지 내에 추가해 주어야 한다.

<script src="http://connect.facebook.net/en_US/all.js"></script>
<script>
 FB.init({
   appId  : 'APP ID',
   status : true, // check login status
   cookie : true, // enable cookies to allow the server to access the session
   xfbml  : true  // parse XFBML
 });
</script>


또는, 페이지 로딩 속도를 확보하기 위해 다음과 같은 코드를 사용할 수도 있다.

<script>
 window.fbAsyncInit = function() {
   FB.init({appId: 'your app id', status: true, cookie: true,
            xfbml: true});
 };
 (function() {
   var e = document.createElement('script'); e.async = true;
   e.src = document.location.protocol +
     '//connect.facebook.net/en_US/all.js';
   document.getElementById('fb-root').appendChild(e);
 }());
</script>


FB.getLoginStatus()를 이용해서 유저의 로그인 상태 체크.

FB.getLoginStatus(function(response) {
 if (response.session) {
   // logged in and connected user, someone you know
 } else {
   // no user session available, someone you dont know
 }
});


FB.login()를 이용해서 페이스북로그인 창을 띄우기.

FB.login(function(response) {
 if (response.session) {
   // user successfully logged in
 } else {
   // user cancelled login
 }
});


FB.login()과 FB.getLoginStatus()를 이용해 이용해 다음과 같이 유저가 로그인 하도록 유도.

 FB.getLoginStatus(handleSessionResponse);
 function handleSessionResponse(response) { 
  if (!response.session) { 
   // Open login dialog box
   FB.login(handleSessionResponse);
   return; 
  } else{
   // already logged in
  }
 }


로그인 유저 정보 가져오기

function getUser(){
 FB.getLoginStatus(handleSessionResponse);
  function handleSessionResponse(response) { 
   if (!response.session) {
    //
   } else{
    FB.api( 
    {
      method: 'fql.query', 
      query: 'select uid,name,email,pic_square from user where uid  = "' + FB.getSession().uid + '"'
     }, 
    function(response) {
      for(var i=0; i < response.length; i++){
       response[i].uid; //유저아이디
       response[i].pic_square; // 사진
       response[i].name; // 이름
       response[i].email; // 이메일주소
      }
    } 
    );
   }
  }	 
}


내친구 리스트 가져오기

function getFriends(){
  FB.getLoginStatus(handleSessionResponse);
    function handleSessionResponse(response) { 
      if (!response.session) { 
         //
      } else{
        FB.api( 
         {
            method: 'fql.query', 
            query: 'SELECT uid, name, email, birthday, pic_square, online_presence  FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1 = me())'
         }, 
        function(response) {
          for(var i=0; i < response.length; i++){ 
             response[i].uid;
             response[i].pic_square;
             response[i].name;
             response[i].birthday;
             response[i].email;
             response[i].online_presence;
          }
        } 
        );
      }
   }
 }


내 담벼락의 글 가져오기

function getPersonalFeed(fbId){
   FB.getLoginStatus(handleSessionResponse);
     function handleSessionResponse(response) { 
        if (!response.session) { 
            alert("no login");
        } else{
          var path = '/me/feed';
          var num = 10;
          FB.api(path, { limit: num }, function(response) {
            for (var i=0, l= response.data.length; i< l; i++) {
               var info = response.data[i];
               if (info.message) {
                  info.id; // 글아이디
                  info.updated_time; // 갱신일
                  info.message; //글내용
                  info.from.name; //글쓴이
                }
             }
          });
        }
    }
  }
 }


내 담벼락에 새글 쓰고 id가져오기

function postPersonalFeed(fbId){
  FB.getLoginStatus(handleSessionResponse);
    function handleSessionResponse(response) { 
      if (!response.session) { 
         alert("No login");
      } else{
         var path = '/me/feed';
         var body = document.getElementById("info1").value;
         FB.api(path, 'post', { message: body }, function(response) {
             if (!response || response.error) {
                 alert("error");
              } else {
                 alert("successful with id [" + response.id + "]");
              }
         });
      }
   }
 }


내 담벼락의 특정 글에 코멘트 달고 글 id가져오기

function postPersonalComment(postId){
   FB.getLoginStatus(handleSessionResponse);
       function handleSessionResponse(response) { 
         if (!response.session) {
            alert("No login");
          } else{
            var path = '/'+postId + '/comments';
            FB.api(path, 'post', { message: body }, function(response) {
               if (!response || response.error) {
                  alert("error");
               } else {
                  alert("successful with id [" + response.id + "]");
               }
            });
          }
     }
 }


내 담벼락의 특정 글의 코멘트 가져오기

 function getPersonalComments(postId){
   FB.getLoginStatus(handleSessionResponse);
     function handleSessionResponse(response) { 
       if (!response.session) { 
          alert("No login");
       } else{
          var path = '/'+postId+'/comments';
          var num = 10;
          FB.api(path, { limit: num }, function(response) {
              for (var i=0, l= response.data.length; i< l; i++) {
                 var info = response.data[i];
                 info.id;
                 info.message";
              }
          });
       }
    }
 }


그룹 리스트 가져오기

 function getGroupList(){
   FB.getLoginStatus(handleSessionResponse);
     function handleSessionResponse(response) { 
       if (!response.session) { 
          alert("No login");
        } else{
           FB.api( 
           {
              method: 'fql.query', 
              query: 'SELECT gid, name, description, creator, icon FROM group WHERE gid IN (SELECT gid FROM group_member WHERE uid = me())'
           }, 
           function(response) { 
              for(var i=0; i < response.length; i++){
                response[i].gid;
                response[i].icon;
                response[i].name;
                response[i].description;
           }
        });
      }
   }
 }


그룹 담벼락의 글 가져오기

function getGroupFeed(groupId){
   FB.getLoginStatus(handleSessionResponse);
     function handleSessionResponse(response) { 
       if (!response.session) { 
         alert("no login");
       } else{
          var path = '/' + groupId + '/feed';
          var num = 5;
          FB.api(path, { limit: num }, function(response) {
              for (var i=0, l= response.data.length; i< l; i++) {
                  var info = response.data[i];
                  if (info.message) {
                     info.id;
                     info.updated_time;
                     info.message;
                     info.from.name+;
                  }
               }
         });
      }
    }
 }


그룹 담벼락의 특정 글에 코멘트를 쓰고 id가져오기

function postGroupComment(postId){
   FB.getLoginStatus(handleSessionResponse);
     function handleSessionResponse(response) { 
        if (!response.session) {
            alert("No login");
        } else{
            var path = '/'+postId + '/comments';
            FB.api(path, 'post', { message: body }, function(response) {
                if (!response || response.error) {
                    alert("error");
                 } else {
                     alert("successful with id [" + response.id + "]");
                 }
             });
       }
    }
 }


그룹 담벼락의 특정 글의 코멘트 가져오기

 function getGroupComments(postId){
    FB.getLoginStatus(handleSessionResponse);
      function handleSessionResponse(response) { 
         if (!response.session) { 
            alert("No login");
         } else{
            var path = '/'+postId+'/comments';
            var num = 10;
            FB.api(path, { limit: num }, function(response) {
            for (var i=0, l= response.data.length; i< l; i++) {
               var info = response.data[i];
               info.id;
               info.message;
            }
         });
       }
    }
 }


그룹 담벼락에 새글 쓰고 id가져오기

 function postGroupFeed(groupid){
    FB.getLoginStatus(handleSessionResponse);
       function handleSessionResponse(response) { 
         if (!response.session) { 
            alert("No login");
         } else{
            var path = '/'+groupid+'/feed';
            FB.api(path, 'post', { message: body }, function(response) {
               if (!response || response.error) {
                  alert("failed");
               } else {
                  alert("successful with id [" + response.id + "]");
               }
            });
         }
    }
 }




참고 사이트

개발자 사이트 : http://developers.facebook.com
Graph API : http://developers.facebook.com/docs/api
개발자 시작 가이드 : http://developers.facebook.com/docs/guides/web


출처 : http://mytory.co.kr/archives/812
         http://mytory.co.kr/archives/783
         http://stackoverflow.com/questions/3220995/jquery-document-onclick-doesnt-work-when-clicking-an-embed-flash-on-ie-bu

var $layerPopupObj = $('.layerPopupBox');
var left = ( $(window).scrollLeft() + ($(window).width() - $layerPopupObj.width()) / 2 );
var top = ( $(window).scrollTop() + ($(window).height() - $layerPopupObj.height()) / 2 );
$layerPopupObj.css({'left':left,'top':top, 'position':'absolute'});
$('body').css('position','relative').append($layerPopupObj);

jQuery의 .scrollLeft() 함수는 좌우로 스크롤된 화면이 왼쪽부터 몇 px인지 구하는 함수다. 보통은 0일 거다.

jQuery의 .scrollTop() 함수는 스크롤된 화면이 맨 위에서부터 몇 px인지 구하는 함수다.

jQuery의 .width() 함수는 너비를 구하는 함수인데, $(window).width() 를 하면 현재 화면의 너비를 구한다. 윈도우를 전체화면으로 하지 말고 사이즈를 줄여 놓고 값을 구해 보면 전체 가로 사이즈보다 작게 나오는 것을 알 수 있다.

jQuery의 .height() 함수 역시 마찬가지인데, $(window).height() 라고 하면 윈도우에서 메뉴바 같은 것들을 빼고 실제 사용되는 부분의 높이만 구해 준다. 나는 높이 800px 화면의 노트북을 사용한데, 실제 사용되는 영역은 675px이었다.


레이어를 Modal 처럼 띄우기 위한 배경 검은 막 씌우기

jQuery modal window를 만드는 튜토리얼에서 그 방법을 발견했다. 우리가 해왔던 방식하고 크게 다르지 않았다. 이것만 가지고 플러그인을 만들어 볼까 하는 생각도 들었다.

자, 반투명 검은 막을 만드는 방법은 간단하다.(예제부터 보길 바라면 아래 파일을 사용하면 된다.)

cfile25.uf.13211E484D4BC96836139F.html

일단, HTML의 어딘가에 아래 코드를 끼워 넣는다. 어디든 별 상관 없지만 찾기 편한 곳에 둬야 할 거다.

<div id="mask"></div>
 

다음은 CSS인데 아래처럼 해 준다.

#mask {
position:absolute;
left:0;
top:0;
z-index:9000;
background-color:#000;
display:none;
}
 

마지막으로 jQuery 코드를 짜 보자.

function wrapWindowByMask(){
//화면의 높이와 너비를 구한다.
var maskHeight = $(document).height();
var maskWidth = $(window).width();
//마스크의 높이와 너비를 화면 것으로 만들어 전체 화면을 채운다.
$('#mask').css({'width':maskWidth,'height':maskHeight});
//애니메이션 효과
$('#mask').fadeIn(1000);
$('#mask').fadeTo("slow",0.8);
}
 

자, 위 함수를 사용하면 반투명의 검은 마스크가 나타날 것이다. 그럼 어떻게 닫을까?

두 가지가 있을 것이다.

1.mordal window의 닫기 버튼을 눌렀을 때

2.반투명 검은 막을 눌렀을 때

두 경우 모두를 지원하기 위해서 click할 때 일어나는 이벤트를 두 군데 걸어야겠다.

이건 원본에서 그냥 긁어 온 코드다. .window는 검은 막 위에 뜬 mordal window다.

//닫기 버튼을 눌렀을 때
$('.window .close').click(function (e) {
//링크 기본동작은 작동하지 않도록 한다.
e.preventDefault();
$('#mask, .window').hide();
});
//검은 막을 눌렀을 때
$('#mask').click(function () {
$(this).hide();
$('.window').hide();
});
 

위 코드들은 당연히 jQuery(document).ready(function(){ /*코드 넣는 부분*/ }) 으로 감싸 줘야 한다.

완성된 코드는 아래와 같다.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
#mask {
position:absolute;
z-index:9000;
background-color:#000;
display:none;
left:0;
top:0;
}
.window{
display: none;
position:absolute;
left:100px;
top:100px;
z-index:10000;
}
</style>
<script>
function wrapWindowByMask(){
//화면의 높이와 너비를 구한다.
var maskHeight = $(document).height();
var maskWidth = $(window).width();
//마스크의 높이와 너비를 화면 것으로 만들어 전체 화면을 채운다.
$('#mask').css({'width':maskWidth,'height':maskHeight});
//애니메이션 효과 - 일단 1초동안 까맣게 됐다가 80% 불투명도로 간다.
$('#mask').fadeIn(1000);
$('#mask').fadeTo("slow",0.8);
//윈도우 같은 거 띄운다.
$('.window').show();
}
$(document).ready(function(){
//검은 막 띄우기
$('.openMask').click(function(e){
e.preventDefault();
wrapWindowByMask();
});
//닫기 버튼을 눌렀을 때
$('.window .close').click(function (e) {
//링크 기본동작은 작동하지 않도록 한다.
e.preventDefault();
$('#mask, .window').hide();
});
//검은 막을 눌렀을 때
$('#mask').click(function () {
$(this).hide();
$('.window').hide();
});
});
</script>
</head>
<body>
<div id="mask"></div>
<div class="window">
<input type="button" href="#" class="close" value="나는야 닫기 버튼(.window .close)"/>
</div>
<a href="#" class="openMask">검은 막 띄우기</a>
</body>
</html>
 



간단한 레이어 팝업

<style type="text/css">
ul.pop_box{position:absolute; background-color:#fff; border:1px solid #333; width:62px; display:none;}
ul.pop_box li{padding:3px;}
</style>

<script type="text/javascript">
jQuery(function($) {
 var $popBox = null;
 $('.friend_list li .user_img').on('click', function(e) {
  if($popBox == null) {
   $popBox = $(
    '<ul class="pop_box">'+
    ' <li>친구끊기</li>'+
    ' <li>서재가기</li>'+
    '</ul>'
   ).appendTo('body');
  }
  
  $popBox.css({left:e.pageX, top:e.pageY}).show();
  e.stopPropagation();
 });
 
 $(document).on('click', function(event) {
  var p = $(event.target).closest('ul.pop_box');
  if(p.size() == 0) {
   $('.pop_box').hide();
  }
 });
});
</script>

<ul class="friend_list">
    <li>
        <img class="user_img" src="" />
    </li>
</ul>

 


출처 : http://blog.daum.net/coschao/16
         http://database.sarang.net/?inc=read&aid=38441&criteria=oracle&subcrit=&id=&limit=20&keyword=%C8%DE%C0%CF&page=1



1. 입력일 포함 다음 영업일 구하기 (휴일 테이블 사용)

휴일정보테이블(HOLIDAY_INFO_TABLE)은
    HOLIDAY_YMD   VARCHAR2(8 BYTE)  NOT NULL, /* 휴일날짜(yyyyMMdd) */
    HOLIDAY_NM    VARCHAR2(30 BYTE) NOT NULL, /* 휴일명 */

    .... 기타 추가정보

로 구성되어 있다고 가정됨.

 

아래에서 IN_YMD 는 'yyyyMMdd' 형식의 입력되는 변수(펑션의 변수 처럼..)

------------------------

    select b.YMD /* into RT_VALUE -- (펑션이라면 반환값) */
      from (
            select a.YMD, s.HOLIDAY_NM || case TO_CHAR(TO_DATE(a.YMD, 'YYYYMMDD'), 'D')
                                          when '1' then '일'
                                          when '7' then '토'
                                          else '' end as HOLI_DOW_TXT
              from (
                        select to_char(x.YMD_DATE - 1 + level, 'YYYYMMDD') as YMD
                          from (select to_date(IN_YMD, 'YYYYMMDD') as YMD_DATE from dual) x
                         where (x.YMD_DATE - 1 + level) <= last_day(x.YMD_DATE)
                       connect by level<=15 /* 입력일 이후 15일간 줄 세워봄. 15일간 연짱 휴일일리는 없겠지... */
                 ) a, HOLIDAY_INFO_TABLE s
             where a.YMD = s.HOLIDAY_YMD(+)
             order by a.YMD
        ) b
    where b.HOLI_DOW_TXT is null
      and rownum = 1




2. 두 기간 사이의 영업일 건수 구하기

WITH off_day AS
(
SELECT '20110301' dt, '삼일절' cmt FROM dual
)
SELECT COUNT(*) cnt
  FROM (SELECT TO_CHAR(sdt + LEVEL - 1, 'yyyymmdd') dt
             , TO_CHAR(sdt + LEVEL - 1, 'd') d
          FROM (SELECT TO_DATE('20110311', 'yyyymmdd') sdt
                     , TO_DATE('20110315', 'yyyymmdd') edt
                  FROM dual)
        CONNECT BY LEVEL <= edt - sdt + 1
        ) a
     , off_day b
 WHERE a.dt = b.dt(+)
   AND a.d NOT IN ('1', '7')
   AND b.dt IS NULL




3. 도시별 휴일 제외한 영업일 구하기 

 메인테이블
일자 영업일 도시코드
20120321 2 01
20120322 5 02
20120323 4 03



휴일테이블

도시코드 휴일일자
01 20120322
01 20120323
02 20120323
03 20120326


메인테이블의 일자에 영업일을 더해서 다음 영업일자를 구해야 합니다.
다음 영업일은 토/일은 기본으로 제외시키고 휴일테이블의 해당 도시에 해당하는 휴일도 제외하여 계산되어야 하는데요.

예를 들면
20120321 +2 영업일은 22,23(휴일), 24,25(토,일) 이므로 20120327일이 나와야 합니다.

Function등을 사용하지 않고 쿼리로만 사용할 수 있는 방법이 있을까요?

오라클 11G 기준 재귀쿼리입니다. 이하 버전에서는 안돌아 갑니다.

WITH t1 AS
(
SELECT '20120321' dt, 2 dy, '01' ct FROM dual
UNION ALL SELECT '20120322', 5, '02' FROM dual
UNION ALL SELECT '20120323', 4, '03' FROM dual
)
, t2 AS
(
SELECT '01' ct, '20120322' dt FROM dual
UNION ALL SELECT '01', '20120323' FROM dual
UNION ALL SELECT '02', '20120323' FROM dual
UNION ALL SELECT '03', '20120326' FROM dual
)
, t3(dt, dy, ct) AS
(
SELECT *
FROM t1
UNION ALL
SELECT TO_CHAR(TO_DATE(a.dt, 'yyyymmdd') + 1, 'yyyymmdd') dt
, a.dy
- CASE WHEN b.dt IS NOT NULL THEN 0
WHEN TO_CHAR(TO_DATE(a.dt, 'yyyymmdd') + 1, 'd') IN ('1','7') THEN 0
ELSE 1 END AS dy
, a.ct
FROM t3 a
LEFT JOIN t2 b
ON a.ct = b.ct
AND b.dt = TO_CHAR(TO_DATE(a.dt, 'yyyymmdd') + 1, 'yyyymmdd')
WHERE a.dy > 0
)
SELECT ct, dt
FROM t3
WHERE dy = 0
ORDER BY ct, dt
;


 



출처 : http://stove99.tistory.com/115

다운로드 : ftp://ftp.ca.com/pub/erwin/ServicePacks/


ERWin 을 이용해 논리, 물리 ERD 를 작성했으면 이제 맹근 ERD 를 바탕으로 데이터베이스에 테이블을 생성해야 한다.

요 테이블을 생성하기 위해서는 뭐 ERWin 에서 데이터베이스에 직접 접속해서 테이블들을 생성까지 해주는 기능이 있지만,


왠지 번거롭게 느껴져서 그냥 테이블 생성 스크립트만 생성해서, 생성된 스크립트를 다른 디비접속툴을 사용해 뽓 실행해 주는것이 더 간편해서 일단

테이블 스크립트만 ERWin 상에서 생성해 보자.



먼저 테스트로 맹글어본 ERD 는 간단히 테이블 하나만 있다. 뭐 테이블이 수없이 많이 있어도 하는방법은 똑같으니깐~

테이블은 논리, 물리 요렇게 되 있다.



빨간색 박스친 부분을 보면 논리모델과 물리모델에 있는 컬럼 순서가 다르게 되 있다.

보통 ERD를 작성할때 당연히 논리모델을 쭉 다 맹글고 물리 모델을 대충 나중에 만든다. 그런데 논리 모델을 맹글면서 컬럼을 순서대로 추가하면 죠런 현상이 없지만,

뭐 그렇게 하기가 힘드니 문득문득 생각나는걸 또 추가하고 추가하고 해서 순서만 살짝살짝 바꿔준다.

그렇게 하다보니 논리모델은 순서가 내가 원하는 순서로 되있지만, 물리모델은 논리모델에서 컬럼을 추가한 순서가 그대로 유지되 있다.

뭐 물론 이상태에서 스크립트를 생성해도 크게 문제는 없지만(생성된 스크립트를 보면 물리모델에 보이는 컬럼순서대로 스크립트가 생성됨)

좋은게 좋은거라고 이왕이면 논리적으로 생각하는 순서와 똑같이 맹글고 싶다.



고걸 하기 위해서는 물리 ERD 작성하는 VIEW로 바꾼다음에 아무테이블 위에서 따블클릭을 하면 요런창이 뽁 뜨는데 요렇게 설정해 주면 논리모델과 순서를 똑같이 맞춰준다.








이제 물리모델의 컬럼 순서까지 보기 좋게 싹 맞춰 줬으니 테이블 생성 스크립트를 생성해 보자.

생성하기전에 살짝 해줄게 하나 있다. 테이블을 생성할때 딸랑 테이블만 생성해도 되지만 이왕이면 논리모델에서 작성한 테이블 이름과 컬럼이름을 코맨트로 달아서

다른 디비접속툴에서도 이 컬럼이 뭐하는 컬럼인지 쉽게 알 수 있게 해주면 좋다.

고런걸 자동으로 해주면 좋겠지만, 아직까진 그렇진 않기 때문에 테이블 생성 스크립트에 코맨트를 추가해주도록 ERWin에서 쓰는 스크립트를 하나 추가해야 한다.


스크립트 추가하기

메뉴 > Database > Pre & Post Scripts > Model-Level...



New 버튼을 클릭해서 적당한 이름으로 하나 추가하고 하단의 스크립트 Code 입력란에 각 디비에 맞는 스크립트를 입력해 준다음 OK 버튼을 클릭하면 된다.



※ 7.3.11 버전
1. 메뉴 > Database > Pre & PostScripts... 메뉴 선택
2. "New" 아이콘 클릭
    - Name : Comment 생성
    - Code 탭에 아래 스크립트 추가




※ 각 데이터베이스별 코맨트 생성 스크립트

MySQL

%ForEachTable()
{
    ALTER TABLE %TableName COMMENT = '%EntityName';
    %ForEachColumn()
    {
        ALTER TABLE %TableName CHANGE COLUMN %ColName %ColName %AttDatatype %AttNullOption COMMENT '%AttName';
    }
}




Oracle

%ForEachTable()
{
    COMMENT ON TABLE %TableName IS '%EntityName';
    
    %ForEachColumn()
    {
        %Switch('%ColumnComment')
        {
            %Choose(''){COMMENT ON COLUMN %TableName.%ColName IS '%AttName';}
            %Default {COMMENT ON COLUMN %TableName.%ColName IS '%ColumnComment';}
        }
    }
}



MSSQL

%ForEachTable() {
    exec sp_addextendedproperty 'MS_Description' , '%EntityName' , 'USER' , 'dbo' , 'TABLE' , '%TableName'
    go

    %ForEachColumn() {
        exec sp_addextendedproperty 'MS_Description' , '%AttName' , 'USER' , 'dbo' , 'TABLE' '%TableName' , 'COLUMN' , '%ColName'
        go
    }
}


%ForEachTable() {
EXEC sp_addextendedproperty @name=N'MS_Description', @value=N'%EntityName', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'%TableName'
go

%ForEachColumn() {
EXEC sp_addextendedproperty @name=N'MS_Description', @value=N'%AttName', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N'%TableName', @level2type=N'COLUMN', @level2name=N'%ColName'
go
}
}





요기까지 했으니 이제 진짜로 테이블 생성 스크립트를 생성해 보자.

메뉴 > Tools > Forward Enginner 
테이블 생성 스크립트 생성시 쫌전에 맹글었던 코맨트 생성스크립트를 사용하라고 Post-Script 를 체크하고 간단히 Preview 버튼만 폿 눌러주면 생성된 스크립트가 팝업창에 뜬다.

요 팝업창에 뜬 스크립트를 폿 복사해서 토드같은 툴에다 붙여넣고 폿 실행을 하면 테이블이 생성된다.

※ sql 파일로 export 하고 싶으면 Report 버튼을 클릭하면 된다.

* 스크립트로 생성 시 컬럼 순서를 Logical과 똑같이 하고 싶다면 Forward Engineer Schema Generation에서 좌측에서 Column을 선택한 후 우측의 Physical Order 체크를 해제하면 된다.




==================================================================================================================

설정


* Forward Engineer - Schema Generation 옵션
  - Column > Physical Order : 체크 해제, 필드 순서를 보이는대로 script 생성
  - Trigger > ERWin Generated : 체크 해제, 불필요한 Trigger 생성 제거
  - Schema > Post-Script : 체크, Comment 스크립트 추가
               > Create > User Defined Type : 체크해제, Domains 생성 스크립트 제거
  - Referential Integrity > Foreign Key (FK) : 체크 해제, Foreign key 생성 제거
  - Other Options > Constraint Name : Script 생성 시 Constraint 생성 여부
  - Table > Drop > Table : Drop Table script 최상단 추가


* Physical 에서 datatype 보기
- Format > Table Display > Column Datatype 선택


* Constraint Key명 변경
- Tools > Names > Model Naming Options 메뉴 선택 > Name Mapping 탭 선택
- Key Group to Index 항목 편집 : 기본값 (%KeyName)

- 예제 테이블명 TB_USR
    PK 룰 : PK_TB_USR
    AK 룰 (Unique) : UX_TB_USR_1
    IE 룰 (Index) : IX_TB_USR_1

%If(%==(%Substr(%KeyType,1,2),IE)){IX}%If(%==(%Substr(%KeyType,1,2),AK)){UX}%If(%==(%KeyType,PK)){PK}_%TableName%If(%Not(%==(%KeyType,PK))){_%Substr(%KeyType,3,1)}


- 예제 테이블명 TB_USR
    PK 룰 : TB_USR_PK
    AK 룰 (Unique) : TB_USR_UX1
    IE 룰 (Index) : TB_USR_IX1

%TableName_%If(%==(%Substr(%KeyType,1,2),IE)){IX}%If(%==(%Substr(%KeyType,1,2),AK)){UX}%If(%==(%KeyType,PK)){PK}%If(%Not(%==(%KeyType,PK))){_%Substr(%KeyType,3,1)}



- %IndexType, Else 샘플

%If(%==(%IndexType,PK)){PK_%TableName}%Else{%IF(%==(%substr(%IndexType,1,2),IF)) {%TableName_%substr(%IndexType,2)}}



* 연결선(관계) 모양 변경
Model > Model Properties : Notation 탭에서 Logical Notation => IE, Physical Notation => IE 선택



==================================================================================================================

Image Export


1. Tools > Report Template Builder > Report Builder 선택

2. New 버튼 클릭

3. 왼쪽의 G Graphical > Picture 선택 후 우측이동 버튼 클릭

4. 저장 버튼 클릭하여 rtb 파일 저장 후 해당 팝업 (New 클릭 후 열린 팝업) 닫기

5. Report Builder 팝업에서 Available Templates 영역에서 저장한 rtb 파일 선택
   Output Type : PDF 선택
   Browse... 버튼을 클릭하여 저장 위치 지정
   Run 버튼 클릭하여 이미지 생성

6. Browse... 에서 지정한 경로에 가면 images 폴더 아래에 이미지가 저장되어 있음.






출처 : http://chaospace.tistory.com/152
         http://www.ugoon.net/study/mobile/mobile_viewport.php


viewport란?

모바일 브라우저 중 webkit을 기반으로 한 녀석들은 PC의 브라우저 윈도우에
해당하는 Viewport라는 녀석이 있습니다.
기본적인 뷰포트의 넓이는 980px로 설정되어 있어서, 폰에서 보여질 컨텐츠를 500px로
만들었다면 480px의 여백이 생기도 페이지의 넓이는 980px이 됩니다.
정리하자면 아이폰의 경우 320 x 480px 의 화면사이즈를 가지기 때문에 980px과 비교하면
PC화면의 약 1/3의 크기로 보이게 됩니다.

viewport설정방법

viewport를 설정하려면 head에 meata태그를 추가하면 됩니다.
<meta name="viewport" content="속성값">

viewport속성값

속성 내용 단위 기본값 허용범위 특이사항
width viewport의 넓이 px 980px 200 ~ 10,000 뷰 포트의 픽셀 가로 길이를 지정. 가로 길이를 지정하지 않으면 데스크탑 사이즈로 지정.
(모바일 사파리의 경우는 980픽셀로 지정됨.)device-width로 지정가능
height viewport의 높이 px 가로화면비율에서
계산된 값
200 ~ 10,000 뷰 포트의 픽셀 세로 길이를 지정. 일반적으로, 이 속성에 대한 지정은 하지 않아도 무관 함.
device-height로 지정가능
initial-scale 초기 배율값 승수지정
120%
= 1.2
viewport에서
계산된 값
0 ~ 10 처음 모바일 페이지가 보여질 때 페이지 배율을 지정. (0~10.0)
(1.0으로 설정하는 것이 안정적임.)
값이 크면 확대, 값이 작으면 축소
minimum-scale 승수 지정 승수 .25 0 ~ 10 사용자가 축소할 수 있는 최소 배율을 지정. (0~10.0)
모바일 사파리는 기본으로 0.25로 설정.
maximum-scale 승수 지정 승수 1.6 0 ~ 10 사용자가 확대할 수 있는 최대 배율을 지정. (0~10.0)
모바일 사파리는 기본으로 1.6으로 설정.
user-scalable 확대,축소 지원여부 yes, no yes 사용자가 배율을 확대 또는 축소 허용 여부를 설정.
모바일 사파리는 기본으로 '사용 함'으로 설정.

viewport의 넓이를 400px하고 시작 비율을 2로 적용한 코드;
<meta name="viewport" content="width=400px, initial-scale=2">

viewport의 확대축소를 지원하고 싶지 않을 경우 :
<meta name="viewport" content="width=400px, user-scalable=no, initial-scale=2">

Viewport가 지원하지 않을때 meta tag 사용
<meta name="HandheldFriendly" content="true" />
<meta name="MobileOptimized" content="320" />


실습하기

시작시 content의 크기를 2.3배로 보이게 하는 코드 :
<meta name="viewport" content="width=320px, initial-scale=2.3">

결과 ( 웹 브라우저는 viewport가 무시됨 )

user-scalable을 이용해 content스케일 조절을 막은 코드 :
<meta name="viewport" content="width=320px, user-scalable=no, initial-scale=1">

결과화면 :


참고 사이트 :

 

 

+ Recent posts