2014/04/26

[Java] 싱글톤 패턴

[출처] java 싱글톤 패턴|작성자 카이로스

객체를 어떻게 생성 합니까 ?
- new MyObject();

다른 객체에서 MyObject를 만들고 싶어한다면 어떻게 해야 하죠? MyObject에 대해서 new 연산자를 다시 쓸 수 있나요?
- 물론 가능하죠.

클래스만 있으면 언제든지 인스턴스를 만들 수 있는 거죠?
- 예, public 으로 선언된 거라면 별 문제 없습니다.

만약 public 으로 선언되지 않았으면요?
- 만약 public 클래스로 선언하지 않은 클래스라면 같은 패키지 안에 있는 클래스에서만 인스턴스를 만들 수 있습니다.하지만 같은 패키지에 속한 클래스에서는 여전히 인스턴스를 두 개 이상 만들 수 있죠.

흠.. 흥미롭군요.. 이렇게 할수있는건 혹시 아나요 ?
public MyClass{
private MyClass() {}
}
- 아, 그런 건 한 번도 생각해보지 못했습니다. 하지만 문법적으로는 전혀 문제가 없는 것 같네요.

저 코드에 대해서 설명해 보겠습니까?
- 생성자가 private으로 선언되어 있기 때문에 인스턴스를 만들 수 없는 클래스 같군요.

private으로 생성된 생성자를 사용할 수 있는 객체가 과연 존재할까요?
- 흠... MyClass에 있는 코드에서만 호출할 수 있는 것 같은데, 그렇다면 절대 객체의 인스턴스를 만들 수 없는 것 아닌가요?

왜 안 되죠?
- 생성자를 호출하려면 일단 그 클래스의 인스턴스가 있어야 되는데, 다른 클래스에서 이 클래스에서 이 클래스의 인스턴스를 만들 수 없기 때문에 인스턴스를 만드는 것이 불가능합니다. 닭이 먼저냐 달걀이 먼저냐 하는 것과 같은 문제라고 볼 수 있습니다. MyClass형식의 객체에서만 private으로 선언된 생성자를 사용할 수 있고, 다른 어떤 클래스에서도 "new MyClass()"라고 할 수 없기 때문에 결국 인스턴스를 만들 수 없게 되는 거죠.

그래요. 그건 그렇고... 이건 어떻게 해석할 수 있을까요 ?
public MyClass{
public static MyClass getInstance() {}
}
- MyClass에 정적 메소드가 있습니다. 그 정적 메소드는 다음과 같은 식으로 호출할 수 있습니다. MyClass.getInstance();

왜 객체 이름을 사용하지 않고 MyClass라는 클래스 이름을 그냥 사용했죠?
- getInstance()는 정적 메소드입니다. 클래스 메소드라고 부르기도 하죠. 정적 메소드를 지칭할 때는 클래스 이름을 써야 합니다.

신기하군요. 그럼 이렇게 합쳐 놓으면 어떨까요? 그럼 MyClass의 인스턴스를 만들 수 있지 않나요?
public MyClass{
  private MyClass() {}
  
  public static MyClass getInstance(){
    return new MyClass();
  }
}
- 아, 그렇게 할 수도 있겠네요.

그러면 이제 객체 인스턴스를 만들 수 있는 방법을 알 수 있겠죠?
- MyClass.getInstance();

MyClass의 인스턴스가 하나만 만들어질 수 있도록 코드를 마무리해 볼 수 있겠습니까?
- 예, 할 수 있을 것 같아요.

싱글톤 패턴의 정의

싱글톤 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다.

* 고전적인 싱글톤 패턴 구현법
public class Singleton{
  private static SingleTon uniqueInstance;

  private Singleton() {}

  public static Singleton getInstance(){
    if(uniqueInstance == null){
      unuqueInstance = new Singleton();
    }
    return uniqueInstance;
  }
}

위 코드는 얼핏보기에 싱글톤 패턴을 정확히(안전히) 구현한 것 같지만 멀티 스레드 환경에서 실행하였을 때 문제점이 발생합니다.

getInstance()메소드가 끝나기도 전에 각각의 스레드들이 접근하기 때문에

유일한 인스턴스생성을 보장할 수 없습니다.

간략히 설명드리자면,

스레드1번이 if(uniqueInstance == null) 을 체크합니다. -> null입니다. 아직 인스턴스 생성 하기 전이니까요.

다음 스레드 2번이 if(uniqueInstance == null) 을 체크합니다. -> 역시 null입니다. 아직 인스턴스 생성 하기 전이니까요.

스레드 1번이 인스턴스를 생성합니다.
스레드 2번이 인스턴스를 생성합니다.

이런, 상황이 좋지 않군요 .

고전적인 싱글톤 패턴으로 구현하였을 경우, 멀티 스레드 환경에서 유일한 인스턴스생성을
보장할 수 없습니다.

멀티스레딩 문제 해결방법

getInstance()를 동기화시키기만 하면 멀티스레딩과 관련된 문제가 간단하게 해결됩니다.

public static Singleton getInstance()
-> public static synchronized Singleton getInstance()


질문 : 이렇게 하면 문제가 해결되긴 하겠지만, 동기화를 하면 속도 문제가 생기지 않나요?

답 : 좋은 지적입니다. 사실 동기화가 꼭 필요한 시점은 이 메소드가 시작되는 때 뿐입니다. 바꿔 말하자면, 일단 uniqueInstance 변수에 Singleton 인스턴스를 대입하고 나면 굳이 이 메소드를 동기화된 상태로 유지시킬 필요가 없는것입니다. 첫 번째 과정을 제외하면 동기화는 불필요한 오버헤드만 증가시킬 뿐 입니다.

더 효율적인 방법

1. 애플리케이션에서 반드시 Singleton의 인스턴스를 생성하고, 그 인스턴스를 항상 사용한다면, 또는 인스턴스를 실행중에 수시로 만들고 관리하기가 성가시다면 다음과 같은 식으로 처음부터 Singleton인스턴스를 만들어버리는 것도 괜찮은 방법입니다.
public class Singleton{
  private static SingleTon uniqueInstance = new Singleton();

  private Singleton() {}

  public static Singleton getInstance(){
    return uniqueInstance;
}

2. DCL(Double-Checking Locking)을 써서 getInstance()에서 동기화되는 부분을 줄입니다.
DCL(Double-Checking Locking)을 사용하면, 일단 인스턴스가 생성되어 있는지 확인한 다음, 생성되어있지 않았을 때만 동기화를 할 수 있습니다. 이렇게 하면 처음에만 동기화를 하고 나중에는 동기화를 하지 않아도 됩니다.
public class Singleton{
  private volatile static SingleTon uniqueInstance;
  private Singleton() {}

  public static Singleton getInstance(){
    if(uniqueInstance == null){
      synchronized(Singleton.class){
        if(uniqueInstance == null){
          uniqueInstance = new Singleton();
        }
      }
    }
    return uniqueInstance;
  }
}

한 템포 늦추라!

[출처]'사랑밭' 메일링 서비스

환란, 역경에 닥칠 때 가장 효과적 대처방법은
자기가 쥐고 있던 것을 놓고
마음에 여유를 가지는 것이다.

한 템포 늦추어 자기를 돌아보고,
주위를 돌아보고
비록 늦어도 잘못된 것은 변경하고,
그래서 숨고르기를 하는 것은
재충전의 시간이 된다.

- 소 천 -
*한 템포 늦추는 방법*
1. 먼 산을 본다.
2. 혼자 길을 걷는다.
3. 하루 정도 혼자 있는다.

- 남을 의식하지 말고 자기를 보는 것입니다. -

[Java] SAXParser를 이용하여 XML데이터 로드

원격서버에서 제공하는 XML데이터를 URL객체로 접근하여 스트림으로 읽어서 SAXParser로 분류하는 작업을 했다. 생각보다 간단했다.

SAXParser를 이용하는 경우 내부적으로 이벤트가 발생하는데 각 이벤트에 대해 정의된 DefaultHandler 인터페이스를 구현한 클래스에서 각 노드에 대해 접근하여 발췌하도록 구성한다.

* 수정 : 2010.04.27
- characters 메서드 변경
* 수정 : 2010.05.07
- XmlHandler 코드를 정리해서 추가 첨부
- empty element의 경우 데이터 획득이 안 되면서 element자체도 획득이 안 되는 문제 개선.


ReadXml.java
package com.xml;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;

public class ReadXml {
    public List read(String strUrl) {
        XmlHandler dh = null;
        
        try {
            URL url = new URL(strUrl);
            URLConnection urlConn = url.openConnection();
            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
            BufferedInputStream bis = new BufferedInputStream(urlConn.getInputStream());
            
            dh = new XmlHandler();
            parser.parse(bis, dh);
        } catch(MalformedURLException e) {
            System.out.println("[ MalformedURLException : " + strUrl + " ]");
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            System.out.println("[ ParserConfigurationException ]");
            e.printStackTrace();
        } catch (SAXException e) {
            System.out.println("[ SAXException ]");
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("[ IOException ]");
            e.printStackTrace();
        }
        
        return dh.getList();
    }
}

XmlHandler.java
package com.xml;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class XmlHandler extends DefaultHandler {
    String strJobName = "";
    private List list = new ArrayList();
    private HashMap map = null;
    private String strEname = "";
    private StringBuffer strBuff = new StringBuffer();
    private boolean bIsColumn = false;
 
    public void startDocument( ) throws SAXException {
        System.out.println( "SAX Event: [" + strJobName + "] START DOCUMENT ##################################" );
    }
    
    public void endDocument( ) throws SAXException {
        System.out.println( "List Size is " + list.size() );
        System.out.println( "SAX Event: [" + strJobName + "] END DOCUMENT ####################################" );
    }
    
    @SuppressWarnings("unchecked")
    public void startElement(String uri, String localName, String eName, Attributes attr) throws SAXException {
        //  System.out.println("SAX Event: START Element localName: " + localName);
        //  System.out.println("SAX Event: START Element eName: " + eName);
        //발췌 대상 엘리먼트 구분(엘리먼트 prefix로 구분함)
        if( eName.equals("item") ) {
            map = new HashMap();
        } else if( eName.substring(0, 3).equals("ns3") && !eName.equals("ns3:data") ) {
            strEname = eName.substring(4);
            bIsColumn = true;
            //데이터 버퍼 초기화
            strBuff.setLength(0);
            //   System.out.println(" Check Element eName: " + eName.substring(4));
        } else {
            bIsColumn = false;
        }
    }
 
    @SuppressWarnings("unchecked")
    public void endElement(String uri, String localName, String eName) throws SAXException {
        if( eName.equals("item") ) {
            list.add(map);
            //   System.out.println("SAX Event: END Element eName: " + eName);
        } else {
            if(bIsColumn) {
                //     System.out.println(strEname + " : ["+ strBuff.toString().trim() + "]");
                map.put(strEname, strBuff.toString().trim());
            }
        }
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        //발췌 대상 엘리먼트의 값만 추출
        if(bIsColumn) {
        strBuff.append(ch, start, length);
        //      System.out.println(strEname + " is [" + map.get(strEname) + "]");
        //      System.out.println("Char Start : " + start + " Length : " + length);
        }
    }
 
    /**
    * 발췌한 대상 서비스 데이터 리턴
    * @return List
    */
    @SuppressWarnings("unchecked")
    public List getList() {
        return this.list;
    }
}

2014/04/21

[Java] 절상, 절하, 반올림

지난번에 javascript에서 절상, 절하, 반올림 처리하는 함수를 올린 적이 있는데
이번엔 그 로직을 살~짝(아주 살짝) 바꿔서 java 함수로 만들었다.
물론 java에서도 Math객체에서 해당 함수들을 지원하지만 몰라서 그런지 정수부분
(원단위, 십단위 등) 또는 소수부분에서의 처리가 안 되는 것 같아서 만들었다.(삽질한 거 아닌지...)
/**
 * 절상, 절하, 반올림 처리
 * @param strMode  - 수식
 * @param nCalcVal - 처리할 값(소수점 이하 데이터 포함)
 * @param nDigit   - 연산 기준 자릿수(오라클의 ROUND함수 자릿수 기준)
 *                   -2:십단위, -1:원단위, 0:소수점 1자리
 *                   1:소수점 2자리, 2:소수점 3자리, 3:소수점 4자리, 4:소수점 5자리 처리
 * @return String nCalcVal
 */
public static String calcMath(String strMode, double nCalcVal, int nDigit) {
    if("CEIL".equals(strMode)) {  //절상
        if(nDigit < 0) {
            nDigit = -(nDigit);
            nCalcVal = Math.ceil(nCalcVal / Math.pow(10, nDigit)) * Math.pow(10, nDigit);
        } else {
            nCalcVal = Math.ceil(nCalcVal * Math.pow(10, nDigit)) / Math.pow(10, nDigit);
        }
    } else if("FLOOR".equals(strMode)) { //절하
        if(nDigit < 0) {
            nDigit = -(nDigit);
            nCalcVal = Math.floor(nCalcVal / Math.pow(10, nDigit)) * Math.pow(10, nDigit);
        } else {
            nCalcVal = Math.floor(nCalcVal * Math.pow(10, nDigit)) / Math.pow(10, nDigit);
        }
    } else {        //반올림
        if(nDigit < 0) {
            nDigit = -(nDigit);
            nCalcVal = Math.round(nCalcVal / Math.pow(10, nDigit)) * Math.pow(10, nDigit);
        } else {
            nCalcVal = Math.round(nCalcVal * Math.pow(10, nDigit)) / Math.pow(10, nDigit);
        }
    }
    return String.valueOf(nCalcVal);
}

웹에서 유튜브 동영상 다운로드


아래 사이트에 유튜브의 동영상 url 을 붙여넣고 이후 원하는 과정을 거치면 됨.

### 추가 : 2012.10.10
1. 인터넷 검색창에서 "유튜브" 를 검색하세요.검색이 어려우시면 아래의 사이트 주소를 클릭!
2. 유튜브 검색창에 원하는 노래 제목이나 가수이름을 검색하세요.예) "단발머리", "조용필"
3. 검색 결과로 나온 것 중 원하는 것을 클릭!영상이나 음악이 재생되는 것을 보실 수 있습니다.
4. 영상이나 음악이 재생되는 창 한가운데에서마우스 오른쪽 버튼을 눌러보세요.
   맨 뒤에 "동영상 URL 복사하기"를 누르세요.
5. 그리고 아래의 사이트를 클릭!
   빈 창에 마우스를 가져다놓고 Ctrl+V (붙여넣기 단축키)한 뒤 "GO" 버튼을 누르세요.
6. 알아서 mp3파일로 저장이 됩니다.변환 과정이 끝나고 나면"Downroad MP3" 버튼을 눌러
   저장하세요.

IE 브라우저에서 파일업로드 시 경로관련 에러조치

※ 현상
 업로드 대상 파일 경로를 찾지 못하며 "C:\fakepath\파일명"으로 경로가 표현된다.

※ 조치
 브라우저 도구 > 인터넷 옵션 > 보안 > 인터넷 > 사용자 지정 수준 의 '기타' 항목 하위의 속성 변경.

[JavaScript] 절상, 절하, 반올림

고민하다 나름 간략하게 만들어 본다고 한건데
아래의 방식으로 처리가 안되는 경우가 있으려나?

//param : strMode - 수식
//param : nCalcVal - 처리할 값(소수점 이하 데이터 포함)
//param : nDigit    - 연산 기준 자릿수
//       => -2:십단위, -1:원단위
//           , 0:소수점 1자리, 1:소수점 2자리, 2:소수점 3자리, 3:소수점 4자리, 4:소수점 5자리 처리
function xxx(strMode, nCalcVal, nDigit) {
    if(strMode == "CEIL") {  //절상
        if(nDigit < 0) {
            nDigit = -(nDigit);
            nCalcVal = Math.ceil(nCalcVal / Math.pow(10, nDigit)) * Math.pow(10, nDigit);
        } else {
            nCalcVal = Math.ceil(nCalcVal * Math.pow(10, nDigit)) / Math.pow(10, nDigit);
        }
    } else if(strMode == "FLOOR") { //절하
        if(nDigit < 0) {
            nDigit = -(nDigit);
            nCalcVal = Math.floor(nCalcVal / Math.pow(10, nDigit)) * Math.pow(10, nDigit);
        } else {
            nCalcVal = Math.floor(nCalcVal * Math.pow(10, nDigit)) / Math.pow(10, nDigit);
        }
    } else {        //반올림
        if(nDigit < 0) {
            nDigit = -(nDigit);
            nCalcVal = (nCalcVal / Math.pow(10, nDigit)).toFixed(0) * Math.pow(10, nDigit); 
        } else {
            nCalcVal = nCalcVal.toFixed(nDigit)
        }
    }
    return nCalcVal;
}

[JavaScript] 두 날짜간 일수/개월수 차이 구하기

두 날짜간 날짜 또는 월의 차이 계산 소스를
프로젝트에서 사용한 소스와 http://koxo.com/ 사이트의 자료를 기반으로하여 좀 더 정확한 계산이
되도록 내 나름대로 개선하여 정리한다.

//param : pStartDate - 시작일
//param : pEndDate  - 마지막일
//param : pType       - 'D':일수, 'M':개월수
// Update. 2014.11.07. 변수명 변경 : strGapDT->strTermCnt
// Update. 2014.11.07. 개월수 계산 시 년도가 다른 경우 부정확성 보완 : floor->round AND 365.25->365
function fn_calcDayMonthCount(pStartDate, pEndDate, pType) {
    var strSDT = new Date(pStartDate.substring(0,4),pStartDate.substring(4,6)-1,pStartDate.substring(6,8));
    var strEDT = new Date(pEndDate.substring(0,4),pEndDate.substring(4,6)-1,pEndDate.substring(6,8));
    var strTermCnt = 0;
     
    if(pType == 'D') {  //일수 차이
        strTermCnt = (strEDT.getTime()-strSDT.getTime())/(1000*60*60*24);
    } else {            //개월수 차이
        //년도가 같으면 단순히 월을 마이너스 한다.
        // => 20090301-20090201 의 경우(윤달이 있는 경우) 아래 else의 로직으로는 정상적인 1이 리턴되지 않는다.
        if(pEndDate.substring(0,4) == pStartDate.substring(0,4)) {
            strTermCnt = pEndDate.substring(4,6) * 1 - pStartDate.substring(4,6) * 1;
        } else {
            //strTermCnt = Math.floor((strEDT.getTime()-strSDT.getTime())/(1000*60*60*24*365.25/12));
            strTermCnt = Math.round((strEDT.getTime()-strSDT.getTime())/(1000*60*60*24*365/12));
        }
    }
    
    return strTermCnt;
}

[Oracle] MERGE

[출처] http://blog.naver.com/jadin1/70018086104
제목
오라클 고급쿼리(2) - MERGE
작성자
작성일
2007-05-31
배포
PUBLIC (출처는 밝혀 주세요)
해당 강좌는 오라클 9i에서부터 제공하는 MERGE구문이다.

DB관련 작업을 하다보면, 특정 테이블에 데이터를 입력시, 기존에 존재하는 레코드이면 기존 행을 UPDATE하고, 존재하지않는다면 INSERT하는 로직을 상당수 작성하게된다.

IF 행있다. THEN
       UPDATE..
ELSE
       INSERT...
END IF ;

MS-SQL에 EXISTS 키워드를 아는 독자라면 오라클에는 왜 이러한 키워드가 없을까 하고 궁금해 하는 분도 있으리라 생각한다. 
필자는 이렇게 처리하곤 했었다. 적어도 MERGE를 모르기 전까지는.
SELECT COUNT(*) INTO ROW_CNT FROM TBL_NAME;

이렇게 싱글톤 SELECT 처리후 변수에 레코드 수를 저장한후
IF ROW_CNT > 0 THEN
       UPDATE..
ELSE
       INSERT...
END IF;

이런식으로 처리하곤 했었다.
아~ 자바 자료구조의 put() 메소드가 절실히 생각나지 않는가?
  
하지만 이젠 MERGE구문을 사용하여 고급스러운 쿼리를 구사하자.

MERGE구문은 다섯가지 정도의 절로 구성된다.

MERGE INTO                                    - 병합할 테이블 또는 뷰 정의
USING                                              - 갱신또는 추가될 대상 정의
ON                                                   - 어떤 구문이 추가/갱신될지의 조건정의. 조건이 TRUE이면 갱신, FALSE이면 추가
WHEN MATCHED THEN UPDATE          - 갱신될 열 정의
WHEN NOT MATCHED THEN INSERT     - 추가될 열 정의 
  
바로 예제를 보자.

이번 예제에서는 카테고리의 이름이 기존에 존재하면 CATE_MEMO컬럼에 기존카테고리이름 + '(갱신)' 이라는 이름으로 갱신할 것이고 , 존재 하지않으면 추가할 예정이다.
-----------------------------------------------------------------------------
MERGE INTO PRT_CATE TARGET_TBL
USING
  (
   SELECT * FROM PRT_CATE
   WHERE CATE_NAME = 
'컴퓨터'
  ) SOURCE_TBL
ON
  (SOURCE_TBL.CATE_NAME = TARGET_TBL.CATE_NAME)

WHEN MATCHED THEN
      UPDATE SET TARGET_TBL.CATE_MEMO = TARGET_TBL.CATE_NAME || 
갱신'
WHEN NOT MATCHED THEN
     INSERT VALUES
            (
10 , '컴퓨터(신규)' , 1 , '',0);
-----------------------------------------------------------------------------

결과값>


위와같이 컴퓨터 라는 이름이 존재하므로 UPDATE로직이 수행되었다.
  
물론 EXCEPTION 방어적 프로그래밍을 통해서도 위와 같이 구현할수 있다.
EXCEPTION
 WHEN NO_DATA_FOUNT THEN
    INSERT....;

위와 같이 구현해도 무방하다.

하지만 MERGE구문을 이용하면 같은 기능을 하나의 구문으로 처리할수 있다는 장점이 있다.

또한 웹 솔루션등에서 SELECT -> 결과후 해당 프로시져 호출 -> UPDATE,INSERT 등으로 이어지는

라운드 트립은 비단 DB SERVER 만의 자원뿐 아니라 웹서버의 성능에도 영향을 끼친다는것을 고려할때, 라운드 트립을 줄일 수 있는 방법도 될 수 있겠다.

[Oracle] Analytic Function LAG/LEAD

[출처] http://blog.naver.com/renovate/40007643531

PURPOSE
SCOPE & APPLICATION
KEY IDEA
ANALYTIC FUNCTION,PARTITION, WINDOW, CURRENT ROW, Lag, Lead, RATIO
SUPPOSITION
VERSION : ORACLE 8.1.6 이상
DESCRIPTION
기존 RDBMS에서는 다른 시간대에 있는 값들을 참조하기 위한 포인터를 가지고 있지 않다. 이것은 집합에서 모든 요소가 동등한 자격과 위치를 가지며 서로를 참조하기 위한 포인터를 가지고 있지 않기 때문이다.
Analytic Function의 Lag와 Lead는 다른 시간대에 있는 값들을 비교하기 위한 포인터를 제공하기 때문에 이러한 연산을 수행할 경우 아주 유리하다.
은 옵션이며 지정하지 않으면 1 이 default로 사용된다.
다음 SQL은 Lag와 Lead Function을 사용하여 자신이 소속한 부서에서 자기보다 월급이 많은 사원의 급여와 자기보다 월급이 적은 사원의 급여를 참조하기 위한 것이다.
select deptno,empno,sal,
       lag(sal,1)   over (order by deptno,sal) as sal_lag,
       lead(sal,1) over (order by deptno,sal) as sal_lead
  from emp
<< Result Set >>
DEPTNO
EMPNO
SAL
SAL_LAG
SAL_LEAD
10
7934
100
 
2450
10
7782
2450
100
5000
10
7839
5000
2450
800
20
7369
800
5000
1100
20
7876
1100
800
2975
20
7566
2975
1100
3000
20
7788
3000
2975
3000
20
7902
3000
3000
950
30
7900
950
3000
1250
30
7521
1250
950
1250
30
7654
1250
1250
1500
30
7844
1500
1250
1600
30
7499
1600
1500
2850
30
7698
2850
1600
 

     
적용 예>
SELECT a.t_dt, SUM(NVL(b.in_usr_cnt,0)) icnt,
       NVL(lag(SUM(in_usr_cnt),1) over (ORDER BY a.t_dt, SUM(in_usr_cnt)),0) bcnt
  FROM TCOPY a,
       ST_JOIN_DD_TB b
 WHERE a.t_yy = b.st_yy(+)
   AND a.t_mm = b.st_mm(+)
   AND a.t_dd = b.st_dd(+)
   AND a.t_dt BETWEEN '20041101' AND '20041108'
 GROUP BY a.t_dt
====================================================================================================

<<< 추가 정보 >>>

LAG is an analytic function. It provides access to more than one row of a table at the same time without a self join. Given a series of rows returned from a query and a position of the cursor, LAG provides access to a row at a given physical offset prior to that position.

If you do not specify offset, then its default is 1. The optional default value is returned if the offset goes beyond the scope of the window. If you do not specify default, then its default is null.

You cannot nest analytic functions by using LAG or any other analytic function for value_expr. However, you can use other built-in function expressions for value_expr.
[참고 ] Oracle 8.1.7 SQL reference [ LAG 함수 ]

[Oracle] 개행문자

Oracle에서 데이터의 개행문자는 CHR(13)||CHR(10) 로 처리함.

캐리지리턴(CR) : CHR(13)
라인피드(LF) : CHR(10)

TOAD에서 excel data upload

[출처] http://cafe.naver.com/tests3/257

메뉴>Database>import>Table data>schema
object type : table
object Name : table명
commit mode :
4가지를 셋팅후 show data를 하면 excure wizard 버튼이 활성화되고 그대로 따라하면 된다.

excure wizard > Text file(csv일경우) > import from file >TextSetting에서
field delimiter 박스에서 Comma선택 >data format(그냥 넘어가고) > filed가 제대로 구분되어졌으면,  automap을 누른다.
>Mapping  한 번 더 확인  > Next >실행

[Oracle] ROW_NUMBER를 이용한 RANKING처리

-- 여러 가구(APT_ROOM)에서 거래하는 은행(BANK_CD)들 중에서 가장 많이 거래하는 상위 4개의 은행을 순서(좌에서 우로)대로 나열하고 
-- 해당은행의 계좌를 출력해라.
WITH T_DUMMY AS
(
 SELECT '0901' AS APT_NO, '0101' AS APT_ROOM, '01' AS BANK_CD, '12345' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0101' AS APT_ROOM, '02' AS BANK_CD, '83636' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0102' AS APT_ROOM, '02' AS BANK_CD, '82228' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0103' AS APT_ROOM, '02' AS BANK_CD, '82727' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0103' AS APT_ROOM, '03' AS BANK_CD, '38338' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0103' AS APT_ROOM, '04' AS BANK_CD, '44758' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0104' AS APT_ROOM, '01' AS BANK_CD, '13338' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0104' AS APT_ROOM, '02' AS BANK_CD, '89988' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0104' AS APT_ROOM, '03' AS BANK_CD, '32113' AS ACNT_NO FROM DUAL UNION ALL
 SELECT '0901' AS APT_NO, '0104' AS APT_ROOM, '05' AS BANK_CD, '55879' AS ACNT_NO FROM DUAL
)
SELECT A.APT_NO, A.APT_ROOM
     , MIN(DECODE(Z.BANK_CD1, A.BANK_CD, A.ACNT_NO, NULL)) AS ACNT_NO1
     , MIN(DECODE(Z.BANK_CD2, A.BANK_CD, A.ACNT_NO, NULL)) AS ACNT_NO2
     , MIN(DECODE(Z.BANK_CD3, A.BANK_CD, A.ACNT_NO, NULL)) AS ACNT_NO3
     , MIN(DECODE(Z.BANK_CD4, A.BANK_CD, A.ACNT_NO, NULL)) AS ACNT_NO4
     , MIN(Z.BANK_CD1) AS BANK_CD1
     , MIN(Z.BANK_CD2) AS BANK_CD2
     , MIN(Z.BANK_CD3) AS BANK_CD3
     , MIN(Z.BANK_CD4) AS BANK_CD4
  FROM (
        SELECT MIN(DECODE(RNUM, 1, BANK_CD, NULL)) AS BANK_CD1
             , MIN(DECODE(RNUM, 2, BANK_CD, NULL)) AS BANK_CD2
             , MIN(DECODE(RNUM, 3, BANK_CD, NULL)) AS BANK_CD3
             , MIN(DECODE(RNUM, 4, BANK_CD, NULL)) AS BANK_CD4
          FROM (
                SELECT BANK_CD, COUNT(*) AS CNT
                     , ROW_NUMBER() OVER(ORDER BY COUNT(*) DESC, BANK_CD) AS RNUM
                  FROM T_DUMMY
                GROUP BY BANK_CD
               )
         WHERE RNUM <= 4
       ) Z
     , T_DUMMY A
 WHERE 1 = 1
GROUP BY A.APT_NO, A.APT_ROOM
ORDER BY 2, 3

2014/04/16

[Oracle] Hint

[출처] http://www.oracleclub.com/lecture/1260


<Optimization Approaches and Goals - Optimization  접근과 목적>
 

/*+ ALL_ROWS */

  ALL_ROWS는 Full Table Scan을 선호하며 CBO(Cost Based Optimization)는 default로
  ALL_ROWS를 선택 합니다.        
       
   SQL>SELECT /*+ ALL_ROWS */  ename, hiredate FROM emp  WHERE ename like ’%%%’
      
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: ALL_ROWS (Cost=1 Card=5 Bytes=80)
   1    0   TABLE ACCESS (FULL) OF ’EMP’ (Cost=1 Card=5 Bytes=80)



/*+ CHOOSE */

  Hint Level의 CHOOSE는 RBO(Rule Based Optimization)인지 CBO(Cost Based Optimization)
  인지를 선택 합니다.

   만약 주어진 table의 통계 정보가 없다면 Rule Based 접근 방식을 사용 합니다.



/*+ FIRST_ROWS */

   Full Table Scan보다는 index scan을 선호하며
   Interactive Application인 경우 best response time을 제공 합니다.

   또한 sort merge join보다는 nested loop join을 선호 합니다.

   SQL>SELECT /*+ FIRST_ROWS */  ename FROM emp WHERE empno=7876

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=1 Card=1 Bytes=20)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF ’EMP’ (Cost=1 Card=1 Bytes=20)
   2    1     INDEX (RANGE SCAN) OF ’PK_EMP’ (UNIQUE) (Cost=1 Card=1)



/*+ RULE */

   Rule Based 접근 방식을 사용하도록 지정 합니다.


<Access Methods - 접근 방법>


/*+ CLUSTER(table_name) */

  Cluster Scan을 선택하도록 지정한다. 따라서 clustered object들에만 적용 됩니다.



/*+ FULL(table_name) */

  Table을 Full Scan하길 원할 때 사용 합니다.



/*+ HASH(table) */

  Hash scan을 선택하도록 지정한다.
  이 hint는 HASHKEYS parameter를 가지고 만들어진 cluster내에 저장된 table에만 적용이 됩니다.



/*+ INDEX(table_name index_name) */

  지정된 index를 강제적으로 쓰게끔 지정 합니다.



/*+ INDEX_ASC(table_name index_name) */

  지정된 index를 오름차순으로 쓰게끔 지정 합니다.
  Default로 Index Scan은 오름차순 입니다



/*+ INDEX_DESC(table_name index_name) */

  지정된 index를 내림차순으로 쓰게끔 지정 합니다.


   SQL>SELECT /*+ index_desc(emp pk_emp) */  empno
           FROM   emp
           WHERE  rownum = 1 ;
       
    위 문장은 제일 큰 것 하나만 조회되므로, max function의 기능을 대신할 수 있습니다.   



 /*+ INDEX_FFS(table index) */

  Full table scan보다 빠른 Full index scan을 유도 합니다.



/*+ ROWID(table) */

  Rowid로 Table Scan을 하도록 지정 합니다.


<Join Orders>


/*+ ORDERED */

  From절에 기술된 테이블 순서대로 join이 일어나도록 유도 합니다.



<Join Operations>


/*+ USE_HASH (table_name) */

  각 테이블간 HASH JOIN이 일어나도록 유도 합니다.



/*+ USE_MERGE (table_name) */

  지정된 테이블들의 조인이 SORT-MERGE형식으로 일어나도록 유도 합니다.


<Parallel Execution>


/*+ NOPARALLEL(table_name) */

  NOPARALLEL hint를 사용하면, parallel query option을 사용하지 않도록 할 수 있다.

  SQL>SELECT /*+ NOPARALLEL */ *  FROM emp;



/*+ PARALLEL(table_name, degree) */

  PARALLEL hint를 사용하면 query에 포함된 table의 degree를 설정할 수 있습니다.

  예를 들어, 다음과 같이 hint를 적어 degree 4로 parallel query option을  실행하도록 할 수 있습니다.
  이 때 parallel이란 글자와 괄호( ’(’ )사이에 blank를 넣지 않도록 주의해야 합니다.
 
  SQL>SELECT /*+ PARALLEL(emp, 4) */   * FROM emp;



* DEGREE의 의미 및 결정

Parallel Query에서 degree란 하나의 operation 수행에 대한 server process의 개수 입니다.
이러한 degree 결정에 영향을 주는 요인들에는 다음과 같은 것들이 있습니다.

(1)  system의 CPU 갯수
(2)  system의 maximum process 갯수
(3)  table이 striping되어 있는 경우, 그 table이 걸쳐있는 disk의 갯수
(4)  data의 위치 (즉, memory에 cache되어 있는지, disk에 있는지)
(5)  query의 형태 (예를 들어 sorts 혹은 full table scan)

한 사용자만이 parallel query를 사용하는 경우, sorting이 많이 필요한
작업과 같은 CPU-bound 작업의 경우는 CPU 갯수의 1 ~ 2배의 degree가 적당하며,
sorting보다는 table scan과 같은 I/O bound 작업의 경우는 disk drive 갯수의 1 ~ 2배가 적당합니다.

동시에 수행되는 parallel query가 많은 경우에는 위의 각 사용자의 degree를
줄이거나 동시에 사용하는 사용자 수를 줄여야 합니다.
 
  ================================================
    * 데이터베이스 정보공유 커뮤니티 oracleclub.com
    * 강좌 작성자 : 김정식 (oramaster _at_ naver.com)
  ================================================
※ oracleclub 강좌를 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
※ oracleclub 강좌는 개인의 학습용으로만 사용 할 수 있습니다. 학원 홍보용이나 수익을 얻기 위한 용도로
    사용을 하시면 안됩니다. ^^

[Oracle] Dictionary User View

 VIEW
설명 
USER_OBJECTS          
USER_TABLES           
USER_TAB_COLUMNS(COLS)
USER_VIEWS            
USER_SYNONYMS(SYN)    
USER_SEQUENCES(SEQ)   
USER_CONSTRAINTS      
USER_CONS_COLUMNS     
USER_TAB_COMMENTS     
USER_COL_COMMENTS     
USER_INDEXES(IND)     
USER_IND_COLUMNS      
USER_CLUSTERS(CLU)    
USER_DB_LINKS         
USER_TRIGGERS         
USER_PROCEDURES       
USER_SOURCE           
USER_TABLESPACES      
USER_USERS            
USER_TAB_PRIVS        
USER_COL_PRIVS        
USER_SYS_PRIVS        
USER_TAB_PRIVS_MADE   
USER_TAB_PRIVS_RECD   
USER_COL_PRIVS_MADE   
USER_COL_PRIVS_RECD   
모든 USER 오브젝트들                 
테이블 정보                          
테이블에 있는 컬럼들의 정보          
뷰 정보                              
시노님 정보                          
시퀀스 정보                          
각종 제약 조건                       
제약 조건을 가진 컬럼들에 대한 정보  
테이블 주석 정보                     
커럼 주석 정보                       
인덱스 정보                          
인덱스 컬럼 정보                     
클러스터 정보                        
디비 링크 정보                       
트리거 정보                          
프로시저, 함수, 패키지 정보          
트리거, 함수, 프로시저, 패키지 소스  
테이블 스페이스 정보                 
사용자 정보                          
테이블 권한 정보                     
컬럼 권한 정보                       
시스템 권한 정보                     
내가 부여한 테이블 권한              
내가 부여받은 테이블 권한            
내가 부여한 컬럼 권한                
내가 부여받은 컬럼 권한        

분노 관리법

출처 : 공병호의 뉴스레터 No.673(2009-06-30)

< '화(분노)' 관리법 >
불쑥 불쑥 화를 내는 사람들이 있습니다.
이런 분들을 두고 '공격형 분노 표출' 유형이라고 부릅니다.
'꽁'하고 혼자서 오래 오래 속에 두고 부글거리는 분들은
'수동형 분노 표출'유형입니다.
분노를 아주 잘 다루는 분들은 흔히 '자기표현형 분노 표출' 유형이라 불립니다.
아무튼 '화'를 잘 다루는 방법은 어떤 것이 있을까요?

1. 공격형의 경우 원치 않는 일에 부닥치면 '왜'라는 단어를 사용하곤 한다.
'왜 그랬어? 왜? 왜?'
'왜 일이 이렇게 된 거야?'
'왜 저 사람은 나를 이런 식으로 대하지?'
하지만 '왜'라는 단어는 원망의 표현이요,
책임을 추궁하는 말로서 자신이나 타인의 화를 돋우고
일을 더 꼬이게 만들 뿐이다.

2. '왜'라는 단어보다는 '어떻게'라는 단어를 사용하는 것이 화난
감정을 가라앉히고 또한 문제를 해결하는 데 훨씬 더 도움이 된다.
아들이 열쇠를 빠뜨렸을 때 아이의 아빠처럼
'왜, 그랬어/"라는 말보다는 엄마와 딸처럼 '어떻게 하면 꺼낼 수 있을까?'라는
말이 문제 해결에 더 도움이 된다.

3. '왜'라는 단어가 원망과 분노를 키우는 단어라면
'어떻게'라는 단어는 원망과 분노를 잠재우고 행동하게 하는 단어이다.
'왜'라는 단어는 더 큰 낙심, 더 큰 분노 등 문제를 더 키우는 문제 지향적인
단어라면 '어떻게'라는 단어는 문제를 해결하게 하는 해결 지향적인 단어이다.

4. 당위적 사고 및 완벽주의 사고에 빠지지 마라
'~해야 한다', '~하지 않으면 안 된다'는 당위적 사고가 얼마나 나 자신과
다른 사람을 힘들게 하는지 알았을 것이다.
그러기에 당위적 사고를 '~했으면 좋겠다'는 소망적 사고로,
'~반드시 그렇지 않을 수도 있다'는 유연한 사고로 바꾸어 나가도록 해야 한다.

5. 공격형의 사람들이 분노를 관리할 수 있는
방법 중 하나는 화가 나려고 할 때 자신에게 '타임아웃'이라고
말하는 것이다.
타임아웃이 선언되면 운동선수가 하던 경기를 멈추듯 화내는 것을
무조건 멈추는 것이다.

6. 멈춘 후의 방법은 두 가지이다.
한 가지는 그 자리를 잠시 피하는 것이고 또 한 가지는 그 자리에 그냥
있는 것이다.
화를 멈추는 훈련이 어느 정도 된 사람은 그냥 있어도 되지만
전혀 훈련이 되지 않은 사람은 훈련이 될 때까지
자리를 잠시 피하는 방법을 택하는 것이 좋다.
공격형들이 화를 참지 못하는 것은 그동안 화를 참아 보지 않았기 때문이다.
하지만 화가 날 때 타임아웃을 선언하고 심호흡 등을 통해
화를 진정하면 내성이 생기게 된다.

7. 알고 보면 우리는 매일 위반 딱지를 발급하면 살고 있다.
'당신은 내가 원하는 머리 모양과 분홍색 투피스를 입지 않았으니 딱지 하나'
'넌 내가 바라는 대로 1등을 못 했으니까 딱지 셋'
'당신은 내 방식대로 청소하지 않았으므로 딱지 둘'
'넌 내가 바라는 대로 일을 다 처리하지 못했으니까 게으르고 책임감이 부족한
사람이야. 그래서 딱지 셋'

8. 스스로의 기준으로 자신 혹은 다른 사람을 판단하는 것은
이미 좌절과 낙심, 그리고 분노를 예약해 놓은 것이나 다름없다.
생각해 보라.
내가 정해 놓은 기준을 정확하게 그래도 지켜줄 사람이 어디 있겠는가?

-출처: 송남용, <내 감정 조절법>, 전나무숲.

[JavaScript] 숫자 금액을 한글로 변환하기

function changeHangul(obj) {
    var hanA = new Array("","일","이","삼","사","오","육","칠","팔","구","십");
    var danA = new Array("","십","백","천","","십","백","천","","십","백","천");
    var num = obj.value;
    var result = "";
    if(isNaN(num)) {
        for(i=0; i>num.length; i++) {
            str = "";
            han = hanA[num.charAt(num.length-(i+1))];
            if(han != "") str = han+danA[i];
            if(i == 4) str += "만";
            if(i == 8) str += "억";
            result = str + result;
        }
    }
    return result;
}

[Utility] revouninstaller

프로그램 제거(uninstall/remove) 유틸리티로 단순한 제거뿐만 아니라 레지스트리와 폴더까지
정리할 수 있는 고급모드를 지원한다.
헌터모드를 강조하는데 정확한 용도도 이해하지 못했으며 필요성 또한 느끼지 못했다.
추가 기능(PC관리 도구)으로 자동실행 프로그램을 관리, 쓰레기파일 삭제, 윈도우 클리어,
MS Office 클리어, 브라우저 클리어 기능 등 여러가지를 지원한다.
프리웨어로 한글도 지원하며 설치버전, 포터블버전이 있다.

http://www.revouninstaller.com

[Utility] CloseAll

CloseAll(Close All Windows)

http://www.ntwind.com/software/utilities/close-all.html

열려있는 모든 프로그램(창)들을 아주 빠르게(눈 깜짝할 사이에) 한꺼번에 종료시켜주는 유틸리티.
빠른 실행 영역이나 시작메뉴에 단축아이콘 등록시켜놓고 클릭만 하면 된다.
퇴근전에 한 번만 클릭하면... 참 편하다.

[Oracle] ORDER BY 절에서 NULL데이터 정렬 지정

ORDER BY 컬럼[ASC|DESC] NULLS FIRST => NULL 데이터를 먼저 출력
ORDER BY 컬럼[ASC|DESC] NULLS LAST => NULL 데이터를 나중에 출력

TOAD에서 한글 깨지는 현상 해결

레지스트리 값(NLS_LANG=KOREAN_KOREA.KO16MSWIN949) 수정 또는 생성.

HKEY_LOCAL_MACHINE > SOFTWARE > ORACLE
NLS_LANG 이 있는 경우 값을 지정.
NLS_LANG 이 없는 경우 '문자열 값'으로 생성 후 값을 지정.

제임스-랑게 이론

덕성여대 심리학과 김정호 교수는 “심리학 이론 중 제임스-랑게 이론이 있는데, 신체 변화가 거꾸로 정서를 결정한다는 것”이라고 말했다.

맞는 것 같다.

인상쓰면 짜증나고, 웃으면 기분이 좋아지고... 더불어 운동으로 몸이 건강해지면 일상의 모든 것이 만족스러울 것이다.

명예와 수치

"명예와 수치는 아무런 조건 없이 생겨난다.
네 맡은 바를 잘하면 그곳에 명예가 있으리."
-알렉산드 포프

출처 : 공병호의 뉴스레터 2009-02-13

SOS 모스부호

SOS 는 모스부호로 ...---...
장점(dash)은 단점(dot)의 3배 길이로 발신(?).

돈돈돈 쓰쓰쓰 돈돈돈

※ 발음(영어/한글) : 단점 - dot or dit[dit]/돈
장점 - dash or dah[dɑ́:]/쓰

여행할 때, 등산할 때 만일의 조난에 대비해 알아두는 것도 나쁘진 않을 듯.

야외에서는 호루라기, 조명(랜턴/후래쉬) 등을 활용하면 되며
실내에서는 호루라기나 소리와 진동이 잘 전달되는 벽면/파이프 등을 두드리면 됨
(기본적인 대응법이며 상황에 따라 활용하면 됨).