전체 글 (358)

데이타 베이스 파일의 autoextend 옵션 및 next extent 옵션을 수정하려면 아래와 같은 쿼리를 사용하면 된다.

 

 

SQL > ALTER DATABASE DATAFILE 6 AUTOEXTEND ON NEXT 10M;

 

여기서 6 이라는 숫자는 어디서 나온걸까? 의심을 해보아야 한다. 무작정 따라하지 마라.

 

저 6 이라는 숫자는 데이타 베이스 파일 조회하기 명령어를 통해서 얻은 파일# 이다. 


데이타베이스 튜닝을 하다보면 물리적인 데이타베이스 파일에 대해 알아야 할 때가 있다.

그럴때 필요한 것이 물리적인 데이타베이스 파일의 경로를 파악하는 것.

 

SQL> CONN /AS SYSDBA;
Connected.

SQL> SELECT * FROM DBA_DATA_FILES; 

 

확인할 수 있는 정보들은 아래와 같다.

 

FILE_NAME  

FILE_ID  

TABLESPACE_NAME                       

BYTES      

BLOCKS  

STATUS     

RELATIVE_FNO  

AUT    

MAXBYTES   

MAXBLOCKS  

INCREMENT_BY  

USER_BYTES  

USER_BLOCKS  

ONLINE_
 

 

 

출처 : https://docs.oracle.com/cd/B28359_01/server.111/b28310/dfiles010.htm#ADMIN11459 


자 오늘은 concat()과 listagg()에 대해서 알아보도록 하겠습니다.

 

DB에서 정보를 추출할 때 여러줄(리스트 형태)로 나오는 것들을 한줄로 합쳐서 뽑아내고 싶거나  

 

한 줄에 여러 필드들이 있을때 이 필드들을 모두 합쳐서 한줄의 스트링으로 뽑아내고 싶을 때가 있을거에요.

 

예를들면 EMP테이블에 한명의 employee만 등록이 되어있다고 가정합시다.

이때 이 사람의 정보를 emp_nm 과 emp_id를 나눠서 emp객체의 name과 id필드에 뽑아올 수도 있지만 궂이 이걸 하나의 String으로 뽑아내고 싶을때가 있을 수도 있다고 가정하고 -_-ㅋ

 

emp_nm | emp_id

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

장군이   |     3

 

이렇게 데이터가 들어있을 때 "3장군이" 라는 스트링을 뽑아오려면 concat을 쓰면 됩니다.

 

select concat( emp_id, emp_nm) from emp; 

 

이제 데이타가 한줄 더 들어와서 두명의 employee가 있다고 가정합시다.

 

emp_nm | emp_id

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

장군이   |     3

이쁭이   |     5

 

그리고 이 직원들의 이름을 쉼표로 구분해서 하나의 스트링으로 뽑아내고 싶다고 가정합시다.


"장군이,이쁭이" 이렇게 말이죠.

 

이럴때는 listagg()를 쓰면 됩니다. 아래처럼 말이죠.

 

select listagg(emp_nm, ',') within group ( order by emp_id ) from emp; 

 

listagg(리스트애그)에 대한 자세한 내용은 아래 출처에 있는 주소로 가시면 좀 더 자세한 설명을 보실 수 있습니다.

 

그럼 좋은하루 되세요~ ^_^ 

 

 

 

 

출처 : http://oracle-base.com/articles/misc/string-aggregation-techniques.php ( listagg() )  

 

링크가 break될 것을 우려하여 밑에 위 링크로가면 볼 수 있는 내용을 가져왔습니다. ^^

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

String Aggregation Techniques

On occasion it is necessary to aggregate data from a number of rows into a single row, giving a list of data associated with a specific value. Using the SCOTT.EMP table as an example, we might want to retrieve a list of employees for each department. Below is a list of the base data and the type of output we would like to return from an aggregate query.

Base Data:

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

Desired Output:

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

This article is based on a thread from asktom.oracle.com and contains several methods to achieve the desired results.

LISTAGG Analystic Function in 11g Release 2

The LISTAGG analytic function was introduced in Oracle 11g Release 2, making it very easy to aggregate strings. The nice thing about this function is it also allows us to order the elements in the concatenated list. If you are using 11g Release 2 you should use this function for string aggregation.

COLUMN employees FORMAT A50

SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM   emp
GROUP BY deptno;

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

3 rows selected.

WM_CONCAT Built-in Function (Not Supported)

If you are not running 11g Release 2, but are running a version of the database where the WM_CONCAT function is present, then it is a zero effort solution as it performs the aggregation for you. It is actually an example of a user defined aggregate function described below, but Oracle have done all the work for you.

COLUMN employees FORMAT A50

SELECT deptno, wm_concat(ename) AS employees
FROM   emp
GROUP BY deptno;

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

3 rows selected.

Note. WM_CONCAT is an undocumented function and as such is not supported by Oracle for user applications (MOS Note ID 1336219.1). If this concerns you, use a User-Defined Aggregate Function described below.

User-Defined Aggregate Function

The WM_CONCAT function described above is an example of a user-defined aggregate function that Oracle have already created for you. If you don't want to use WM_CONCAT, you can create your own user-defined aggregate function as described at asktom.oracle.com.

CREATE OR REPLACE TYPE t_string_agg AS OBJECT
(
  g_string  VARCHAR2(32767),

  STATIC FUNCTION ODCIAggregateInitialize(sctx  IN OUT  t_string_agg)
    RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateIterate(self   IN OUT  t_string_agg,
                                       value  IN      VARCHAR2 )
     RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateTerminate(self         IN   t_string_agg,
                                         returnValue  OUT  VARCHAR2,
                                         flags        IN   NUMBER)
    RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateMerge(self  IN OUT  t_string_agg,
                                     ctx2  IN      t_string_agg)
    RETURN NUMBER
);
/
SHOW ERRORS


CREATE OR REPLACE TYPE BODY t_string_agg IS
  STATIC FUNCTION ODCIAggregateInitialize(sctx  IN OUT  t_string_agg)
    RETURN NUMBER IS
  BEGIN
    sctx := t_string_agg(NULL);
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateIterate(self   IN OUT  t_string_agg,
                                       value  IN      VARCHAR2 )
    RETURN NUMBER IS
  BEGIN
    SELF.g_string := self.g_string || ',' || value;
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateTerminate(self         IN   t_string_agg,
                                         returnValue  OUT  VARCHAR2,
                                         flags        IN   NUMBER)
    RETURN NUMBER IS
  BEGIN
    returnValue := RTRIM(LTRIM(SELF.g_string, ','), ',');
    RETURN ODCIConst.Success;
  END;

  MEMBER FUNCTION ODCIAggregateMerge(self  IN OUT  t_string_agg,
                                     ctx2  IN      t_string_agg)
    RETURN NUMBER IS
  BEGIN
    SELF.g_string := SELF.g_string || ',' || ctx2.g_string;
    RETURN ODCIConst.Success;
  END;
END;
/
SHOW ERRORS


CREATE OR REPLACE FUNCTION string_agg (p_input VARCHAR2)
RETURN VARCHAR2
PARALLEL_ENABLE AGGREGATE USING t_string_agg;
/
SHOW ERRORS

The aggregate function is implemented using a type and type body, and is used within a query.

COLUMN employees FORMAT A50

SELECT deptno, string_agg(ename) AS employees
FROM   emp
GROUP BY deptno;

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

3 rows selected.

Specific Function

One approach is to write a specific function to solve the problems. The get_employees function listed below returns a list of employees for the specified department.

CREATE OR REPLACE FUNCTION get_employees (p_deptno  in  emp.deptno%TYPE)
  RETURN VARCHAR2
IS
  l_text  VARCHAR2(32767) := NULL;
BEGIN
  FOR cur_rec IN (SELECT ename FROM emp WHERE deptno = p_deptno) LOOP
    l_text := l_text || ',' || cur_rec.ename;
  END LOOP;
  RETURN LTRIM(l_text, ',');
END;
/
SHOW ERRORS

The function can then be incorporated into a query as follows.

COLUMN employees FORMAT A50

SELECT deptno,
       get_employees(deptno) AS employees
FROM   emp
GROUP by deptno;

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

3 rows selected.

To reduce the number of calls to the function, and thereby improve performance, we might want to filter the rows in advance.

COLUMN employees FORMAT A50

SELECT e.deptno,
       get_employees(e.deptno) AS employees
FROM   (SELECT DISTINCT deptno
        FROM   emp) e;

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

Generic Function using Ref Cursor

An alternative approach is to write a function to concatenate values passed using a ref cursor. This is essentially the same as the previous example, except that the cursor is passed in making it generic, as shown below.

CREATE OR REPLACE FUNCTION concatenate_list (p_cursor IN  SYS_REFCURSOR)
  RETURN  VARCHAR2
IS
  l_return  VARCHAR2(32767); 
  l_temp    VARCHAR2(32767);
BEGIN
  LOOP
    FETCH p_cursor
    INTO  l_temp;
    EXIT WHEN p_cursor%NOTFOUND;
    l_return := l_return || ',' || l_temp;
  END LOOP;
  RETURN LTRIM(l_return, ',');
END;
/
SHOW ERRORS

The CURSOR function is used to allow a query to be passed to the function as a ref cursor, as shown below.

COLUMN employees FORMAT A50

SELECT e1.deptno,
       concatenate_list(CURSOR(SELECT e2.ename FROM emp e2 WHERE e2.deptno = e1.deptno)) employees
FROM   emp e1
GROUP BY e1.deptno;

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

3 rows selected.

Once again, the total number of function calls can be reduced by filtering the distinct values, rather than calling the function for each row.

COLUMN employees FORMAT A50

SELECT deptno,
       concatenate_list(CURSOR(SELECT e2.ename FROM emp e2 WHERE e2.deptno = e1.deptno)) employees
FROM   (SELECT DISTINCT deptno
        FROM emp) e1;

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

3 rows selected.

ROW_NUMBER() and SYS_CONNECT_BY_PATH functions in Oracle 9i

An example on williamrobertson.net uses the ROW_NUMBER() and SYS_CONNECT_BY_PATH functions to achieve the same result without the use of PL/SQL or additional type definitions.

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

3 rows selected.

COLLECT function in Oracle 10g

An example on oracle-developer.net uses the COLLECT function in Oracle 10g to get the same result. This method requires a table type and a function to convert the contents of the table type to a string. I've altered his method slightly to bring it in line with this article.

CREATE OR REPLACE TYPE t_varchar2_tab AS TABLE OF VARCHAR2(4000);
/

CREATE OR REPLACE FUNCTION tab_to_string (p_varchar2_tab  IN  t_varchar2_tab,
                                          p_delimiter     IN  VARCHAR2 DEFAULT ',') RETURN VARCHAR2 IS
  l_string     VARCHAR2(32767);
BEGIN
  FOR i IN p_varchar2_tab.FIRST .. p_varchar2_tab.LAST LOOP
    IF i != p_varchar2_tab.FIRST THEN
      l_string := l_string || p_delimiter;
    END IF;
    l_string := l_string || p_varchar2_tab(i);
  END LOOP;
  RETURN l_string;
END tab_to_string;
/

The query below shows the COLLECT function in action.

COLUMN employees FORMAT A50

SELECT deptno,
       tab_to_string(CAST(COLLECT(ename) AS t_varchar2_tab)) AS employees
FROM   emp
GROUP BY deptno;
       
    DEPTNO EMPLOYEES
---------- --------------------------------------------------
        10 CLARK,KING,MILLER
        20 SMITH,JONES,SCOTT,ADAMS,FORD
        30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES
        
3 rows selected.




이상입니다!!!


아...슬프도다....이 에러의 원인을 찾느라 얼마나 헤맸던지...

쿼리문의 인자로 들어가는 녀석들이 13개이고 이 쿼리문을 스트링 + 스트링 형식으로 묶어놨는데 콤마가 빠진 이유로 변수명이 부적합하다는 오류를 찍어대니 이거 원인을 어떻게 찾으라고.!!!!!!

 

구글링 해보니......

 

콤마가 빠져도 저런 오류를 뿌린다는 것...

 

잘 보니 인자가 많아서 줄바꿈할 때 쉼표를 하나 빼먹었던 것.


특정 테이블의 속성을 조회할 때에는 아래 두가지 테이블을 보면됩니다.

1. dba권한으로 모든 테이블을 조회할 경우 : dba_tables

2. 일반 사용자 권한으로 내 소유의 테이블을 조회할 경우 : user_tables

 

아래 예제를 보면

 

SQL > select * from user_tables;

 

를 실행하면 꽤나 긴 페이지를 보게 될 것입니다.  

putty를 이용해서 DB에 접속한 뒤 pagesize와 linesize를 설정하지 않았을 경우 특히 더 길게 나올거에요. 

만약 pagesize와 linesize를 설정하지 않았다면 아래와 같이 실행해보세요.

 

SQL > set pagesize 100

SQL > set linesize 3000

 

위 두 줄을의 내용은 한 페이지에 100 라인이 들어가고 한 라인에 3천개의 문자가 들어간다고 정의하는 것입니다.

 

그리고 다시 select문을 실행시켜보면 그나마 보기좋게 나올 것입니다. 그래도 아직 영 맘에 안들죠? 

위 select문을 실행시키면 테이블명과 해당 테이블이 속해있는 테이블 스페이스명을 시작으로 온갖 속성들의 값이 나올것입니다.  

아래는 user_tables에 어떤 컬럼들이 있는지를 뽑아본 것인데 아래 컬럼중에서 원하는 것만 뽑아서 조회를 하는것이 보이에 훨씬 좋을테니 몇개만 골라서 조회해보세요. 

 

TABLE_NAME                      

TABLESPACE_NAME                 

CLUSTER_NAME                                                                               IOT_NAME                                                                                    

STATUS      

PCT_FREE    

PCT_USED   

INI_TRANS   

MAX_TRANS  

INITIAL_EXTENT  

NEXT_EXTENT  

MIN_EXTENTS  

MAX_EXTENTS  

PCT_INCREASE   

FREELISTS  

FREELIST_GROUPS  

LOG  

B    

NUM_ROWS      

BLOCKS  

EMPTY_BLOCKS   

AVG_SPACE   

CHAIN_CNT  

AVG_ROW_LEN A

VG_SPACE_FREELIST_BLOCKS  

NUM_FREELISTOW_MOVE  

GLO  

USE  

DURATION                                             

SKIP_COR MON CLUSTER_OWNER                                                                               

DEPENDEN  

COMPRESS  

COMPRESS_FOR  

DRO  

REA  

SEG  

RESULT_

아래 쿼리문은 dba 유저로 모든 테이블 스페이스의 사용량( 총용량, 사용량, 남은용량 )에 대한 정보를 얻어온다.

 

select     a.TABLESPACE_NAME,
     a.BYTES MB_total,
     b.BYTES MB_free,
     b.largest,
     a.BYTES-b.BYTES MB_used,
     round(((a.BYTES-b.BYTES)/a.BYTES)*100,2) percent_used
from
     (
          select      TABLESPACE_NAME,
               sum(BYTES)/1048576 BYTES
          from      dba_data_files
          group      by TABLESPACE_NAME
     )
     a,
     (
          select      TABLESPACE_NAME,
               sum(BYTES)/1048576 BYTES ,
               max(BYTES)/1048576 largest
          from      dba_free_space
          group      by TABLESPACE_NAME
     )
     b
where      a.TABLESPACE_NAME=b.TABLESPACE_NAME
order      by ((a.BYTES-b.BYTES)/a.BYTES) desc;


인덱스의 테이블스페이스를 변경하기위해서 아래와 같은 쿼리를 날렸더니 제목에 쓰인 오류가 났다.

 

SQL> alter index 인덱스명 rebuild tablespace 테이블스페이스명;

 

이유인즉슨 인덱스가 파티셔닝이 되어버려서 한번에 rebuild할 수 없다는 얘기

해당 인덱스가 파티셔닝이 되어있는지는 아래와 같은 명령어로 확인할 수 있다.

 

SQL> select index_name,partitioned from dba_indexes where partitioned='YES' and table_name='테이블명';

 

자 그럼 어떻게 해야할까?? 인덱스가 파티셔닝이 되었다는 얘기는 테이블이 파티셔닝이 되어있다는 얘기같은데....그럼 이제 이 인덱스가 어느 파티션에서 사용이 되는지 확인해서 각 파티션별로 인덱스를 rebuild하면 될것 같은데??

 

우선 이 인덱스가 어느 파티션에서 사용되는지 확인해보자.

SQL> select index_name, partition_name from dba_ind_partitions where index_name='인덱스명';

 

INDEX_NAME                     PARTITION_NAME
------------------------------ ------------------------------
EE                             ARCH_STATE
EE                             CURR_STATE
EE                             PREV_STATE

그리고 각 파티션에 대해서 인덱스를 rebuild하자.

SQL>  alter index ee rebuild partition  ARCH_STATE;
Index altered.

SQL>  alter index ee rebuild partition  CURR_STATE;
Index altered.

SQL>  alter index ee rebuild partition  PREV_STATE;
Index altered.

게임 오바~!! 

alter index ee rebuild partition  ARCH_STATE;


이 에러가 났을 때 뭐 때문에 busy라고 나오는지 궁금하다면??

sysdba권한으로 아래 쿼리를 날려보자. 무슨쿼리 때문에 어느 테이블이 lock이 걸려있는지 확인할 수 있다. 

SELECT O.OBJECT_NAME, S.SID, S.SERIAL#, P.SPID, S.PROGRAM,S.USERNAME,
S.MACHINE,S.PORT , S.LOGON_TIME,SQ.SQL_FULLTEXT 
FROM V$LOCKED_OBJECT L, DBA_OBJECTS O, V$SESSION S, 
V$PROCESS P, V$SQL SQ 
WHERE L.OBJECT_ID = O.OBJECT_ID 
AND L.SESSION_ID = S.SID AND S.PADDR = P.ADDR 
AND S.SQL_ADDRESS = SQ.ADDRESS;


위 쿼리를 실행해서 SID와 SERIAL#을 알아내면 아래와 같은 명령으로 해당 세션을 죽일 수 있다.

ALTER SYSTEM KILL SESSION 'SID, SERIAL#';

이상~!!! 당신에게 노력과 행운의 여신이 함께하여 문제가 해결되기를~~


테이블 스페이스를 튜닝하려고 아래 명령어를 입력하였다.

 

ALTER TABLESPACE 테이블스페이스명 AUTOEXTEND ON NEXT 1024K;

 

그랬더니 아래처럼 오류가 떨어졌다.

 

ORA-32773: operation not supported for smallfile tablespace 테이블스페이스명  

 

구글링해서 얻어온 해결책 하나. 아래 경우를 살펴보기 바란다.

 

 

SQL> alter tablespace users resize 300m;
alter tablespace users resize 300m
*
ERROR at line 1:
ORA-32773: operation not supported for smallfile tablespace USERS

SQL> select file_id, tablespace_name from dba_data_files;

   FILE_ID TABLESPACE_NAME
---------- ------------------------------
         1 SYSTEM
         2 UNDOTBS1
         3 SYSAUX
         4 USERS

SQL> alter database datafile 4 resize 300m;

Database altered.

//--------Description from online documents------------------------------------------------------------------
ORA-32773: operation not supported for smallfile tablespace string
Cause:
An attempt was made to perform. an operation which is supported only for bigfile tablespaces, e.g. resize tablespace.
Action: Use the appropriate clause of the ALTER DATABASE DATAFILE command instead.


SQL> alter tablespace users autoextend off;
alter tablespace users autoextend off
*
ERROR at line 1:
ORA-32773: operation not supported for smallfile tablespace USERS


SQL> alter database datafile 4 autoextend off;

Database altered.

 

출처 : http://blog.itpub.net/9765498/viewspace-259958/ 


ORA-00845: MEMORY_TARGET not supported on this system 오류가 발생했다!!!!!

으악!!!!

 

오라클을 재시작하려고 shutdown시킨 뒤 startup을 실행시켰더니 갑자기 이런 오류가 발생했다.

뭥미?? 처음 본 오류라 당황ㅠㅠ

 

구글링해보니 /dev/shm 마운트한거랑 관련이 있었다. 무슨 이유 때문에 발생했는지는 아직도 확실치 않지만...아래와 같이 해결했다.

 

$ umount -l tmpfs

( tmpfs는 /dev/shm이 마운트된 이름이다. 쉘에서 mount명령어로 확인했을때 제일 좌측에 나오는 이름 )

( -l (소문자L)옵션없이 언마운트시키려고 했을때는 busy라고 계속뜨길래 옵션을 주고 언마운트 시켰다. lazy umount 옵션이다. )

 

$ mount -t tmpfs tmpfs /dev/shm

( 다시 마운트 하기 ) 

 

그리고 sqlplus에 sysdba로 conn해서 

SQL> startup

 

게임오버~!  

 

좀 더 자세한 사항은 이곳을 참조하세요. 

device is busy 문제로 umount가 안된다면 이곳을 참조하세요.