===========
객체의 사용
===========

일단 객체에 대한 개념이 있다는 전제하에 시작하겠습니다...
만일 객체에 대한 개념이 없으시다면 다른 웹문서나 서적을 참고하시기 바랍니다..

오라클은 객체-관계형 데이타베이스 관리 시스템입니다..
즉, 객체와 관계형 모델을 모두 지원해서 사용한다는 뜻입니다..
물론 기준은 관계형 모델입니다...
객체를 관계형 모델에서 사용하는 것처럼 사용합니다...
즉, 객체지향의 중심개념인 캡슐화, 상속성, 다형성을 제공한다는 것입니다..
객체형으로 선언하면 그 객체는 메소드와, 속성을 가질 수 있습니다..
오브젝트 뷰라는 것도 있는데...이것은 오라클 관리 부분에서 다룹니다..그쪽을 참고하시구여..
개념만 이야기 하자면 테이블에서 뷰를 만들고 이를 객체형으로 만든다는 것입니다..
일단 객체를 정의하는 것부터 살펴보겠습니다..
다시한번 말씀드리지만 이 부분은 객체에 대한 개념이 없으시다면 읽어도 무슨 소리인지 모를겁니다..
위에서 말한것 처럼 객체의 개념을 익히시고 이 문서를 보시면 좋겠습니다..
C++이나 다른 객체지향형의 언어나 툴을 다루셨다면 이제까지 데이타베이스를 하면서 느끼지 못한 즐거움을 
느끼실 겁니다..물론 객체형 데이타베이스를 하신분은 빼구여...헐헐~

바로 실습에 들어가지여..언급한데로 PL/SQL의 예제는 (주)사이버출판사의 PL/SQL임돠...
문법은 메뉴얼을 참고하시구여...
쩝...맨날 메뉴얼 참고하라는데..메뉴얼이 있어야 어디서 보지...라구 외치시는 분덜은 제 자료실을 찾아보세요..
제가 주로 참고하는 문서라고 있습니다..

자..그러면 객체-관계형 DBMS인 오라클이 왜 객체-관계형이라고 하는지 실제로 해보면서 살펴봅시다..
책에는 처음에 하기엔 너무 길고 어려운 예들이 많슴다...헐헐~
이것은 나중에 하구여...제가 만들어서 해보겠슴다..
객체에 대한 개념이 있다는 가정하에 밑에 것을 해보시게 된다면 금방 이해가 될 것이라 확신함돠..
혹시나 객체에 대한 개념이 없으신 분덜이 아래 예를 해보신다면 객체가 먼지를 알지도 모르겠슴다.. 

SQL> create type test_object as object(
  2  id varchar(4),
  3  name varchar(10));
  4  /

유형이 생성되었습니다.

SQL> create table test_object_table of test_object;       -------------> 객체를 테이블화 했슴다...

테이블이 생성되었습니다.

SQL> insert into test_object_table
  2  values (test_object('1111', 'scott'));               ---------------> 관계형 처럼 입력이 되는 것이 보입니다..

1 개의 행이 만들어졌습니다.

SQL> insert into test_object_table
  2  values (test_object('2222', 'yasi'));

1 개의 행이 만들어졌습니다.

SQL> select * from test_object_table;                 --------------> 객체타입을 사용해서 만든 테이블이 관계형에서와 똑같이 조회됩니다..
                                                                      이것이 바로 오라클을 객체-관계형 DBMS 라고 부르는 이유임돠..
ID   NAME
---- ----------
1111 scott
2222 yasi

SQL> create table test_object_table2
  2  ( id_name test_object,                        --------------> test_object는 앞에서 생성한 객체타입입니다...재사용성을 보여주고 있습니다..
  3    group_id varchar2(10));                                     

테이블이 생성되었습니다.

SQL> select * from test_object_table2;

선택된 레코드가 없습니다.

SQL> insert into test_object_table2
  2  values (test_object('1111', 'system'), 'sysoper');

1 개의 행이 만들어졌습니다.

SQL> insert into test_object_table2
  2  values (test_object('2222', 'yasi'), 'users');

1 개의 행이 만들어졌습니다.

SQL> select * from test_object_table2;

ID_NAME(ID, NAME)                        GROUP_ID
---------------------------------------- ----------
TEST_OBJECT('1111', 'system')            sysoper
TEST_OBJECT('2222', 'yasi')              users

SQL> select t.id_name.id as id,                   -----------> 객체지향 언어에서 사용하는 점(.) 표시법으로 조회하고 있씀다...
  2         t.id_name.name as name,                            재밌슴다...^^
  3         t.group_id as group_id
  4  from test_object_table2 t;

ID   NAME       GROUP_ID
---- ---------- ----------
1111 system     sysoper
2222 yasi       users

SQL> create or replace type test_object2 as object(
  2  id_name test_object,                             ----------> 앞에서 생성한 객체의 특성을 그대로 사용해서 또 다른 객체를 생성함돠....
  3  address varchar2(40));                                       
  4  /

유형이 생성되었습니다.


헐~ 상당히 재밌씀다...저는 이것을 익히면서 너무나도 재미를 느꼈었슴다...
이것을 보시는 분덜도 그러리라 생각합니다...헐헐~
이제 감을 잡으셨다고 생각하고 복잡한 예제를 보겠슴다..

SQL> CREATE OR REPLACE TYPE address AS OBJECT (
  2    street_1      VARCHAR2(40),
  3    street_2      VARCHAR2(40),
  4    city          VARCHAR2(40),
  5    state_abbr    VARCHAR2(2),
  6    zip_code      VARCHAR2(5),
  7    phone_number  VARCHAR2(10),
  8    MEMBER PROCEDURE ChangeAddress (                      -------------------> 메소드까정 생성
  9      st_1 IN VARCHAR2, st_2 IN VARCHAR2, cty IN VARCHAR2,
 10      state IN VARCHAR2, zip IN VARCHAR2),
 11    MEMBER FUNCTION getStreet (line_no IN number) RETURN VARCHAR2,
 12    MEMBER FUNCTION getCity RETURN VARCHAR2,
 13    MEMBER FUNCTION getStateAbbr RETURN VARCHAR2,
 14    MEMBER FUNCTION getPostalCode RETURN VARCHAR2,
 15    MEMBER FUNCTION getPhone RETURN VARCHAR2,
 16    MEMBER PROCEDURE setPhone (newPhone IN VARCHAR2)
 17  );
 18  /

유형이 생성되었습니다.


address라는 객체타입을 생성했습니다...
프로시저와 함수를 선언했지여...프로시저는 리턴값이 없는 것을 야그하고, 함수는 리턴값이 있는 것을 말합니다...
이러한 메소드를 생성하는 이유는 생성한 객체타입에 대한 조작을 하기 위한 것이지여...
패키지 생성하는 것처럼 몸체도 생성해 주어야 합니다...
컬럼 비스무리하게 선언한 것은 속성임돠...오라클의 객체타입은 속성을 1000개까정 가질수 있슴다..
그럼 몸체를 생성해 보겠슴다..

SQL> CREATE OR REPLACE TYPE address AS OBJECT (
  2    street_1      VARCHAR2(40),
  3    street_2      VARCHAR2(40),
  4    city          VARCHAR2(40),
  5    state_abbr    VARCHAR2(2),
  6    zip_code      VARCHAR2(5),
  7    phone_number  VARCHAR2(10),
  8    MEMBER PROCEDURE ChangeAddress (
  9      st_1 IN VARCHAR2, st_2 IN VARCHAR2, cty IN VARCHAR2,
 10      state IN VARCHAR2, zip IN VARCHAR2),
 11    MEMBER FUNCTION getStreet (line_no IN number) RETURN VARCHAR2,
 12    MEMBER FUNCTION getCity RETURN VARCHAR2,
 13    MEMBER FUNCTION getStateAbbr RETURN VARCHAR2,
 14    MEMBER FUNCTION getPostalCode RETURN VARCHAR2,
 15    MEMBER FUNCTION getPhone RETURN VARCHAR2,
 16    MEMBER PROCEDURE setPhone (newPhone IN VARCHAR2)
 17  );
 18  /

유형이 생성되었습니다.

SQL> CREATE OR REPLACE TYPE BODY address AS
  2    MEMBER PROCEDURE ChangeAddress (
  3      st_1 IN VARCHAR2, st_2 IN VARCHAR2, cty IN VARCHAR2,
  4      state IN VARCHAR2, zip IN VARCHAR2) IS
  5    BEGIN
  6      IF (st_1 IS NULL) OR (cty IS NULL) OR
  7         (state IS NULL) OR (zip IS NULL)
  8         OR (upper(state) NOT IN ('AK','AL','AR','AZ','CA','CO',
  9                                  'CT','DC','DE','FL','GA','HI',
 10                                  'IA','ID','IL','IN','KS','KY',
 11                                  'LA','MA','MD','ME','MI','MN',
 12                                  'MO','MS','MT','NC','ND','NE',
 13                                  'NH','NJ','NM','NV','NY','OH',
 14                                  'OK','OR','PA','RI','SC','SD',
 15                                  'TN','TX','UT','VA','VT','WA',
 16                                  'WI','WV','WY'))
 17  OR (zip <> ltrim(to_char(to_number(zip),'09999'))) THEN
 18       RAISE_application_error(-20001,'The new Address is invalid.');
 19      ELSE
 20        street_1 := st_1;
 21        street_2 := st_2;
 22        city := cty;
 23        state_abbr := upper(state);
 24        zip_code := zip;
 25      END IF;
 26    END;
 27  
 28    MEMBER FUNCTION getStreet (line_no IN number)
 29      RETURN VARCHAR2 IS
 30    BEGIN
 31      IF line_no = 1 THEN
 32        RETURN street_1;
 33      ELSIF line_no = 2 THEN
 34        RETURN street_2;
 35      ELSE
 36        RETURN ' ';    --send back a blank.
 37      END IF;
 38    END;
 39  
 40    MEMBER FUNCTION getCity RETURN VARCHAR2 IS
 41    BEGIN
 42      RETURN city;
 43    END;
 44  
 45    MEMBER FUNCTION getStateAbbr RETURN VARCHAR2 IS
 46    BEGIN
 47      RETURN state_abbr;
 48    END;
 49  
 50    MEMBER FUNCTION getPostalCode RETURN VARCHAR2 IS
 51    BEGIN
 52      RETURN zip_code;
 53    END;
 54  
 55    MEMBER FUNCTION getPhone RETURN VARCHAR2 IS
 56    BEGIN
 57      RETURN phone_number;
 58    END;
 59  
 60    MEMBER PROCEDURE setPhone (newPhone IN VARCHAR2) IS
 61    BEGIN
 62      phone_number := newPhone;
 63    END;
 64  END;
 65  /

유형 본문이 생성되었습니다.


헐헐~ 상당히 길지만....패턴은 비슷함돠...
MEMBER PROCEDURE ChangeAddress 는 속성에 대한 변경을 하려고 만든 프로시저입니다...
속성 각각에 입력되거나 갱신할때 데이타의 무결성을 유지하기 위한 제어문이 들어갔슴다..
각각의 멤버 함수는 값을 리턴하기 위해서 생성된 것들입니다...

오라클의 객체타입에는 생성자 메소드라는 것이 내장되어 있습니다...
이것은 그 객체형의 인스턴스를 생성하는데 사용됩니다..
그 생성자 메소드는 다음과 같은 식이지여..

FUNCTION address(street_1 in varchar2, street_2 in varchar2, city in varchar2, state_abbr,.............) returns address

이런 생성자 메소드는 항상 같은 형의 객체를 반환합니다...
생성자 함수의 사용은 다음과 같이 하지여..

address_variable := address('101 Oak', '', 'Detroit', 'MI', '48223', '3135358886');


함수의 몸체에서 다음을 살펴봅시다..

MEMBER FUNCTION getPhone RETURN VARCHAR2 IS
    BEGIN
      RETURN phone_number;
    END;

속성을 반환하는데 사용되는 메소드입니다...이 메소드를 Accessor Method라 합니다...
일반적으로 get이란 접두어를 사용합니다..

그 밑에 있는 setPhone은 mutator Method라는 것입니다...코드상에서 보는 것과 같이 속성을 직접 참조하지 않습니다..
즉, 속성을 직접 건더리지 않고 간접적으로 건더리는 것입니다...   

MEMBER PROCEDURE setPhone (newPhone IN VARCHAR2) IS
    BEGIN
      phone_number := newPhone;
    END;    
    
자...그럼 이러한 메소드를 사용해서 객체타입의 인스턴스를 생성하고, 조회해 보겠슴다..

SQL> set serveroutput on
SQL> 
SQL> --A PL/SQL block demonstrating the
SQL> --use of the address object.
SQL> DECLARE
  2    address_1   address;
  3    address_2   address;
  4    address_3   address;
  5  BEGIN
  6    --Instantiate a new address object named address_1,
  7    --and assign a copy of it to address_2.
  8    address_1 := address ('2700 Peerless Road','Apt 1',              -------------> address는 생성자 함수지여? address_1이 새로운 인스턴스가 됩니다..
  9                          'Cleveland','TN','37312','4235551212');
 10    address_2 := address_1;
 11  
 12    --Change address #1
 13    address_1.ChangeAddress ('2800 Peermore Road','Apt 99',
 14                             'Detroit','MI','48823');                 --------------> ChangeAddress 프로시저를 사용해서 인스턴스를 갱신함돠..
 15  
 16    --Instantiate a second object.
 17    address_3 := address ('2700 Eaton Rapids Road','Lot 98',
 18                          'Lansing','MI','48911','5173943551');
 19  
 20    --Now print out the attributes from each object.
 21    dbms_output.put_line('Attributes for address_1:');           -------------> 여기부터는 생성한 인스턴스를 이쁘게 보여주기 위한 짓임돠..
 22    dbms_output.put_line(address_1.getStreet(1));                -------------> 객체형으로 생성해서 엑서스 메소드를 사용합니다..객체의 사용처럼 점표시법으로...
 23    dbms_output.put_line(address_1.getStreet(2));
 24    dbms_output.put_line(address_1.getCity
 25                         || ' ' || address_1.getStateAbbr
 26                         || ' ' || address_1.getPostalCode);
 27    dbms_output.put_line(address_1.getPhone);
 28  
 29    dbms_output.put_line('-------------------------');
 30    dbms_output.put_line('Attributes for address_2:');
 31    dbms_output.put_line(address_2.getStreet(1));
 32    dbms_output.put_line(address_2.getStreet(2));
 33    dbms_output.put_line(address_2.getCity
 34                         || ' ' || address_2.getStateAbbr
 35                         || ' ' || address_2.getPostalCode);
 36    dbms_output.put_line(address_2.getPhone);
 37  
 38    dbms_output.put_line('-------------------------');
 39    dbms_output.put_line('Attributes for address_3:');
 40    dbms_output.put_line(address_3.street_1);
 41    dbms_output.put_line(address_3.street_2);
 42    dbms_output.put_line(address_3.city
 43                         || ' ' || address_3.state_abbr
 44                         || ' ' || address_3.zip_code);
 45    dbms_output.put_line(address_3.phone_number);
 46  END;
 47  /
Attributes for address_1:
2800 Peermore Road
Apt 99
Detroit MI 48823
4235551212
-------------------------
Attributes for address_2:
2700 Peerless Road
Apt 1
Cleveland TN 37312
4235551212
-------------------------
Attributes for address_3:
2700 Eaton Rapids Road
Lot 98
Lansing MI 48911
5173943551

PL/SQL 처리가 정상적으로 완료되었습니다.

노가다입니다...헐헐~ 이상스럽게 이러한 예제는 비효율적이라고 생각합니다...
웬지 그런 느낌이 드네여..헐헐~
암튼...
여기까지 했으면 감이 잡혔지여??

다른 것을 살펴보구 갱신하는 짓을 해봅시다...

SQL> CREATE TABLE employee
  2         (emp_id           INTEGER,
  3           emp_name      VARCHAR2(32),
  4           supervised_by      INTEGER,
  5           pay_rate               NUMBER(9,2),
  6           pay_type              CHAR);

테이블이 생성되었습니다.

SQL> alter table employee
  2  add (home_address address);      ------------------> 객체형으로 정의된 컬럼을 추가합니다.

테이블이 변경되었습니다.

SQL> INSERT INTO employee
  2    (emp_id, emp_name,pay_rate,pay_type,home_address)
  3    VALUES (597,'Matthew Higgenbottom',120000,'S',
  4            address('101 Maple','','Mio','MI','48640','5173943551'));       --------> 데이타 삽입

1 개의 행이 만들어졌습니다.

SQL> 
SQL> COMMIT;

커밋이 완료되었습니다.

SQL> 
SQL> DECLARE
  2    emp_home_address    address;           ---------------------> 객체형으로 변수 선언
  3  BEGIN
  4    emp_home_address := address('911 Pearl','Apt 2','Lewiston',
  5                                'MI','48645','5173363366');
  6    INSERT INTO employee
  7      (emp_id, emp_name,pay_rate,pay_type,home_address)
  8      VALUES (598, 'Raymond Gennick',55,'H',emp_home_address);
  9    COMMIT;
 10  END;
 11  /

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> SELECT emp_id, emp_name, home_address
  2    FROM employee
  3   WHERE home_address IS NOT null;

    EMP_ID EMP_NAME                         HOME_ADDRESS(STREET_1, STREET_2, CITY, STATE_ABBR, ZIP_CODE, PHONE_NUMBER)
---------- -------------------------------- --------------------------------------------------------------------------------
       597 Matthew Higgenbottom             ADDRESS('101 Maple', NULL, 'Mio', 'MI', '48640', '5173943551')
       598 Raymond Gennick                  ADDRESS('911 Pearl', 'Apt 2', 'Lewiston', 'MI', '48645', '5173363366')
       

설명을 안해도 될꺼 같습니다...
이제 갱신하는 것을 해보겠습니다...기본적으로 PL/SQL 어케돌아가는지 아신다면 쉽습니다...

SQL> DECLARE
  2    emp_addr    address;
  3  BEGIN
  4    --Retrieve the object from the table
  5    SELECT home_address INTO emp_addr
  6      FROM employee
  7     WHERE emp_id = 597;
  8  
  9    --Use a mutator method to change the phone number.
 10    emp_addr.setPhone('3139830301');               --------------> 뮤테이터 메소드를 사용해서 갱신함돠..
 11  
 12    UPDATE employee
 13       SET home_address = emp_addr
 14     WHERE emp_id = 597;
 15  
 16    COMMIT;
 17  END;
 18  /

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> SELECT emp_id, emp_name, e.home_address.phone_number home_phone
  2    FROM employee e
  3   WHERE emp_id = 597;

    EMP_ID EMP_NAME                         HOME_PHONE
---------- -------------------------------- ----------
       597 Matthew Higgenbottom             3139830301
     
SQL> UPDATE employee e
  2     SET e.home_address  = address(e.home_address.street_1,
  3                         e.home_address.street_2, e.home_address.city,
  4                         e.home_address.state_abbr, e.home_address.zip_code,
  5                         '5173433333')
  6   WHERE emp_id = 598;

1 행이 갱신되었습니다.

SQL> COMMIT;

커밋이 완료되었습니다.


마지막에서 살펴본 갱신문도....처음에 해본것들과 비슷합니다...점표시법을 사용해서 잘 구분해주고...
각각을 조회, 갱신, 입력, 삭제할 수 있습니다...
그러면 오라클은 어떻게 객체를 사용하는데 각각을 구분하는 방법을 생각해봅시다...
구분하는 방법은 각 테이블의 컬럼으로 정의된 객체의 각 속성에 일치합니다... 따라서 테이블의 각 행은
객체의 한 인스턴스를 저장하는데 사용됩니다...
이렇게 구분하는 방법말구...오라클은 Object idenfifier란 오라클의 생성하는 값으로 구분을 합니다..
이 값은 각각 생성한 객체를 데이타베이스 전체에서 고유하게 구별하는 것 입니다...

다음은 객체형 테이블을 만드는 예를 살펴보겠슴다..

SQL> CREATE OR REPLACE TYPE building AS OBJECT (
  2    BldgName          VARCHAR2(40),
  3    BldgAddress       address,
  4    BldgMgr           INTEGER,
  5    MEMBER PROCEDURE  ChangeMgr (NewMgr IN INTEGER),
  6    ORDER MEMBER FUNCTION Compare (OtherBuilding IN building)      ---------> ORDER 형 멤버 함수를 사용
  7        RETURN INTEGER
  8    );
  9  /

유형이 생성되었습니다.


여기서 봐야 될것은  ORDER 입니다...
이제까지 했던 다른 것들하고 먼가가 틀립니다...
ORDER 함수는 두 객체를 비교하는 코드를 작성할 수 있게 해줍니다..
두 객체가 같은지 아니면 어떤 것이 더 큰지 또는 작은지 가리키는 값을 반환합니다..
나중에 더 살펴보구여...일단 이런것이라는 것만 알고 계시면 되리라 생각합니다..

SQL> CREATE OR REPLACE TYPE BODY building AS
  2    MEMBER PROCEDURE  ChangeMgr(NewMgr IN INTEGER) IS
  3      BEGIN
  4        BldgMgr := NewMgr;
  5      END;
  6  
  7    ORDER MEMBER FUNCTION Compare (OtherBuilding IN building)
  8    RETURN INTEGER IS
  9        BldgName1     VARCHAR2(40);
 10        BldgName2     building.BldgName%TYPE;            ------------> '변수이름 테이블이름.컬럼이름%TYPE' 의 형식을 같습니다..
 11      BEGIN
 12        --Grab the two building names for comparison.
 13        --Make sure that we don't get messed up by leading/trailing
 14        --spaces or by case.
 15        BldgName1 := upper(ltrim(rtrim(BldgName)));         ---------------> 앞뒤 공백을 모두 없애고 대문자로 치환해서 BldgName1에 넣구 있슴다..
 16        BldgName2 := upper(ltrim(rtrim(OtherBuilding.BldgName)));
 17  
 18        --Return the appropriate value to indicate the order of
 19        --this object vs OtherBuilding.
 20        IF BldgName1 = BldgName2 THEN                -----------------> 두 값의 비교를 위한 제어문입니다..
 21          RETURN 0;
 22        ELSIF BldgName1 < BldgName2 THEN
 23          RETURN -1;
 24        ELSE
 25          RETURN 1;
 26        END IF;
 27      END;
 28  END;
 29  /

유형 본문이 생성되었습니다.

SQL> CREATE TABLE buildings OF building;         ---------> 객체형 테이블 생성

테이블이 생성되었습니다.

SQL> INSERT INTO buildings
  2    values (building('Victor Building',
  3            address('203 Washington Square',' ','Lansing',
  4                    'MI','48823',' '),
  5            597));

1 개의 행이 만들어졌습니다.

SQL> 
SQL> INSERT INTO buildings
  2    values (building('East Storage Shed',
  3            address('1400 Abbott Rd','','Lansing','MI','48823',''),
  4            598));

1 개의 행이 만들어졌습니다.

SQL> INSERT INTO buildings
  2    values (building('Headquarters Building',
  3            address('150 West Jefferson','','Detroit','MI','48226',''),
  4            599));

1 개의 행이 만들어졌습니다.

SQL> 
SQL> SELECT * from buildings;

BLDGNAME                                 BLDGADDRESS(STREET_1, STREET_2, CITY, STATE_ABBR, ZIP_CODE, PHONE_NUMBER) BLDGMGR
---------------------------------------- ------------------------------------------------------------------------- ----------
Victor Building                          ADDRESS('203 Washington Square', ' ', 'Lansing', 'MI', '48823', ' ')       597
East Storage Shed                        ADDRESS('1400 Abbott Rd', NULL, 'Lansing', 'MI', '48823', NULL)            598
Headquarters Building                    ADDRESS('150 West Jefferson', NULL, 'Detroit', 'MI', '48226', NULL)        599

SQL> COMMIT;

커밋이 완료되었습니다. 


다음은 객체형 테이블에 대한 검색을 하는 또다른 예를 보겠습니다...
객체형 테이블을 검색하는 또다른 방법은 VALUE라는 연산자를 사용하는 것입니다..
SQL> select value(e) from buildings e;

VALUE(E)(BLDGNAME, BLDGADDRESS(STREET_1, STREET_2, CITY, STATE_ABBR, ZIP_CODE, PHONE_NUMBER), BLDGMG
----------------------------------------------------------------------------------------------------
BUILDING('Victor Building', ADDRESS('203 Washington Square', ' ', 'Lansing', 'MI', '48823', ' '), 59
BUILDING('East Storage Shed', ADDRESS('1400 Abbott Rd', NULL, 'Lansing', 'MI', '48823', NULL), 598)
BUILDING('Headquarters Building', ADDRESS('150 West Jefferson', NULL, 'Detroit', 'MI', '48226', NULL

SQL> desc building
 이름                                                                                                              널?      유형
 ---------------------------------------------------------------------------------------------------
 BLDGNAME                                                                                                                   VARCHAR2(40)
 BLDGADDRESS                                                                                                                ADDRESS
 BLDGMGR                                                                                                                    NUMBER(38)

METHOD
------
 MEMBER PROCEDURE CHANGEMGR
 인수명                         유형                    기본 내부/외부?
 ------------------------------ ----------------------- --------- --------
 NEWMGR                         NUMBER                  IN

METHOD
------
 ORDER MEMBER FUNCTION COMPARE RETURNS NUMBER
 인수명                         유형                    기본 내부/외부?
 ------------------------------ ----------------------- --------- --------
 OTHERBUILDING                  BUILDING                IN

SQL> 


다음은 이러한 객체형의 문제점을 살펴보구 오라클이 이 문제점을 해결하는 방안을 살펴보겠습니다...
예를 들면, 어떤 빌딩에 1000명의 사원이 있다면, 각 사원대 해새 하나씩 1000개의 독립된 building 객체가 있게 됩니다..
결국 너무 많은 정보를 갖게 되는 것이죠...
이런한 것을 해결하기 위해 오라클은 오브젝트에 대한 참조만 저장합니다..참조의 생성은 REF를 사용합니다..
즉, 객체를 사용하는 것이 아니라 참조만 한다는 뜻입니다..
오라클 관리 부분에도 언급되었던 것이죠..
참조를 생성해 봅시다...

SQL> ALTER TABLE employee
  2    ADD (emp_bldg    REF building);

테이블이 변경되었습니다.

예에서 보면 building 객체를 사용하는 것이 아니라 참조합니다...즉, 오라클은 객체 식별자를 처리하는데 REF와 DEREF 연산자를
사용한다는 것입니다..
REF 연산자는 오브젝트에 대한 오브젝트 식별자를 반환하는 SQL문에서 사용되고,
DEREF 연산자는 오브젝트 식별자에 의해 참조되는 실제 오브젝트를 검색하는 SQL문에서 사용합니다.

REF연산자와 DEREF 연산자를 사용하는 예를 연속으로 보겠씁니다..

SQL> CREATE OR REPLACE PROCEDURE AssignEmpToBldg (
  2    EmpNumIn IN employee.emp_id%TYPE,
  3    BldgNameIn IN buildings.BldgName%TYPE
  4    ) AS
  5  BEGIN
  6    UPDATE employee
  7       SET emp_bldg = (SELECT REF(b)             -------------> REF연산자 사용, buildings 테이블에서 사용한 객체에 대한 참조만 한다.
  8                        FROM buildings B
  9                       WHERE BldgName = BldgNameIn)
 10     WHERE emp_id = EmpNumIn;
 11  
 12    --Raise an error if either the employee number or
 13    --building name is invalid.
 14    IF SQL%NOTFOUND THEN
 15      RAISE_application_error(-20000,'Employee ' || EmpNumIn
 16                             || ' could not be assigned to building '
 17                             || BldgNameIn);
 18    END IF;
 19  END;
 20  /

프로시저가 생성되었습니다.

SQL> 
SQL> BEGIN
  2    AssignEmpToBldg (598,'Victor Building');
  3    AssignEmpToBldg (597,'East Storage Shed');
  4  END;
  5  /

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL> CREATE OR REPLACE FUNCTION GetEmpBldgName (
  2    EmpNumIn IN employee.emp_id%TYPE
  3    ) RETURN VARCHAR2 AS
  4  TheBldg     building;
  5  BEGIN
  6    --Select the building object reference from this employee's record.
  7    SELECT DEREF(emp_bldg) INTO TheBldg                   -------------------> 실제 객체에 대한 검색을 한다.즉, 참조를 해제한다.
  8      FROM employee
  9     WHERE emp_id = EmpNumIn;
 10  
 11    IF TheBldg IS NULL THEN
 12      RETURN 'No Building Assigned';
 13    ELSE
 14      RETURN TheBldg.BldgName;
 15    END IF;
 16  END;
 17  /

함수가 생성되었습니다.

SQL> 
SQL> BEGIN
  2    dbms_output.put_line(GetEmpBldgName(598));
  3    dbms_output.put_line(GetEmpBldgName(597));
  4  END;
  5  /
Victor Building
East Storage Shed

PL/SQL 처리가 정상적으로 완료되었습니다.

주의깊게 볼것은 어떤 것이 참조를 하는지 알아서 참조해제를 하는 것이냐? 입니다..
DEREF 연산자에 의해서 사용되는 오브젝트 참조에는 오라클이 객체를 찾는데 필요한 모든 정보가 
들어 있습니다..그래서 관계형 테이블에서 사용되는 조인문에서와 같이 엑세스할 테이블을 지정하지 
않아도 되는 것입니다..

참고로 메뉴얼의 문법을 보면 SELF 파라미터가 있는데...이것은 메소드를 작성시 기본값입니다.
호출되는 객체의 속성을 참조하는데 사용됩니다.

이제 객체타입에 대한 기나긴 장정의 마지막인 객체 비교하는 것을 해보구....제약사항을 알아 보겠습니다..
객체 비교에는 두가지 메소드를 지원합니다...
그 두가지는 앞에서 잠시 살펴본 ORDER와 MAP 메소드입니다..
먼저 ORDER에 대해서 살펴보게습니다..

앞에서 살펴본 예제에서 다음과 같은 구문이 있었습니다..

ORDER MEMBER FUNCTION Compare (OtherBuilding IN building) 
        RETURN INTEGER         

ORDER 예약어는 Compare 함수가 한 building 객체를 다른 building 객체와 비교할 때 호출되도록 특별히 작성된 
함수라는 것을 말해주는 것입니다..
함수가 취하는 인수는 오브젝트와 같은 형이어야 합니다..
ORDER함수는 SELF 파라미터에서 알아본 것처럼 비교하여 다음과 같은 값을 반환합니다..

반환값           의미
------------     ------------------
     -1          SELF가 인수보다 작다.
     0           SELF가 인수와 같다.
     1           SELF가 인수보다 크다.

다음은 예입니다..

SQL> set serveroutput on
SQL> 
SQL> --A demonstration of the ORDER function.
SQL> DECLARE
  2    bldg_a      building;   --will be less than bldg_b
  3    bldg_b      building;
  4    bldg_b2     building;
  5    bldg_c      building;
  6  BEGIN
  7    --First, create four building objects.
  8    bldg_a := building('A Building',null,null);
  9    bldg_b := building('Another Building',null,null);
 10    bldg_b2 := building('Another Building',null,null);
 11    bldg_c := building('Cosmotology Research Lab',null,null);
 12  
 13    --Now compare the building objects and display the results;
 14    IF bldg_a < bldg_b THEN
 15      dbms_output.put_line('bldg_a < bldg_b');
 16    END IF;
 17  
 18    --These two have the same name, so should be equal.
 19    IF bldg_b = bldg_b2 THEN
 20      dbms_output.put_line('bldg_b = bldg_b2');
 21    END IF;
 22  
 23    IF bldg_c > bldg_b2 THEN
 24      dbms_output.put_line('bldg_c > bldg_b2');
 25    END IF;
 26  END;
 27  /
bldg_a < bldg_b
bldg_b = bldg_b2
bldg_c > bldg_b2

PL/SQL 처리가 정상적으로 완료되었습니다.

MAP 메소드는 ORDER 함수대신 사용할 수 있는 다른 방법입니다..
MAP 함수는 객체의 하나 또는 그 이상의 속성에 따라 하나의 값을 계산합니다..그리고
그 값을 한 객체와 다른 객체(같은 객체형)를 비교하는데 사용합니다...
MAP함수의 결과는 다음 데이타형중 하나이어야 합니다..

- NUMBER
- DATE
- VARCHAR2

마지막으로 오라클의 객체타입에 대한 제약사항을 알보구 마치겠씁니다..

- 상속성을 지원하지 않는다.    --------------> 책에 나온 내용인데...이상함돠..제가 알기론 상속성을 지원하는데 말이죠..아무래도 완벽하지는 않은듯..
- PRIVATE 속성을 지원하지 않는다.
- 사용자 정의 생성자를 지원하지 않는다.
- 객체형은 데이타베이스 수준에서 정의되어야 하며, PL/SQL 함수나 프로시저 내에서 정의할 수 없다.
- 일부 데이타형은 속성으로 사용될 수 없다.
- 객체의 속성의 개수를 최대 1000개 이상 사용할 없다.