一、建同构(dm-dm)
同构数据迁移指的是在相同类型的数据库之间进行数据迁移,此处特指达梦数据库之间的迁移。这种迁移相对简单,因为数据库的语法、数据类型等差异较小。
DM数据库的模式、表、视图、序列、索引迁移到主流大型数据库Oracle;
◆ DM数据库之间模式、表、序列、视图、存储过程/函数、包、类、同义词、触发器、对象权限的迁移;
◆ DM数据库模式、表、序列、视图、存储过程/函数、包、类、同义词、触发器、对象权限迁移到SQL脚本文件;
IP:10.127.17.107-10.127.17.108
1.1、DM数据库的模式、表
在进行数据迁移之前,需要先在源数据库中创建测试数据,以便后续迁移操作的验证。
1.1.1 创建测试数据
创建一个dmtest用户,创建一个test1表里面包含100条数据、模式以及表
-- 创建用户 dmtest,密码设为 Dameng123(按需修改)
CREATE USER dmtest IDENTIFIED BY "Dameng123";
-- 授予基础权限(建表、插入数据等)
GRANT RESOURCE, PUBLIC TO dmtest;
2. 创建表并插入 100 条测试数据
-- 切换到 dmtest 模式(用户登录后默认进入同名模式)
SET SCHEMA dmtest;
-- 创建测试表
CREATE TABLE test1 (
id INT PRIMARY KEY,
name VARCHAR(50),
create_time DATE DEFAULT SYSDATE
);
-- 使用循环插入数据(推荐)
BEGIN
FOR i IN 1..100 LOOP
INSERT INTO dmtest.test1(id, name)
VALUES (i, 'User_' || i);
END LOOP;
COMMIT;
END;
-- 查看表数据量
SELECT COUNT(*) FROM dmtest.test1; -- 应返回 100
-- 查询前 10 条数据
SELECT * FROM dmtest.test1 WHERE ROWNUM <= 10;
-- 查看表数据量
SELECT COUNT(*) FROM dmtest.test1; -- 应返回 100
-- 查询前 10 条数据
SELECT * FROM dmtest.test1 WHERE ROWNUM <= 10;
迁移评估
迁移评估是数据迁移过程中的重要环节,它可以帮助我们了解源数据库和目标数据库的兼容性,以及迁移过程中可能出现的问题。
在达梦数据库迁移工具中,进行迁移评估的步骤如下:
打开迁移工具,选择 “数据源” 为达梦数据库,输入源数据库信息,包括主机名(10.127.17.107)、端口(5236)、用户名(sysdba)、口令等。
点击 “评估” 按钮,工具将对源数据库的对象进行评估,包括模式、表、序列等。评估结果会显示对象的总数、完全兼容数、转换后兼容数、不兼容数等信息。
从评估结果来看,本次测试数据的迁移评估任务总数为 85,已完成 85,出错 0,所有对象的兼容率为 98.8%,表和模式的兼容率均为 100%,这表明源数据库对象与目标数据库具有较好的兼容性,适合进行迁移。
数据迁移
在完成迁移评估并确认无误后,即可进行数据迁移操作。
配置目标数据库信息:在迁移工具中,指定目标数据库为 10.127.17.108,端口为 5238,其他信息根据实际情况填写。
选择迁移对象:在迁移工具中,勾选需要迁移的模式(DMTEST)和表(TEST1)等对象。
目标端:108机器
迁移完成后,工具会显示迁移任务的执行情况。本次迁移任务总数为 6,已完成 6,出错 0,耗时 206 毫秒。其中,创建模式耗时 23 毫秒,创建表耗时 9 毫秒,迁移数据耗时 93 毫秒,添加主键耗时 12 毫秒。这表明数据迁移操作顺利完成。
本例构建了一套 Oracle 19C的单机示例库,并介绍利用 DTS 工具从 Oracle 19c 移植 Etest 用户下的所有对象到 DM8 数据库的详细步骤,
oracle源端信息
调研项 | 调研结果 |
---|---|
数据库后台操作系统 | Red Hat Linux |
数据库架构 | 单机 |
数据库版本 | Oracle 19C |
待迁移数据库名 | ORCLPDB |
待迁移的模式名 | Etest |
IP/端口信息 | 10.127.17.101/1521 |
用户名/密码 | xxxxx |
字符集编码 | ZHS16GBK |
需要移植的对象 | 表(数据量)、物化视图、触发器、存储过程、函数 |
-- 表数据量统计示例
SELECT table_name, num_rows FROM all_tables WHERE owner='ETEST';
-- 对象数量统计示例
SELECT object_type, COUNT(*)
FROM all_objects
WHERE owner='ETEST'
GROUP BY object_type;
10.127.17.108-10.127.17.101
关键配置说明
字符集处理
由于数据库字符集为ZHS16GBK,中文数据可直接存储(如示例中的’测试数据’)存储配置使用默认表空间USERS块大小8192字节(DB_BLOCK_SIZE)日期字段默认使用SYSDATE。
oracle数据库的模式、表、视图、序列、索引迁移到主流大型数据库Oracle;
◆oracle数据库之间模式、表、序列、视图、存储过程/函数、包、类、同义词、触发器、对象权限的迁移;
◆ oracle数据库模式、表、序列、视图、存储过程/函数、包、类、同义词、触发器、对象权限迁移到SQL脚本文件;
2.21迁移对象统计
部分调研项查询方法如下:
--迁移对象统计
-- 对象统计查询
SELECT
a.username "用户",
(SELECT COUNT(1) FROM dba_tables b WHERE b.owner = a.username) "表数量",
(SELECT COUNT(1) FROM DBA_INDEXES i
WHERE (UNIQUENESS = 'UNIQUE' AND OWNER = a.username)
OR (INDEX_NAME NOT LIKE 'SYS\_%' ESCAPE '\' AND OWNER = a.username)) "索引数量",
(SELECT COUNT(DISTINCT c.table_name)
FROM dba_tab_partitions c
WHERE c.table_owner = a.username) "分区表数量",
(SELECT COUNT(1)
FROM dba_tab_cols d
WHERE d.OWNER = a.username
AND d.DATA_TYPE LIKE '%LOB%') "包含LOB表数量",
(SELECT SUM(e.bytes)/1024/1024/1024
FROM dba_extents e
WHERE EXISTS (
SELECT 1
FROM dba_lobs f
WHERE f.owner = a.username
AND f.segment_name = e.segment_name
)) "LOB占用空间(GB)",
(SELECT COUNT(1) FROM dba_views g WHERE g.OWNER = a.username) "视图数量",
(SELECT COUNT(1) FROM dba_triggers h WHERE h.owner = a.username) "触发器数量",
(SELECT COUNT(DISTINCT i.name)
FROM DBA_SOURCE i
WHERE i.OWNER = a.username
AND i.TYPE = 'FUNCTION') "函数数量",
(SELECT COUNT(1)
FROM DBA_SEQUENCES j
WHERE j.sequence_owner = a.username) "序列数量",
(SELECT COUNT(1) FROM dba_synonyms WHERE owner = a.username) "同义词数量",
(SELECT COUNT(1) FROM DBA_MVIEWS k WHERE k.owner = a.username) "物化视图数量",
(SELECT COUNT(DISTINCT l.name)
FROM DBA_SOURCE l
WHERE l.OWNER = a.username
AND l.TYPE = 'PROCEDURE') "存储过程数量",
(SELECT COUNT(1) FROM DBA_DB_LINKS m WHERE m.owner = a.username) "DBLINK数量",
(SELECT MAX(n.DATA_LENGTH)
FROM dba_tab_cols n
WHERE n.OWNER = a.username) "最大单字段宽度",
(SELECT SUM(o.DATA_LENGTH)
FROM dba_tab_cols o
WHERE o.OWNER = a.username
AND o.DATA_TYPE NOT LIKE '%LOB%') "非LOB字段总宽度"
FROM dba_users a
WHERE username = 'ETEST';
-- 数据量统计
SELECT
segment_type "段类型",
SUM(BYTES)/1024/1024/1024 "大小(GB)",
COUNT(*) "数量"
FROM DBA_SEGMENTS
WHERE owner = 'ETEST' -- 替换为实际用户名
GROUP BY segment_type
ORDER BY 2 DESC;
统计结果:
创建一个Etest用户,创建一个test2表里面包含200条数据、模式以及表
-- 创建用户 Etest ,密码设为123456(按需修改)
-- 确保在PDB容器中
ALTER SESSION SET CONTAINER = ORCLPDB;
-- 创建用户并授权
CREATE USER Etest IDENTIFIED BY 123456
DEFAULT TABLESPACE USERS
QUEtestA UNLIMITED ON USERS;
GRANT CONNECT, RESOURCE TO Etest;
GRANT CREATE TABLE, UNLIMITED TABLESPACE TO Etest;
-- 切换到Etest用户
CONNECT Etest/123456@//fyszcdb22:1521/orclpdb
-- 创建学生课程表
CREATE TABLE test2 (
id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
full_name VARCHAR2(50) NEtest NULL,
major VARCHAR2(50) NEtest NULL,
course VARCHAR2(50) NEtest NULL,
description VARCHAR2(400) NEtest NULL
);
-- 插入200条学生数据
DECLARE
TYPE name_array IS VARRAY(20) OF VARCHAR2(10);
TYPE major_array IS VARRAY(10) OF VARCHAR2(50);
TYPE course_array IS VARRAY(10) OF VARCHAR2(50);
surnames name_array := name_array('李', '孙', '王', '梦', '于', '张', '刘', '陈', '杨', '赵', '黄', '周', '吴', '徐', '马');
given_names name_array := name_array('明', '华', '强', '伟', '芳', '婷', '亮', '丽', '静', '杰', '敏', '浩', '娜', '勇', '艳', '超', '鑫', '磊', '洋', '慧');
majors major_array := major_array(
'软件工程', '人工智能', '计算机科学与技术', '园林设计',
'数据科学', '网络安全', '物联网工程', '数字媒体技术',
'电子信息工程', '自动化'
);
courses course_array := course_array(
'计算机组成原理', '数据结构', '算法分析', '数据库系统',
'计算机网络', '操作系统', '机器学习', '深度学习',
'英语高级阅读', '专业英语写作'
);
full_name VARCHAR2(50);
major_val VARCHAR2(50);
course_val VARCHAR2(50);
description VARCHAR2(400);
BEGIN
FOR i IN 1..200 LOOP
-- 生成随机姓名 (姓氏+名字)
full_name := surnames(TRUNC(DBMS_RANDOM.VALUE(1, surnames.COUNT+1))) ||
given_names(TRUNC(DBMS_RANDOM.VALUE(1, given_names.COUNT+1))) ||
given_names(TRUNC(DBMS_RANDOM.VALUE(1, given_names.COUNT+1)));
-- 随机选择专业和课程
major_val := majors(TRUNC(DBMS_RANDOM.VALUE(1, majors.COUNT+1)));
course_val := courses(TRUNC(DBMS_RANDOM.VALUE(1, courses.COUNT+1)));
-- 生成个性化描述
description :=
'我是' || full_name || ',' || major_val || '专业学生。' ||
'我正在学习《' || course_val || '》课程。我的兴趣包括' ||
CASE TRUNC(DBMS_RANDOM.VALUE(1,6))
WHEN 1 THEN '编程和算法设计'
WHEN 2 THEN '人工智能应用开发'
WHEN 3 THEN '网络系统构建'
WHEN 4 THEN '数据分析和可视化'
ELSE '软件工程实践'
END || '。未来我希望从事' ||
CASE TRUNC(DBMS_RANDOM.VALUE(1,6))
WHEN 1 THEN '软件开发工程师'
WHEN 2 THEN '数据科学家'
WHEN 3 THEN '系统架构师'
WHEN 4 THEN 'AI研究员'
ELSE '技术顾问'
END || '工作。我的学号是' || TO_CHAR(20250000 + i) || '。';
-- 插入数据
INSERT INTO test2 (full_name, major, course, description)
VALUES (full_name, major_val, course_val, description);
END LOOP;
COMMIT;
END;
/
-- 检查表结构
DESC test2;
-- 查询数据量
SELECT COUNT(*) FROM test2;
-- 查看前10条数据
SELECT * FROM test2 WHERE ROWNUM <= 10;
-- 插入200条学生数据
DECLARE
TYPE name_array IS VARRAY(20) OF VARCHAR2(10);
TYPE major_array IS VARRAY(10) OF VARCHAR2(50);
TYPE course_array IS VARRAY(10) OF VARCHAR2(50);
surnames name_array := name_array('李', '孙', '王', '梦', '于', '张', '刘', '陈', '杨', '赵', '黄', '周', '吴', '徐', '马');
given_names name_array := name_array('明', '华', '强', '伟', '芳', '婷', '亮', '丽', '静', '杰', '敏', '浩', '娜', '勇', '艳', '超', '鑫', '磊', '洋', '慧');
majors major_array := major_array(
'软件工程', '人工智能', '计算机科学与技术', '园林设计',
'数据科学', '网络安全', '物联网工程', '数字媒体技术',
'电子信息工程', '自动化'
);
courses course_array := course_array(
'计算机组成原理', '数据结构', '算法分析', '数据库系统',
'计算机网络', '操作系统', '机器学习', '深度学习',
'英语高级阅读', '专业英语写作'
);
full_name VARCHAR2(50);
major_val VARCHAR2(50);
course_val VARCHAR2(50);
description VARCHAR2(400);
BEGIN
FOR i IN 1..200 LOOP
-- 生成随机姓名 (姓氏+名字)
full_name := surnames(TRUNC(DBMS_RANDOM.VALUE(1, surnames.COUNT+1))) ||
given_names(TRUNC(DBMS_RANDOM.VALUE(1, given_names.COUNT+1))) ||
given_names(TRUNC(DBMS_RANDOM.VALUE(1, given_names.COUNT+1)));
-- 随机选择专业和课程
major_val := majors(TRUNC(DBMS_RANDOM.VALUE(1, majors.COUNT+1)));
course_val := courses(TRUNC(DBMS_RANDOM.VALUE(1, courses.COUNT+1)));
-- 生成个性化描述
description :=
'我是' || full_name || ',' || major_val || '专业学生。' ||
'我正在学习《' || course_val || '》课程。我的兴趣包括' ||
CASE TRUNC(DBMS_RANDOM.VALUE(1,6))
WHEN 1 THEN '编程和算法设计'
WHEN 2 THEN '人工智能应用开发'
WHEN 3 THEN '网络系统构建'
WHEN 4 THEN '数据分析和可视化'
ELSE '软件工程实践'
END || '。未来我希望从事' ||
CASE TRUNC(DBMS_RANDOM.VALUE(1,6))
WHEN 1 THEN '软件开发工程师'
WHEN 2 THEN '数据科学家'
WHEN 3 THEN '系统架构师'
WHEN 4 THEN 'AI研究员'
ELSE '技术顾问'
END || '工作。我的学号是' || TO_CHAR(20250000 + i) || '。';
-- 插入数据
INSERT INTO test2 (full_name, major, course, description)
VALUES (full_name, major_val, course_val, description);
END LOOP;
COMMIT;
END;
/
-- 检查表结构
DESC test2;
-- 查询数据量
SELECT COUNT(*) FROM test2;
-- 查看前10条数据
SELECT * FROM test2 WHERE ROWNUM <= 10;
2.4迁移评估
从这张图里能看到在 ETEST 模式下,存在一个名为 TEST2 的表和一个名为 ISEQ$$_73047 的序列 。
根据评估得到
从 Oracle 移植到 DM,需额外注意以下参数。
目的端达梦的表空间需要根据源端数据量情况,规划表空间数据文件个数以及单个数据文件上限,单个数据文件不宜过大,可以创建多个数据文件。
2.6创建迁移用户和表空间
从 Oracle 移植到 DM,要先创建好待使用的用户和这个用户的表空间,不要把数据迁移到系统默认的管理员 SYSDBA 用户下和 MAIN 表空间下。
Oracle 的体系架构是单库多实例的模式,DM 数据库也是单库多实例的架构,从 Oracle 迁移到达梦的时候就需要针对 Oracle 中的库在达梦里面创建一个用户和表空间来对应。例如 Oracle 中有一个库Etest,需要移植库中的Etest模式下的数据对象,达梦里面先创建一个表空间 dbtest ,然后创建一个用户 etest,指定默认表空间为 dbtest。示例如下:
1)创建 dbtest 表空间存储 Oracle 中ORCLPDB库迁移过来的数据。
create tablespace "dbtest" datafile '/opt/dmdbms/data/DAMENG/DBTEST.DBF' size 2048;
--创建表空间dbtest,数据文件为DBTEST.DBF,打开数据库文件自动扩展。
2)创建 testc用户并授予权限,使用 dbtest 表空间。
-- 创建用户ETEST并指定表空间
CREATE USER "etest" IDENTIFIED BY "Dameng123"
DEFAULT TABLESPACE "dbtest"
default index tablespace "dbtest";
-- 授予用户etest常规权限
GRANT "PUBLIC", "RESOURCE", "SOI", "SVI", "VTI" TO "etest";
迁移 Oracle 库的数据的时候,用 Etest用户连接;用 testc用户连接达梦,在迁移过程中指定迁移到的用户模式,这样就把ORCLPDB库的Etest模式下的数据迁移到了达梦 testc用户模式下。
在做ORCLPDB迁移的时候要先分析本次迁移需要从源库中移植库中的那个模式或者那几个模式下的数据,然后为每一个模式,分别在达梦中创建独立的表空间和用户,达梦同时生成相应的模式,大多数情况下,并不是所有的数据都需要迁移,所以在迁移准备阶段,一定要明确迁移那些模式下的哪些数据。
本次采用的是DM 数据迁移工具 V8,Linux 环境下进入 tool 目录中执行./dts 即可运行 DM DTS 工具,
(1)打开 DMDTS 迁移工具
创建建立好的用户密码登录
待迁移具体对象勾选完毕后可以通过点击转换进行自定义对象迁移策略。
7)自定义对象迁移策略。
点击转换后可以设置表的映射关系,包括迁移策略和列映射选项。
在迁移策略中可根据需要设置表及数据迁移的策略。在左侧选项中可以选择“表定义”、“主键”、“约束”、“索引”等的迁移策略;在右侧选项中可以配置与迁移数据相关的策略。
8)开始迁移
检查迁移任务,确认迁移对象是否正确。
迁移失败是
没有对应的权限
给出相对应的权限
-- 授予迁移所需的所有权限
GRANT CREATE TABLE, CREATE VIEW, CREATE INDEX, CREATE SYNONYM, CREATE TRIGGER, CREATE PROCEDURE TO "etest";
GRANT RESOURCE, VTI, SOI, SVI, PUBLIC TO "etest";
或者直接在该用户下给权限
2.9迁移成功
从 Oracle 迁移数据到达梦数据库后,验证数据的完整性和准确性
三、数据校验
通过 SQL 脚本分别统计 Oracle 端和 DM 端的对象和数据量,通过对比判断是否迁移完成。
使用脚本进行验证介绍如下:
统计用户下各类对象的数量,在源端和目的端通过对应的系统表进行查询记录对比是否一致。
统计用户下的表数量及对应的数据条目,在源端和目的端分别创建辅助表,使用脚本将源端和目的端的表的数量和表的数据量插入到辅助表中,通过查看辅助表内的数据进行比对,验证表的数量和数据量是否一致。
3.1统计源端对象及数据
统计各个表的数据量。
(1)在源端创建辅助表 table_count 用来统计模式下所有表的数据量。
create table table_count (owner varchar(100),table_name varchar(100),cnt int);
(2)分别在源端和目的端执行脚本,将模式下表的数据量插入到辅助表 table_count 中,需要统计哪个模式将 ’ETEST’ 替换为模式名即可。
CREATE TABLE table_count (
owner VARCHAR2(100),
table_name VARCHAR2(100),
cnt NUMBER
);
DECLARE
v_owner VARCHAR2(100);
v_tabname VARCHAR2(100);
stmt VARCHAR2(200);
num_rows NUMBER;
BEGIN
FOR rec IN (SELECT owner, table_name
FROM dba_tables
WHERE owner = 'ETEST'
ORDER BY 1, 2)
LOOP
v_owner := rec.owner;
v_tabname := rec.table_name;
-- 构建查询语句(使用双引号处理大小写敏感的表名)
stmt := 'SELECT COUNT(*) FROM "' || v_owner || '"."' || v_tabname || '"';
-- 执行查询获取行数
EXECUTE IMMEDIATE stmt INTO num_rows;
-- 插入结果(使用绑定变量更安全)
INSERT INTO table_count (owner, table_name, cnt)
VALUES (v_owner, v_tabname, num_rows);
END LOOP;
COMMIT; -- 提交所有插入操作
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- 出错时回滚
RAISE; -- 重新抛出异常
END;
/
3.2统计各种对象的数量
在数据库中除了表还有索引,视图,序列,存储过程,PKG,自定义类型等都需要验证。
达梦客户端执行如下语句。
SELECT
A.USERNAME "用户名",
(SELECT COUNT(1) FROM DBA_TABLES B WHERE B.OWNER = A.USERNAME) "表数量",
( SELECT COUNT(1) FROM DBA_VIEWS G WHERE G.OWNER = A.USERNAME ) "视图数量",
( SELECT COUNT(1) FROM DBA_TRIGGERS H WHERE H.OWNER = A.USERNAME ) "触发器数量",
( SELECT COUNT(DISTINCT I.NAME) FROM DBA_SOURCE I WHERE I.OWNER = A.USERNAME AND I.TYPE = 'FUNCTION' ) "函数数量",
( SELECT COUNT(1) FROM DBA_SEQUENCES J WHERE J.SEQUENCE_OWNER = A.USERNAME ) "序列数量",
( SELECT COUNT(DISTINCT L.NAME) FROM DBA_SOURCE L WHERE L.OWNER = A.USERNAME AND L.TYPE = 'PROCEDURE' ) "存储过程数量",
( SELECT COUNT(1) FROM DBA_DB_LINKS M WHERE M.OWNER = A.USERNAME ) "DBLINK数量",
( SELECT COUNT(1) FROM DBA_INDEXES I WHERE UNIQUENESS = 'UNIQUE' AND OWNER =A.USERNAME OR INDEX_NAME NOT LIKE 'INDEX335%' AND OWNER =A.USERNAME) "索引数量",
( SELECT COUNT(1) FROM DBA_OBJECTS WHERE OBJECT_TYPE='TYPE' AND OWNER =A.USERNAME OR OBJECT_TYPE='CLASS' AND OWNER =A.USERNAME ) "自定义类型",
( SELECT COUNT(1) FROM DBA_OBJECTS WHERE OBJECT_TYPE='PACKAGE' AND OWNER =A.USERNAME) "PKG数量"
FROM
DBA_USERS A WHERE A.USERNAME IN ('ETEST');
3.3更新统计信息
先进行模式级别更新,再更新索引统计信息,也可以根据需求,进行单独表或者单独列进行更新,下面列出来一些更新统计信息的方式:
注意
大表更新统计信息尽量采用更新关联列、条件列以及索引的统计信息,不要更新全表。
模式更新。
当迁移后全库数据量较小时,可以使用全模式更新的方法:
DBMS_STATS.GATHER_SCHEMA_STATS( '模式名',100,TRUE,'FOR ALL COLUMNS SIZE AUTO');
DBMS_STATS.GATHER_SCHEMA_STATS( 'ETEST',100,TRUE,'FOR ALL COLUMNS SIZE AUTO');
如果数据量较大,该过程可能较慢,请耐心等待。
3.31收集表的统计信息
DBMS_STATS.GATHER_TABLE_STATS('模式名','表名',NULL,100,TRUE,'FOR ALL COLUMNS SIZE AUTO');
DBMS_STATS.GATHER_TABLE_STATS('ETEST','TEST2',,NULL,100,TRUE,'FOR ALL COLUMNS SIZE AUTO');
DBMS_STATS.GATHER_TABLE_STATS('ETEST','TEST3,TEST2’,NULL,100,TRUE,'FOR ALL COLUMNS SIZE AUTO');
如果数据量较大,该过程可能较慢,请耐心等待。
3.32收集所有列的统计信息
当全库数据量较大时,按模式更新统计信息比较慢,可以使用全列收集统计信息的方式进行收集,通过存储过程完成对全库全部列的收集:
STAT < 统计信息采样百分比 > ON [< 模式名 >.]< 表名 >(< 列名 >);
如:STAT 100 ON TEST(A);
--更新指定表的所有列的统计信息可使用 SYSDBA 或者用户自身
--DROP TABLE SYSDBA.stat_history;
--SELECT * FROM SYSDBA.stat_history;
--创建日志记录表
create table SYSDBA.stat_history (beg_time TIMESTAMP,end_time timestamp,sql_str varchar2(500),table_name varchar2(200),col_name VARCHAR2(200));
--创建存储过程
CREATE OR REPLACE PROCEDURE "SYSDBA"."TABLE_STATS"
AUTHID DEFINER
AS
declare --更新所有列的统计信息
v_sql varchar(4000);
v_begtime VARCHAR2(200);
v_endtime varchar2(200);
CURSOR c1 IS SELECT
SCH.NAME AS SCHEMA_NAME,
TAB.NAME AS TABLE_NAME ,
SYSCOL.NAME AS COLUMN_NAME
FROM
SYSOBJECTS AS TAB,
SYSOBJECTS AS SCH,
SYSCOLUMNS AS SYSCOL
WHERE
SCH.ID =TAB.SCHID
AND TAB.ID =SYSCOL.ID
AND SCH.NAME in ('ETEST') --填写实际用户名
AND SYSCOL.TYPE$ NOT IN ('BLOB','CLOB','TEXT')
and TAB.NAME IN(select TABLE_NAME from ALL_TABLES WHERE OWNER in ('SYSDBA') and TABLE_NAME not like '%BM$_%' AND TABLE_NAME NOT LIKE 'MTAB$%');
begin
execute immediate 'truncate table SYSDBA.STAT_HISTORY;';
for i in c1
loop
v_sql='stat 100 on '||i.SCHEMA_NAME||'."'||i.TABLE_NAME||'"("'||i.COLUMN_NAME||'");';
begin
v_begtime := sysdate();
execute immediate v_sql;
v_endtime := sysdate();
insert into SYSDBA.stat_history VALUES (v_begtime,v_endtime,v_sql,i.TABLE_NAME,i.COLUMN_NAME);
commit;
EXCEPTION WHEN OTHERS THEN
PRINT SQLERRM;
end;
end loop;
end;
--调用存储过程
call "SYSDBA"."TABLE_STATS" ();
select * from SYSDBA.stat_history;
3.33收集分区较多的分区表的统计信息
1)使用“STAT 100 on 表名(列名)”的方法收集统计信息时,水平分区表子表采样数受 HP_STAT_SAMPLE_COUNT 参数影响。需要适当调大此参数,参数值大于实际分区数,否则分区表统计信息收集不准确,参数修改方法如下:
sp_set_para_value(1,‘HP_STAT_SAMPLE_COUNT’,2000);
把这个参数设置为 2000 之后,当数据库收集直方图统计信息时,会基于 2000 行采样数据来进行。这对于数据分布不均匀的列(像性别列,可能大部分都是男性或者女性;或者状态列,可能大部分都是某个特定状态)非常有用。优化器可以利用这些直方图信息,更准确地估算查询结果集的大小,进而选择更优的执行计划。
2)若不调整 HP_STAT_SAMPLE_COUNT 参数,可以使用 DBMS 系统包进行 GLOBAL 全部分区收集,此方法收集统计信息速度较慢:
--按列收集:
DBMS_STATS.GATHER_TABLE_STATS ('模式名','表名',null,100,false,'FOR COLUMNS "列名" SIZE AUTO',1,'GLOBAL');
--样例:
DBMS_STATS.GATHER_TABLE_STATS ('ETEST','test2',null,100,false,'FOR COLUMNS "FULL_NAME" SIZE AUTO',1,'GLOBAL');
--按表收集:
DBMS_STATS.GATHER_TABLE_STATS ('模式名','表名',null,100,false,'FOR ALL COLUMNS SIZE AUTO',1,'GLOBAL');
--样例:
DBMS_STATS.GATHER_TABLE_STATS ('ETEST','TEST2',null,100,false,'FOR ALL COLUMNS SIZE AUTO',1,'GLOBAL');
3.34清除列的统计信息
SP_COL_STAT_DEINIT('模式名','表名','列名');
SP_COL_STAT_DEINIT('ETEST','TEST2','FULL_NAME');
本次构建了 MySQL 8.0 的单机示例库,并介绍利用 DTS 工具从 MySQL 8.0 移植 dbtest 库中的所有对象到 DM8 数据库的详细步骤
数据库信息
提前对源端 MySQL 数据库做相关了解,一方面为后面的安装提供参考依据,另一方面提前了解迁移数据量、字符编码、归档保留等信息为后续迁移提前做好充分准备。
注意
如果 MySQL 是以字节为单位,在使用 DMDTS 进行迁移时需设置数据类型映射,将 varchar 和 char 类型映射为 varchar( char) 类型。
-- 创建用户并授权
CREATE USER 'mytest'@'localhost' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON *.* TO 'mytest'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
-- 创建数据库
CREATE DATABASE IF NOT EXISTS company_db;
USE company_db;
4.1.3 统计指定库中的表的数目
SELECT COUNT(*) TABLES, TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '数据库名称' GROUP BY TABLE_SCHEMA;
--示例
SELECT COUNT(*) TABLES, TABLE_SCHEMA FROM INFORMATION_[达梦社区地址 ](https://eco.dameng.com)SCHEMA.TABLES WHERE TABLE_SCHEMA = 'employee_db' GROUP BY TABLE_SCHEMA;
4.1.4 统计指定库中视图的数目
SELECT TABLE_SCHEMA,COUNT(*) VIEWS FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = '数据库名称' GROUP BY TABLE_SCHEMA;
--示例
SELECT TABLE_SCHEMA,COUNT(*) VIEWS FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = 'employee_db' GROUP BY TABLE_SCHEMA;
SELECT SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='数据库名称';
--示例
SELECT SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE='PROCEDURE' AND ROUTINE_SCHEMA='employee_db';
SELECT SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE='FUNCTION' AND ROUTINE_SCHEMA='数据库名称';
--示例
SELECT SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE='FUNCTION' AND ROUTINE_SCHEMA='employee_db';
SELECT TRIGGER_SCHEMA,TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA= '数据库名称';
--示例
SELECT TRIGGER_SCHEMA,TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA= 'employee_db';
CREATE TABLE MYSQL_TABLES(TAB_OWNER VARCHAR(100),TAB_NAME VARCHAR(100),TAB_COUNT INT); INSERT INTO MYSQL_TABLES SELECT TABLE_SCHEMA,TABLE_NAME,TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '数据库名称' ORDER BY TABLE_ROWS DESC;
--示例
CREATE TABLE MYSQL_TABLES(TAB_OWNER VARCHAR(100),TAB_NAME VARCHAR(100),TAB_COUNT INT); INSERT INTO MYSQL_TABLES SELECT TABLE_SCHEMA,TABLE_NAME,TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'employee_db' ORDER BY TABLE_ROWS DESC;
调研项 | 说明 |
---|---|
库名 | employee_db |
表数目 | 7 |
视图数目 | 3 |
存储过程 | 2 |
函数 | 0 |
触发器 | 2 |
SELECT table_name, num_rows, tablespace_name
FROM all_tables
WHERE owner='EMPLOYEE_DB';
数据验证一致,迁移成功。
DBLink 的核心作用
A机器
达梦数据库主机:10.127.17.107
版本:麒麟V10
IP:10.127.17.107
达梦数据库版本与架构:DM8-ENT 单机
B机器
达梦数据库主机:10.127.17.108
版本:麒麟V10
IP:10.127.17.108/110
达梦数据库版本与架构:DM8-ENT 集群
达梦搭建同构dblink是通过达梦MAL系统进行数据传输,达梦通过mal系统实现redo日志传输,以及其他实例间的消息通讯。开启MAL功能后,本地达梦数据库创建dblink即可实现访问;相对而言达梦之间的dblink搭建较为简单。
(1)三台达梦主机修改dm.ini配置文件参数MAL_INI值为1:
[dmdba@qiqi221 ~]$ vim /opt/dm8/data/DAMENG/dm.ini
## 将MAL_INI参数设置为1;
MAL_INI = 1
A机器
B机器
(2)三台达梦主机配置dmmal.ini文件内容:
MAL_CHECK_INTERVAL = 10
MAL_CONN_FAIL_INTERVAL = 10
MAL_TEMP_PATH = /opt/dmdbms/data/malpath/
MAL_BUF_SIZE = 512
MAL_SYS_BUF_SIZE = 2048
MAL_COMPRESS_LEVEL = 0
[MAL_INST1]
MAL_INST_NAME = GRP1_RT_01
MAL_HOST = 10.127.17.107
MAL_PORT = 5238
MAL_INST_HOST = 10.127.17.107
MAL_INST_PORT = 5236
MAL_DW_PORT = 5239
MAL_INST_DW_PORT = 5237
[MAL_INST2]
MAL_INST_NAME = GRP1_RT_02
MAL_HOST = 10.127.17.110
MAL_PORT = 5238
MAL_INST_HOST =10.127.17.110
MAL_INST_PORT = 5236
MAL_DW_PORT = 5239
MAL_INST_DW_PORT = 5237
[MAL_INST3]
MAL_INST_NAME = PROD
MAL_HOST = 10.127.17.108
MAL_PORT = 5233
注意:两台达梦主机的dmmal.ini文件内容要一致;dmmal.ini配置项中的MAL_INST_NAM是实例名,两边的实例名不能相同;为得到dblink的使用效果,在测试环境下,选择关闭防火墙和SELINUX,设置为开启不自启;
(3)达梦两边主机重启数据库服务:
# 本地端重启服务:107
[dmdba@dameng bin]$ ./DmServiceGRP1_RT_01 start
Starting DmServiceGRP1_RT_01: [ OK ]
[dmdba@dameng bin]$ ./DmWatcherServiceWatcher start
Starting DmWatcherServiceWatcher: [ OK ]
# 本地端重启服务:107
[dmdba@dameng bin]$ ./DmServiceGRP1_RT_02 start
Starting DmServiceGRP1_RT_02: [ OK ]
[dmdba@dameng bin]$ ./DmWatcherServiceWatcher start
Starting DmWatcherServiceWatcher: [ OK ]
# 本地端重启服务:108
[dmdba@dameng data]$ ./bin/DmServicePROD start
(4)创建dblink语句:
-- 创建私有dblink;
CREATE LINK "DMTEST"."SRLINK" CONNECT WITH "DMSR" IDENTIFIED BY "DMSR12345" USING '192.168.222.223/5236';
-- 创建公有dblink;
B机器
CREATE PUBLIC LINK LINK01
CONNECT WITH "SYSDBA"
IDENTIFIED BY "Dameng123"
USING '10.127.17.107:5236';
A机器
CREATE TABLE TEST(C1 INT,C2 VARCHAR(20));
1.查看DBLINK信息
验证DBLINK连通性
SELECT 'LINK01 STATUS: ' || CASE WHEN COUNT(*) > 0 THEN 'ACTIVE' ELSE 'INACTIVE' END FROM DUAL@LINK01;
2.本地dblink查看远程数据
SELECT * FROM DUAL@LINK01;
3. 执行DML语句
INSERT INTO TEST@LINK01 VALUES(1,'A');
INSERT INTO TEST@LINK01 VALUES(2,'B');
UPDATE TEST@LINK01 SET C2='C' WHERE C1=1;
DELETE FROM TEST@LINK01 WHERE C1=2;
COMMIT;
DROP TABLE TEST;
-- 删除已存在的LINK01(若有)
DROP PUBLIC LINK LINK01;
4.执行存储过程
-- 创建存储过程
CREATE OR REPLACE PROCEDURE simple_test_proc
AS
v_count INTEGER;
v_max_id INTEGER;
BEGIN
-- 1. 查询当前记录数
SELECT COUNT(*) INTO v_count FROM TEST@LINK01;
DBMS_OUTPUT.PUT_LINE('当前表中有 ' || v_count || ' 条记录');
-- 2. 找出最大的C1值
SELECT NVL(MAX(C1), 0) INTO v_max_id FROM TEST@LINK01;
DBMS_OUTPUT.PUT_LINE('当前最大的C1值是: ' || v_max_id);
-- 3. 插入一条新记录
INSERT INTO TEST@LINK01 (C1, C2) VALUES (v_max_id + 1, '新记录');
COMMIT;
DBMS_OUTPUT.PUT_LINE('已插入一条新记录,C1=' || (v_max_id + 1));
-- 4. 显示所有记录
DBMS_OUTPUT.PUT_LINE('更新后的所有记录:');
FOR rec IN (SELECT * FROM TEST@LINK01 ORDER BY C1) LOOP
DBMS_OUTPUT.PUT_LINE('C1=' || rec.C1 || ', C2=' || rec.C2);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('发生错误: ' || SQLERRM);
ROLLBACK;
END;
/
5.达梦本地端调用存储过程:
调用存储过程
-- 插入一条数据
CALL SIMPLE_INSERT_REMOTE(5, 'E');
-- 插入另一条数据
CALL SIMPLE_INSERT_REMOTE(6, 'F');
-- 启用DBMS_OUTPUT输出
SET SERVEROUTPUT ON;
-- 执行存储过程
CALL simple_test_proc();
6.验证结果
-- 查看所有数据
SELECT * FROM TEST@LINK01 ORDER BY C1;
A机器-DM8
达梦数据库主机:10.127.17.108 版本:麒麟V10 IP:10.127.17.107 达梦数据库版本与架构:DM8-ENT 单机
B机器-Oracle
达梦数据库主机:10.127.17.101 版本:麒麟V10 IP:10.127.17.108/110 达梦数据库版本与架构:Oracle 19C 集群
注:需要下载oracle对应版本的客户端
下载三个zip包:basic、sdk、sqlplus解压
mkdir -p /dm/oracle19c
unzip xxx --三个包都自动解压到这一个目录里面了
将libclntsh.so.19.1文件复制1份命名为libclntsh.so 或者做一个软链接
查看libclntsh.so.19.1的依赖,确保没有出现no found
ldd /dm/data/bin/libclntsh.so.19.1
环境配置:解压客户端的路径/dm/oracle19c
测试能否从达梦访问Oracle端
[dmdba@dameng instantclient_19_27]$ sqlplus USER01/123456@10.127.17.101:1521/orclpdb
CREATE OR REPLACE LINK TESTLINK1
CONNECT 'ORACLE'
WITH USER01 IDENTIFIED BY 123456
USING '(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 10.127.17.101)(PORT = 1521))
(CONNECT_DATA = (SERVICE_NAME = orclpdb))
)';
在Oracle端设置
SELECT * FROM SYS.TEST@TESTLINK1;
Oracle端
达梦社区地址 :https://eco.dameng.com
文章
阅读量
获赞