본문 바로가기
[DATABASE] ORACLE/Data Migration

[Oracle] Data Unload to text file (sam file)

by 기미차니 2022. 9. 8.
반응형

Oracle Table을 SAM file 로 Unload 시 참고. 

spool 등 다양한 방법이 있으나, 대상 테이블이 많고 format 지정 등이 필요한 경우 유용 할거 같음

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
CREATE OR REPLACE DIRECTORY TEMP_DIR AS 'C:\TEMP';
 
CREATE OR REPLACE PROCEDURE ORACLE_TO_SAMFILE
IS
BEGIN
    FOR TABLES IN (
        SELECT OWNER||'_'||TABLE_NAME||'.txt'                   AS FILE_NAME
             , 'SELECT * FROM "'||OWNER||'"."'||TABLE_NAME||'"' AS V_SQL
          FROM ALL_TABLES
         WHERE OWNER = 'ORAKIM'
           AND TABLE_NAME LIKE 'T%'
         ORDER BY 1
    ) 
    LOOP
        DATA_DUMP   
        (
            QUERY_IN        => TABLES.V_SQL,
            FILE_IN         => TABLES.FILE_NAME,
            DIRECTORY_IN    => 'TEMP_DIR',
            DELIMITER_IN    => '|',
            HEADER_ROW_IN   => FALSE
        );
    END LOOP;
END;
/
 
-- 전체 테이블 수행
EXEC ORACLE_TO_SAMFILE;
 
-- 단일 테이블 수행
EXEC DATA_DUMP ( QUERY_IN       => 'SELECT * FROM T10',
                 FILE_IN        => 'ORAKIM_T10.txt',
                 DIRECTORY_IN   => 'TEMP_DIR',
                 DELIMITER_IN   => '|',
                 HEADER_ROW_IN  => FALSE
                 );
cs

 

DATA_DUMP 프로시저 참조 URL : https://github.com/oracle-developer/utilities/blob/master/data_dump.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
CREATE OR REPLACE PROCEDURE DATA_DUMP 
(
    query_in        IN VARCHAR2,
    file_in         IN VARCHAR2,
    directory_in    IN VARCHAR2,
    nls_date_fmt_in IN VARCHAR2 DEFAULT 'YYYY-MM-DD HH24:MI:SS',
    write_action_in IN VARCHAR2 DEFAULT 'W',
    array_size_in   IN PLS_INTEGER DEFAULT 1000,
    delimiter_in    IN VARCHAR2 DEFAULT NULL,
    dump_code_in    IN BOOLEAN DEFAULT FALSE,
    header_row_in   IN BOOLEAN DEFAULT FALSE
 
AUTHID CURRENT_USER IS
 
   v_fh             UTL_FILE.FILE_TYPE;
   v_ch             BINARY_INTEGER      := DBMS_SQL.OPEN_CURSOR;
   v_sql            VARCHAR2(32767)     := query_in;
   v_dir            VARCHAR2(512)       := upper(directory_in);
   v_outfile        VARCHAR2(128)       := file_in;
   v_sqlfile        VARCHAR2(128)       := file_in||'.sql';
   v_arr_size       PLS_INTEGER         := array_size_in;
   v_col_cnt        PLS_INTEGER         := 0;
   v_delimiter      VARCHAR2(1)         := NULL;
   v_write_action   VARCHAR2(1)         := write_action_in;
   v_nls_date_fmt   VARCHAR2(30)        := nls_date_fmt_in;
   v_dummy          NUMBER;
   v_type           VARCHAR2(8);
   v_header_row     VARCHAR2(32767);
   t_describe       DBMS_SQL.DESC_TAB;
   t_plsql          DBMS_SQL.VARCHAR2A;
 
   /* Procedure to output code for debug and assign plsql variable... */
   PROCEDURE put (string_in IN VARCHAR2IS
   BEGIN
      IF dump_code_in THEN
         UTL_FILE.PUT_LINE(v_fh,string_in);
      END IF;
      t_plsql(t_plsql.COUNT + 1) := string_in;
   END put;
 
BEGIN
 
   /* Open the file that the dynamic PL/SQL will be written to for debug... */
   IF dump_code_in THEN
      v_fh := UTL_FILE.FOPEN(v_dir, v_sqlfile, 'W'32767);
   END IF;
 
   /* Parse the query that will be used to fetch all the data to be written out... */
   DBMS_SQL.PARSE(v_ch, v_sql, DBMS_SQL.NATIVE);
 
   /* Now describe the dynamic SQL to analyze the number of columns in the query... */
   DBMS_SQL.DESCRIBE_COLUMNS(v_ch, v_col_cnt, t_describe);
 
   /* Now begin the dynamic PL/SQL... */
   put('DECLARE');
   put('   v_fh     UTL_FILE.FILE_TYPE;');
   put('   v_eol    VARCHAR2(2);');
   put('   CURSOR cur_sql IS');
   put('      '||v_sql||';');
 
   /* Now loop through the describe table to declare arrays in the dynamic PL/SQL... */
   FOR i IN t_describe.FIRST .. t_describe.LAST LOOP
      IF t_describe(i).col_type = 2 THEN
         v_type := 'NUMBER';
      ELSIF t_describe(i).col_type = 12 THEN
         v_type := 'DATE';
      ELSIF t_describe(i).col_type = 113 THEN
         v_type := 'BLOB';
      ELSE
         v_type := 'VARCHAR2';
      END IF;
      put('   "'||t_describe(i).col_name||'" DBMS_SQL.'||v_type||'_TABLE;');
   END LOOP;
 
   /* Enclose and escape rules based on CSV RFP: https://tools.ietf.org/html/rfc4180 */
   /* 2.6: "Fields containing line breaks (CRLF), double quotes, and commas should be enclosed in double-quotes." */
   /* 2.7: "If double-quotes are used to enclose fields, then a double-quote appearing inside a field must be escaped by preceding it with another double quote." */
   put(q'[                                                                                                                                                ]');
   put(q'[   FUNCTION enclose_and_escape(p_value VARCHAR2) RETURN VARCHAR2 IS                                                                             ]');
   put(q'[      v_value VARCHAR2(32767);                                                                                                                  ]');
   put(q'[   BEGIN                                                                                                                                        ]');
   put(q'[      v_value := REPLACE(p_value, '"', '""');                                                                                                   ]');
   put(q'[      IF v_value LIKE '%'||CHR(10)||'%' OR v_value LIKE '%'||CHR(13)||'%' OR v_value LIKE '%"%' OR v_value LIKE '%]'||delimiter_in||q'[%' THEN  ]');
   put(q'[         v_value := '"' || v_value || '"';                                                                                                      ]');
   put(q'[      END IF;                                                                                                                                   ]');
   put(q'[      RETURN v_value;                                                                                                                           ]');
   put(q'[   END enclose_and_escape;                                                                                                                      ]');
   put(q'[                                                                                                                                                ]');
 
   /* Set the date format to preserve time in the output, open the out file... */
   put('BEGIN');
   put('   EXECUTE IMMEDIATE ''ALTER SESSION SET NLS_DATE_FORMAT = '''''||v_nls_date_fmt||''''''';');
   put('   v_eol := CASE');
   put('               WHEN DBMS_UTILITY.PORT_STRING LIKE ''IBMPC%''');
   put('               THEN CHR(13)||CHR(10)');
   put('               ELSE CHR(10)');
   put('            END;');
   put('   v_fh := UTL_FILE.FOPEN('''||v_dir||''','''||v_outfile||''','''||v_write_action||''', 32767);');
   put('   OPEN cur_sql;');
 
   /* Create and print a header row... */
   IF header_row_in THEN
      v_header_row := 'enclose_and_escape('''||t_describe(1).col_name||''')';
      FOR i IN t_describe.FIRST + 1 .. t_describe.LAST LOOP
         v_header_row := v_header_row||'||'''||delimiter_in||'''||enclose_and_escape('''||t_describe(i).col_name||''')';
      END LOOP;
 
      put('   --Print header.');
      put('   UTL_FILE.PUT(v_fh, '||v_header_row||');');
      put('   UTL_FILE.NEW_LINE(v_fh);');
   END IF;
 
   /* Start to collect... */
   put('   LOOP');
   put('      FETCH cur_sql');
 
   IF t_describe.COUNT > 1 THEN
 
      put('      BULK COLLECT INTO "'||t_describe(t_describe.FIRST).col_name||'",');
 
      /* Add all other arrays into the fetch list except the last... */
      FOR i IN t_describe.FIRST + 1 .. t_describe.LAST - 1 LOOP
         put('                        "'||t_describe(i).col_name||'",');
      END LOOP;
 
      /* Add in the last array and limit... */
      put('                        "'||t_describe(t_describe.LAST).col_name||'" LIMIT '||v_arr_size||';');
 
   ELSE
      /* Just output the one collection and LIMIT... */
      put('      BULK COLLECT INTO "'||t_describe(t_describe.FIRST).col_name||'" LIMIT '||v_arr_size||';');
 
   END IF;
 
   /* Now add syntax to loop though the fetched array and write out the values to file... */
   put('      IF "'||t_describe(t_describe.FIRST).col_name||'".COUNT > 0 THEN');
   put('         FOR i IN "'||t_describe(t_describe.FIRST).col_name||'".FIRST .. "'||
                                  t_describe(t_describe.FIRST).col_name||'".LAST LOOP');
 
   /* Write enclosed and escaped values... */
   FOR i IN t_describe.FIRST .. t_describe.LAST LOOP
      /* Some data types are not currently supported... */
      IF t_describe(i).col_type = 113 THEN
         put('            UTL_FILE.PUT(v_fh,'''||v_delimiter||''' ||enclose_and_escape(''WARNING - BLOB type is not supported''));');
      ELSE
         put('            UTL_FILE.PUT(v_fh,'''||v_delimiter||''' ||enclose_and_escape("'||t_describe(i).col_name||'"(i)));');
      END IF;
      v_delimiter := NVL(delimiter_in,',');
   END LOOP;
 
   /* Add a new line marker into the file and move on to next record... */
   put('            UTL_FILE.NEW_LINE(v_fh);');
   put('         END LOOP;');
 
   /* Complete the IF statement... */
   put('      END IF;');
 
   /* Add in an EXIT condition and complete the loop syntax... */
   put('      EXIT WHEN cur_sql%NOTFOUND;');
   put('   END LOOP;');
   put('   CLOSE cur_sql;');
   put('   UTL_FILE.FCLOSE(v_fh);');
 
   /* Add in some exception handling... */
   put('EXCEPTION');
   put('   WHEN UTL_FILE.INVALID_PATH THEN');
   put('      DBMS_OUTPUT.PUT_LINE(''Error - invalid path.'');');
   put('      RAISE;');
   put('   WHEN UTL_FILE.INVALID_MODE THEN');
   put('      DBMS_OUTPUT.PUT_LINE(''Error - invalid mode.'');');
   put('      RAISE;');
   put('   WHEN UTL_FILE.INVALID_OPERATION THEN');
   put('      DBMS_OUTPUT.PUT_LINE(''Error - invalid operation.'');');
   put('      RAISE;');
   put('   WHEN UTL_FILE.INVALID_FILEHANDLE THEN');
   put('      DBMS_OUTPUT.PUT_LINE(''Error - invalid filehandle.'');');
   put('      RAISE;');
   put('   WHEN UTL_FILE.WRITE_ERROR THEN');
   put('      DBMS_OUTPUT.PUT_LINE(''Error - write error.'');');
   put('      RAISE;');
   put('   WHEN UTL_FILE.READ_ERROR THEN');
   put('      DBMS_OUTPUT.PUT_LINE(''Error - read error.'');');
   put('      RAISE;');
   put('   WHEN UTL_FILE.INTERNAL_ERROR THEN');
   put('      DBMS_OUTPUT.PUT_LINE(''Error - internal error.'');');
   put('      RAISE;');
   put('END;');
 
   /* Now close the cursor and sql file... */
   DBMS_SQL.CLOSE_CURSOR(v_ch);
   IF dump_code_in THEN
      UTL_FILE.FCLOSE(v_fh);
   END IF;
 
   /*
    * Execute the t_plsql collection to dump the data. Use DBMS_SQL as we have a collection
    * of syntax...
   */
   v_ch := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(v_ch, t_plsql, t_plsql.FIRST, t_plsql.LASTTRUE, DBMS_SQL.NATIVE);
   v_dummy := DBMS_SQL.EXECUTE(v_ch);
   DBMS_SQL.CLOSE_CURSOR(v_ch);
 
EXCEPTION
   WHEN UTL_FILE.INVALID_PATH THEN
      DBMS_OUTPUT.PUT_LINE('Error - invalid path.');
      RAISE;
   WHEN UTL_FILE.INVALID_MODE THEN
      DBMS_OUTPUT.PUT_LINE('Error - invalid mode.');
      RAISE;
   WHEN UTL_FILE.INVALID_OPERATION THEN
      DBMS_OUTPUT.PUT_LINE('Error - invalid operation.');
      RAISE;
   WHEN UTL_FILE.INVALID_FILEHANDLE THEN
      DBMS_OUTPUT.PUT_LINE('Error - invalid filehandle.');
      RAISE;
   WHEN UTL_FILE.WRITE_ERROR THEN
      DBMS_OUTPUT.PUT_LINE('Error - write error.');
      RAISE;
   WHEN UTL_FILE.READ_ERROR THEN
      DBMS_OUTPUT.PUT_LINE('Error - read error.');
      RAISE;
   WHEN UTL_FILE.INTERNAL_ERROR THEN
      DBMS_OUTPUT.PUT_LINE('Error - internal error.');
      RAISE;
END;
/
cs

 

반응형

댓글