我需要将Oracle数据库的完整模式(仅ddl,无数据)转储到文本文件或一组文本文件中,以便能够使用标准系统地跟踪数据库模式的修订VCS工具,如git。
使用我最喜欢的RDBMS,postgresql,使用pg_dump --schema-only
,这是一项几乎简单易用的任务。
但是,使用Oracle 11g将Oracle DB模式转储到SQL文件已经证明是一项非常困难的任务。我很想知道其他人已经想到的方法。
不幸的是,我无法使用Oracle 10g中引入的data pump export工具,因为这些工具需要DBA级访问权限,而且我无法轻松获得大多数客户的此级别访问权限。 ;数据库。
我已经使用了Oracle的SQL开发人员GUI,而 主要使用"分离文件"设定:
然而,它有几个主要问题:
exp
和imp
Oracle年龄较大的exp
command-line tool 不需要DBA访问权限。它可以导出数据库的完整DDL(具有DBA访问权限),或仅导出单个用户拥有的对象的DDL。
不幸的是,它甚至比SQL开发人员更慢(即使使用a few performance tweaks,同一数据库也需要1小时。
然而,关于exp
最糟糕的事情是它不发出SQL,而是一个专有的二进制格式转储文件(例如expdat.dmp
)。
相应的imp
tool可以"翻译"这些转储文件被严重损坏的SQL,不包含语法正确的语句结束分隔符。
imp show=y
发出的可怕的错误SQL示例;注意部分但不是所有语句末尾的疯狂换行和缺少分号。
Export file created by EXPORT:V11.02.00 via direct path
import done in US7ASCII character set and AL16UTF16 NCHAR character set
import server uses AL32UTF8 character set (possible charset conversion)
. importing FPSADMIN's objects into FPSADMIN
"BEGIN "
"sys.dbms_logrep_imp.instantiate_schema(schema_name=>SYS_CONTEXT('USERENV','"
"CURRENT_SCHEMA'), export_db_name=>'*******', inst_scn=>'371301226');"
"COMMIT; END;"
"CREATE TYPE "CLOBSTRINGAGGTYPE" TIMESTAMP '2015-06-01:13:37:41' OID '367CDD"
"7E59D14CF496B27D1B19ABF051' "
"AS OBJECT"
"("
" theString CLOB,"
" STATIC FUNCTION"
" ODCIAggregateInitialize(sctx IN OUT CLOBSTRINGAGGTYPE )"
" RETURN NUMBER,"
" MEMBER FUNCTION"
" ODCIAggregateIterate(self IN OUT CLOBSTRINGAGGTYPE, VALUE IN VARC"
"HAR2 )"
" RETURN NUMBER,"
" MEMBER FUNCTION"
" ODCIAggregateTerminate(self IN CLOBSTRINGAGGTYPE, returnValue OUT"
" CLOB, flags IN NUMBER)"
" RETURN NUMBER,"
" MEMBER FUNCTION"
" ODCIAggregateMerge(self IN OUT CLOBSTRINGAGGTYPE, ctx2 IN CLOBSTR"
"INGAGGTYPE)"
" RETURN NUMBER"
");"
"GRANT EXECUTE ON "CLOBSTRINGAGGTYPE" TO PUBLIC"
"GRANT DEBUG ON "CLOBSTRINGAGGTYPE" TO PUBLIC"
"CREATE OR REPLACE TYPE BODY CLOBSTRINGAGGTYPE"
我编写了一个Python脚本来解析imp show=y
的输出,但它无法可靠地解析输出,因为它不了解完整的Oracle SQL语法。
Oracle有一个dbms_metadata
包,它支持内省数据库内容。
编写一个SQL语句相对容易,该语句将检索某些但不是所有数据库对象的DDL。例如,以下语句将检索CREATE TABLE
语句,但不会在这些表上检索相应的权限GRANT
。
select sub.*, dbms_metadata.get_ddl(sub.object_type, sub.object_name, sub.owner) sql
from (
select
created,
owner,
object_name,
decode(object_type,
'PACKAGE', 'PACKAGE_SPEC',
'PACKAGE BODY', 'PACKAGE_BODY',
'TYPE BODY', 'TYPE_BODY',
object_type
) object_type
from all_objects
where owner = :un
--These objects are included with other object types.
and object_type not in ('INDEX PARTITION','LOB','LOB PARTITION','TABLE PARTITION','DATABASE LINK')
--Ignore system-generated types that support collection processing.
and not (object_type like 'TYPE' and object_name like 'SYS_PLSQL_%')
) sub
尝试快速获取整套物体会导致一个非常复杂的兔子洞。 (有关更多详细信息,请参阅"Reverse engineering object DDL and finding object dependencies"。)
有什么建议吗?我完全失去了一个理智和可维护的方式来执行这个看似不可或缺的数据库编程任务。
答案 0 :(得分:1)
结合DBMS_DATAPUMP,Oracle Copy (OCP)和一个简单的shell脚本来创建一键式解决方案。
要导出的示例架构
--Create test user.
drop user test_user cascade;
create user test_user identified by test_user;
create table test_user.table1(a number);
create view test_user.view1 as select 1 a from dual;
create or replace procedure test_user.procedure1 is begin null; end;
/
创建目录和程序
以SYS作为步骤运行它们。定义者权限过程以SYS身份运行。这样就不需要为任何用户授予任何角色或特权。
--Create directory that will contain SQL file.
create directory ddl_directory as 'C:\temp';
grant read on directory ddl_directory to jheller;
--Create procedure that can only export one hard-coded schema.
--This is based on René Nyffenegger's solution here:
--dba.stackexchange.com/questions/91149/how-to-generate-an-sql-file-with-dbms-datapump
create or replace procedure sys.generate_ddl authid definer is
procedure create_export_file is
datapump_job number;
job_state varchar2(20);
begin
datapump_job := dbms_datapump.open(
operation => 'EXPORT',
job_mode => 'SCHEMA',
remote_link => null,
job_name => 'Export dump file',
version => 'LATEST');
dbms_output.put_line('datapump_job: ' || datapump_job);
dbms_datapump.add_file(
handle => datapump_job,
filename => 'export.dmp',
directory => 'DDL_DIRECTORY',
filetype => dbms_datapump.ku$_file_type_dump_file);
dbms_datapump.metadata_filter(
handle => datapump_job,
name => 'SCHEMA_LIST',
value => '''TEST_USER''');
dbms_datapump.start_job(
handle => datapump_job,
skip_current => 0,
abort_step => 0);
dbms_datapump.wait_for_job(datapump_job, job_state);
dbms_output.put_line('Job state: ' || job_state);
dbms_datapump.detach(datapump_job);
end create_export_file;
procedure create_sql_file is
datapump_job number;
job_state varchar2(20);
begin
datapump_job := dbms_datapump.open(
operation => 'SQL_FILE',
job_mode => 'SCHEMA',
remote_link => null,
job_name => 'Export SQL file',
version => 'LATEST');
dbms_output.put_line('datapump_job: ' || datapump_job);
dbms_datapump.add_file(
handle => datapump_job,
filename => 'export.dmp',
directory => 'DDL_DIRECTORY',
filetype => dbms_datapump.ku$_file_type_dump_file);
dbms_datapump.add_file(
handle => datapump_job,
filename => 'schema.sql',
directory => 'DDL_DIRECTORY',
filetype => dbms_datapump.ku$_file_type_sql_file);
dbms_datapump.start_job(
handle => datapump_job,
skip_current => 0,
abort_step => 0);
dbms_datapump.wait_for_job(datapump_job, job_state);
dbms_output.put_line('Job state: ' || job_state);
dbms_datapump.detach(datapump_job);
end create_sql_file;
begin
create_export_file;
create_sql_file;
end;
/
--Grant to users.
grant execute on generate_ddl to jheller;
在客户端设置OCP
如this answer中所述,可以使用OCP轻松地将Oracle目录中的文件传输到客户端PC。设置 有点棘手 - 下载程序的精确版本和即时客户端并将它们解压缩到同一目录中。我想我也遇到了一些问题 一个VC ++可再发行组件或第一次的东西。
要运行的命令
现在简单的部分 - 创建和移动文件只需两个简单的步骤:
execute sys.generate_ddl;
C:\Users\jonearles\Downloads\ocp-0.1-win32>ocp jheller/jheller@orcl12 DDL_DIRECTORY:schema.sql schema.sql
示例输出
这个脚本包含很多奇怪的东西。一些没有人会理解的奇怪的额外命令,以及一些没有人会理解的奇怪选项。这可能是这个看似显而易见的特征如此困难的原因之一 - 由于成千上万的奇怪特征,输出既不可理解也不完全明确。
CREATE TABLE "TEST_USER"."TABLE1"
( "A" NUMBER
) SEGMENT CREATION DEFERRED
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
TABLESPACE "USERS" ;
...
-- new object type path: SCHEMA_EXPORT/PROCEDURE/PROCEDURE
-- CONNECT TEST_USER
CREATE EDITIONABLE procedure procedure1 is begin null; end;
/
...
-- new object type path: SCHEMA_EXPORT/VIEW/VIEW
CREATE FORCE EDITIONABLE VIEW "TEST_USER"."VIEW1" ("A") AS
select 1 a from dual
;