java로 생각한 초간단 조인의 개념 

Class 직원
{
int id;
int department_id;
String name; 
직원(int id){ this.id = id;}
void setDeparment_id(int department_id){ this.department_id = department_id; } <-- null 허용 + agreegation아 아닌, 
                                                                                                                     association 이기에 setter
}

컨테이너 클래스 직원List
ArrayList<직원> list직원 =  new ArrayLsit<직원>(100);

for(int id=0; id<list직원.size; id++)
list.append( new 직원(id, (int)(Math.rand()*10 + 1) );  //유일한 직원 인스턴스 생성해서 컨테이너에 추가


Class 부서
{
int id;
String address; 
부서(int id){ this.id = id;}
}

컨테이너 클래스 부서List
ArrayList<부서> listB =  new ArrayLsit<부서>(10);

for(int id=0; id<list부서.size; id++)
list.append( new 부서(id));   //부서 종류별 유일한 인스턴스 생성해서 컨테이너에 추가


컨테이너 클래스 list직원이 컨테이너 클래스 list부서에 대하여
참조할수 있는 필드(fk = department_id)를 바탕으로 비교 연산을 수행한다.

=이중 반복문

Set<Entry<직원이름,부서주소>> set = new Set<Entry<직원이름,부서주소>>();  //리절트셋

for(직원 a : list직원)    <-- from 절 
                (1) 개념 간단화를 위해 생략
      for(부서 b : list부서)  <-- from 절
         if( a.departmet_id == b.id)  <-- where 절 중 equal 비교 연산    <-- 만약 요 조건절이.. 유일키(uk)가 아닐경우.. 
                                                                                                  리스트 생성후 한번 더 루프
          set.add( a.name + b.address) ; <-- 여기가 select 절에서 선언한 리절트셋 형태
                                                
      (1)물론 요 라인에서 selete절에 포함시킨 내가 얻어올 컬럼들 1~컬럼수 만큼 반복문이 포함될테고..
          if( a.id == 3) <-- 이건 selete절에서 인라인 쿼리(특정 조건에 맞는 로우를 추출)인가를 사용햇을때 일테고.. 
                              3번 직원(물론 모든직원을 검색할지, 특정 3번 직원을 검색할지는.. 상황따라)       
      (2)카티션 곱이란..
      if(a.departmet_id == b.id) 가 빠졋을 경우..  직원의 인스턴스(row=레코드) 갯수 * 부서의 인스턴스(row=레코드) 갯수만큼의
      return 결과가 발생함.  return 결과는 resultSet 개념이고.. 

이게 중첩 루프 조인인듯 하고..(아님 말고 -_-;) 그 외에 해쉬 조인, 머지 소트 조인의 경우는 아직 모르겟다 ;;
일단 해쉬는 문자열등 긴 데이터를 ->(가급적 중복안되는) 짧은 숫자로 사상시키는 건데..
머지 소트는 기억 진짜 안남;; 

selete 쿼리에서.. selete 절의 의미는! 테이블도, 컬럼도, 조건도 뭣도 아닌.. 말 그대로 dbms에게 파일에서 메모리로 읽어올
타입(리절트셋 테이블)에 대한 선언이며, 또한 dbms 에게 리턴할 값에 xx 작업 명령을 한다는 의미인것 같다.

from 절은 실제 파일에 접근할 위치를 지정하는 것이고
where 절은 접근한 파일들에 대해 조건(제약=추출)을 거는 것이고..

왜 selete만 dql 이고,.. 그외에 insert , update, delete 는 dml인지를 이해해야한다! ddl은 논외


그럼으로 아래의 코드는 이해되어야 한다.

insert /*+ append */ into t1 
  아래는 values 대신 .. 서브 쿼리를 사용한다.
  select  rownum,    <-- selete 절은 내게 리턴할 결과 값의 형태(templet)에 대해 내가 지정할수 있는 구문.
  mod(rownum, 1000),      <-- 1000으로 나눈 나머지
  floor((rownum-1)/1000),   <-- 소숫점 버리기: 로직이 멋짐.. 예전 floor 할때 + 좌로 시프트하고 + 0.5 한후에 
                                                                                           우측으로 시프트하고 (int)로 형변환 했던 방법
  lpad('x', 1000, 'x')  <-- LPAD 함수설명 LPAD함수는 문자열이 일정 길이(byte)가 될 때까지 왼쪽에 특정 문자를 덧붙인다.
                                     구문 LPAD(char1,n, [,char2] ) 
  from   <-- from 절이 실제로 파일의 물리적 주소에 접근할 위치 지정
    all_source a1, all_source a2 
       where rownum <= 1e6;     <-- 물리적 주소에 접근해서 메모리(selete절)로 읽어들일지 말지의 조건문이지만
                                              당연하게도 카티션 곱이 일어난다.
****************************************************


(etc)쿼리 최적화 관한 오라클 문서

*******************************************************
오늘 오라클 내장 메서드를 배우던중에..

의문점이. 데이터에 대한 조회 업무 외의 작업인.. 데이터 가공 to_char(format) to_date(format)

등의 업무가.. dbms의 몫인가?  db에 요청하는 app측의 몫인가에 대한 의문이었다.

이건 거의 예전에 프로젝트했던.. word 객체가 .. 다형적 타입의 format 객체를 주입받아(stratergy= 동적으로
객체의 행동을 바꿔줄수 있음.. 인터페이스는 그대로)패턴..  자신의 print에서 {format.printf(msg) 처럼 포맷 
객체에게 일을 시키는 느낌인데} 

또한 위의 느낌은.. jsp 페이지에서 ejb 또는 사용자 정의 태크or jsp 내장 태그를 이용해..
프리젠테이션과 비지니스를 분리하는 것과 같은 느낌 아닌가???

하여간.. 아직 책임영역.. dbms가 자신의 존재 이유인..데이터의 관리에 과연.. 프리젠테이션 처리가 필요할지에 
대한 위의 의문은 안풀렸고... <-- 선생님 말씀으론 가급적 내장 함수 안쓰고  app가 가공하는게 낫다고 하심!


*************************************************************************
(etc)오라클이 제공하는 사용자 정의 함수의 장점 설명..



SELECT 절에 붙는 논리적 테이블(hdd가 아닌 메모리)인 리절트셋 = 값에 대해서.. (char_to()등으로 표현까지 정해줄수있다)
FROM 절은 접근할 물리적 테이블을 지정할수 있는 키워드 이고(cf:DUAL 은 더미 테이블.. 의미 없다)
WHERE 절은 물리적 테이블에 대한 접근 조건이랄까? .. 한마디로 hdd에 대해 memory로 가져올 조건을 지정할수 있고

그외에 아래는 값들의 집합의 표현을 어떻게 지원할 것인지에 대한 것들..
GROUP BY 절은 위에 WHERE 절을 통해 추출할 SELETE 뒤의 논리적 테이블에 대해서.. 그것을 특정 컬럼에 대해서
그룹화해서 논리적 테이블을 구성하겠다는 키워드이고..
HAVAING 의 경우 그룹 메서드를 통해 추출한 그룹화된 값들에 대해  논리 조건등을 추가로 설정할 수 있는 키워드



*************************************************************

조인의 경우..

 SELECT e.first_name, e.last_name, e.salary, d.department_name
      FROM hr.employees e, hr.departments d
       WHERE d.department_name IN ('Marketing', 'Sales')
       AND e.department_id = d.department_id;
       
    이 쿼리의 플랜은 
      ------------------------------------------------------------------------------------------------
      | Id  | Operation                    | Name              | Rows  | Bytes | Cost(%CPU)| Time      |
      -------------------------------------------------------------------------------------------------
      |   0 | SELECT STATEMENT             |                   |    19 |   722 |     3   (0)| 00:00:01 |
      |   1 |  NESTED LOOPS                |                   |       |       |            |          |
      |   2 |   NESTED LOOPS               |                   |    19 |   722 |     3   (0)| 00:00:01 |
      |*  3 |    TABLE ACCESS FULL         | DEPARTMENTS       |     2 |    32 |     2   (0)| 00:00:01 |
      |*  4 |    INDEX RANGE SCAN          | EMP_DEPARTMENT_IX |    10 |       |     0   (0)| 00:00:01 |
      |   5 |   TABLE ACCESS BY INDEX ROWID| EMPLOYEES         |    10 |   220 |     1   (0)| 00:00:01 |
      -------------------------------------------------------------------------------------------------
 
      Predicate Information (identified by operation id):
      ---------------------------------------------------
         3 - filter("D"."DEPARTMENT_NAME"='Marketing' OR "D"."DEPARTMENT_NAME"='Sales')
         4 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")


***************************************************************
아참. 깜박했는데. DBMS의 경우 일반적으로 제일 잦은 연산인 데이터에 대한 빠른 접근 = 조회
를 위해.. 자료 구조중 이진트리(b tree) 형식으로 접근 주소 인덱스 테이블을 구성하는듯 하다. 
(밸런스드 형태..) (물론 각각의 데이터들의 실제 메모리상 위치야 제각각 일테지만..주소만 인덱스를 사용해
트리형태로 구성해주면.. ok지~)

그렇기에 서치가 빠르고, 아.. 머리가 안돌아간다.. 나중에 좀 더 생각해볼것! 
(인덱스의 이해, 조건절을 제대로 구성한 다는 것이 어떠한 의미일지에 대해..)

내가 원하는 결과를 얻기위해 특정 테이블을 선택하고.. 다른 n개의 테이블을 조인해야 할때..

테이블을 조인하는 '순서'에 따라.. fatorial(n) 의 경우의 수가 발생하고
각 테이블마다 인덱스 사용의 true/false에 따라.. 2의 n승의 경우의 수가 발생하고
조인 방식의 종류인.. 해쉬조인,중첩루프조인,머지소트 조인;;; 에 따하 3의 n승의 경우의 수가 발생한다고
한다.. OTL 


1.인덱스 접근 방식
    (1) range 스캔 (possible duplicated column key)
    (2) unique 스캔 (pk,uk.. 영어도 못하는데.. 걍 한글로 쓰자 -_-;)
    (3) full 스캔
    (4) fast full 스캔
    (5) skip 스캔 (!=)
    (6) min/max 스캔
    (7) index Join


(etc)http://prayyou.egloos.com/viewer/1799600   이해가 안간다.. 속상하다 ㅠㅠ

보통 매우 큰 인덱스를 Range/Full/Skip scan을 한 후 테이블 Access를 수항하면, 엄청난 양의 Single Block Read가 발생합니다. 

클러스터링 Fact에 따라, 
Index Leaf Block 의 Rowid 갯수에 따라 Table lookup 발생하면서 Table Block Read가 발생합니다. (내 예측이 맞았는듯.. 인덱스는 이진 트리로 구성하고..거기에 실제 파일 데이터를 연결 리스트or 리스트

형식으로 배치한듯..)
순차적 수행 플랜의 경우, 쿼리 성능은 Single Random table Read가 얼마나 빠른가에 좌우 되었습니다.

만약 단일 블록 하나를 읽는데 5ms 이 필요하다면, 1000 block을 읽는데 약 5초 정도가 소요됩니다. 
다만 여기에서도 
스토리지 시스템이 동시 IO 요청을 처리할 수 있으며, 최종 사용자 세션이 필요한 블록을 동시에 수행하는 어떤 방법이나, 병렬로 처리할 수 잇따면
OS와 스토리지와의 연결 상에서 부하가 될 수 있겠지만 사용자는 더 짧은 시간에 결과를 받아 볼 수 있습니다.



오라클은 어떻게 IO를 최적화했는가?

현재까지 제가 아는 한, 오라클은 12.1 버전 이전에서도 몇가지 교묘한 방법으로 IO를 다루어 왔습니다. 아래는 그 몇몇가지 방법입니다.

- Nested Loop Join 에해서 오라클에 사용자에 대한 몇가지 전략이 사용됨 : NL-Join Batching 과 Table Access를 Join 밖으로 이동시켰습니다.

     --NL-JOIN Batching (http://docs.oracle.com/cd/E11882_01/server.112/e41573/optimops.htm#PFGRF94642)
     
     
11g에서는 Physical IO의 지연을 감소시키기 위하여 NL Join 에 대한 새로운 방식을 소개합니다. 
     Table Block 이나, index Block이 Buffer Cache에 존재하지 않지만, Join 에 필요한 경우 Physical IO가 필요합니다. 
     오라클 데이터베이스 11g는 한번에 하나씩 처리하는 대신 여러 Physical IO 요청을 묶어 Vector IO를 사용하여 처리할 수 있습니다.

     


이전 버전의 Exeution Plan에서 한 line으로 나타났던 Nested Loop Join의 Row source가 두번의 Nested Loop Join Row source로 나타나게 되었습니다. 
     이 경우, Oracle 데이터베이스는 첫번째 NL Join Row source를 Inner 쪽 인덱스와 Outer 쪽 Table에 Join 으로 할당합니다. 
     두번째 Row Source는 index에 저장된 Rowid를 포함한 첫번째 join 결과 set과 Inner 쪽 Table사이의 Join 에 할당합니다.
     
     즉, 
     SELECT e.first_name, e.last_name, e.salary, d.department_name
      FROM hr.employees e, hr.departments d
       WHERE d.department_name
 IN ('Marketing', 'Sales') <-어제 봤던 부분..  IN(a or b)키워드= 풀스캔..
       AND e.department_id = d.department_id;
       
    이 쿼리의 플랜은 
      ------------------------------------------------------------------------------------------------
      | Id  | Operation                    | Name              | Rows  | Bytes | Cost(%CPU)| Time      |
      -------------------------------------------------------------------------------------------------
      |   0 | SELECT STATEMENT             |                   |    19 |   722 |     3   (0)| 00:00:01 |
      |   1 |  NESTED LOOPS                |                   |       |       |            |          |
      |   2 |   NESTED LOOPS               |                   |    19 |   722 |     3   (0)| 00:00:01 |
      |*  3 |    TABLE ACCESS FULL         | DEPARTMENTS       |     2 |    32 |     2   (0)| 00:00:01 |
      |*  4 |    INDEX RANGE SCAN          | EMP_DEPARTMENT_IX |    10 |       |     0   (0)| 00:00:01 |
      |   5 |   TABLE ACCESS BY INDEX ROWID| EMPLOYEES         |    10 |   220 |     1   (0)| 00:00:01 |
      -------------------------------------------------------------------------------------------------
 
      Predicate Information (identified by operation id):
      ---------------------------------------------------
         3 - filter("D"."DEPARTMENT_NAME"='Marketing' OR "D"."DEPARTMENT_NAME"='Sales')
         4 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")
         
         
         
        로 수행됩니다. 

   이 경우, HR.department Table의 row 는 첫 번째 Join의 Outer Side로되며, 첫번째 Join의 Inner Side는 Index EMP_DEPARTMENT_IX가 됩니다.
      첫 번째 Join 의 결과집합은 다시 두번 째 Join 의 Outer Side가 되며, Hr.Employee Table은 Inner Side가 됩니다. 
      
   이전 버전의 실행계획과 동일하게 표시되는, 두번째 Join row source가 할당되지 않는 경우는 다음과 같습니다. 
      - Inner Side에서 필요한 모든 컬럼이 Index 상에 있어 Table Access가 필요없는 경우. 이 경우, Oracle은 오직 한번만 Join 한다 <--이게 핵심인듯하다. 조인을 위해 첫번째 선택해주는 인덱스에 대해 loop를 돌 필요가 없다면..
그만큼 퍼포먼스상 이익이 있다.  => 자주 사용될 테이블을 미리 만들어 둔다는게 이런 의미였구나..  특정 직원에 대한 연봉 정보를 자주 조회할 도메인일때 이것을 직원테이블에서 id와 이름 + 특정 조건 으로 새롭게 테이블을 구성해두면.. 나중에 이것을 
급여 테이블과 조인 연산시에.. 이중 반복문이 아닌, 단일 반복문이 될것이다. selete *(아스타리스크)를 사용하도록 노력할?
것!  .. 물론 쓸데 없는 데이터를 파일에서 읽어오게 되면.. 그 성능 하락이 더 크다... 외부 i/o는 적을수록 빠른것! (선생님의 
명언!!!)
 
      - 이전 버전과 Row 의 순서가 다르게 리턴될 수 있다. 따라서, Oracle DB가 특정한 순서로 Row를 추출해야 되는경우, 예를 들어, Order by Sort를 제거할 필요가 있을경우
        기존의 NL Join 방식을 사용할 수 있다.
      - OPIMIZER_FEATURES_ENABLE 초기 파라메터가 11g 이전 버전으로 set 되어 있는 경우, 기존의 NL Join 을 수행한다. 


http://prayyou.egloos.com/viewer/1799600



*******************************************************************

statement vs preparestatement


http://translate.googleusercontent.com/translate_c?depth=1&hl=ko&prev=/search%3Fq%3Dstatement%2Bvs%2Bpreparedstatement%26newwindow%3D1%26espv%3D210%26es_sm%3D93&rurl=translate.google.co.kr&sl=en&u=http://stackoverflow.com/questions/3271249/difference-between-statement-and-preparedstatement&usg=ALkJrhg7XYWCDIhZNBVwbSd1ll4UI_YUqg



DiverManger.getConnection() 에서 하는 짓..


//  Worker method called by the public getConnection() methods.
    private static Connection getConnection(
        String urljava.util.Properties infoClassLoader callerCLthrows SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        synchronized(DriverManager.class) {
          // synchronize loading of the correct classloader.
          if(callerCL == null) {
              callerCL = Thread.currentThread().getContextClassLoader();
           }
        }
        if(url == null) {
            throw new SQLException("The url cannot be null""08001");
        }
        println("DriverManager.getConnection(\"" + url + "\")");
        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;
        for(DriverInfo aDriver : ) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.drivercallerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(urlinfo);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }
            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }
        }
        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }
        println("getConnection: no suitable driver found for "url);
        throw new SQLException("No suitable driver found for "url"08001");
    }


by givingsheart 2014. 1. 2. 09:08