背景

由于要替换 HANA、TeraData,必然会有数据迁移的问题
这里参考 腾讯的 DB-bridege,列出迁移中可能的问题

这里有两份腾讯的迁移相关文档,也值得参考

迁移步骤

image
上图是迁移 Oracle 时,给出的评估
对于HANA、TD 我们根据上图,也列出类似的情况:

  • 索引
  • 约束
  • 视图
  • 物化视图
  • 函数
  • 存储过程
  • 触发器
  • 数据类型

对于是否能迁移列出三种情况:

  • 完全兼容,比如 varchar -> varchar,这种不用改,直接迁移
  • 可转换的,比如Oracle的varchar2,可以用varchar替代
  • 不可转换的,比如 自定义的函数,这种就没有对应的,需要在目标端也做修改

根据db-bridge的文档,在迁移Oracle时,也无法做到 100% 兼容和可转换,必然有一些无法转换的
腾讯给出的例子,无法转换的比例是 1.8% , 考虑到是对外宣传的,实际这个数字可能会高不少
而无法转换,就会导致双方都需要改动,也就是主要的迁移成本

整个迁移过程包括:

  • 评估
  • 测试环境搭建
  • 全量数据迁移
  • 数据一致性校验
  • 增量数据迁移
  • 数据一致性再校验
  • 反向迁移(可选)

重点问题

  • 源和目标数据库的表、数据类型兼容问题
    • 写出迁移工具,实现自动化转换工具
    • 对于少量不支持的情况,可以手动修改
    • 目标库的表建完后,要跑一边源端的SQL,检查兼容性
  • ETL工具本身的高可用
    • db-bridge是将数据写入到本地的db中(这个db修改自PG)来保障同步过程中数据不丢
    • 数据拷贝过程中,需要读源库(一般是从库),但不能对源库造成太大压力
  • 增量数据如何同步
    • db-bridge可能是针对Oracle开发了一套增量迁移工具
    • 现有方案一般是将增量数据写入到kafka中
    • 也可以将源端写入到kafka中,然后从kafka消费数据,再同时写入到 源库和目标库,需要修改应用层代码
    • 增量同步过程中的也要保证高可用,数据不能丢
  • 一致性校验

下面列出 HANA、TeraData的数据类型、各种语法

HANA

数据类型

参考自 官方文档

数据类型 描述
VARBINARY 可变的二进制长度,默认为1,可以指定长度n
BOOLEAN
VARCHAR
NVARCHAR 存储unicode的varchar
ALPHANUM 包含字母数字的的可变长字符串
SHORTTEXT 可变长字符,但只能用于 列存
DATE
TIME
SECONDDATE
TIMESTAMP
BLOB
CLOB
NCLOB 类似CLOB,存储Unicode 值
TEXT
ARRAY 数组类型,其元素可以是整数、浮点数、变长字符,不支持空间、LOB,也不支持多维数组
TINYINT 0 - 255
SMALLINT -32,768 到 32,767
INTEGER -2,147,483,648 到 2,147,483,647
BIGINT -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
DECIMAL
SMALLDECIMAL
REAL
DOUBLE -1.7976931348623157E308 到 1.7976931348623157E308
FLOAT

HANA Data Types for Spark

系统和数据类型限制

内置的函数

由于内置的函数很多,这里只给出官方文档的链接

特殊语法

一些语法示例

这里直接引用官方文档内容:

索引和视图

全文搜索 ,参考 -> 这里

1
2
CREATE FULLTEXT INDEX <index_name> ON <table_name> ( <column_name> ) 
   [ <fulltext_parameter_list> ]

索引 ,参考 -> 这里

1
2
3
4
5
6
CREATE [ UNIQUE ] [ BTREE | CPBTREE | INVERTED [ <composite_type> ] ] INDEX <index_name> ON <table_name>
   ( <column_name_order_entry>[, <column_name_order_entry> [,...]) 
   [ <global_index_order> ] 
   [ <fillfactor> ] 
   [ NOWAIT ] 
   [ ONLINE ]  

投影视图, 参考 -> 这里

1
2
3
4
5
6
CREATE [ OR REPLACE ] PROJECTION VIEW <projection_view_name> [ ( <view_column_name_list> ) ]
 AS SELECT <table_column_list>
 FROM [ <schema>.]<source_table_name>
 [ <with_association_clause> ]
 [ <with_annotations_clause> ]
 [ WITH DDL ONLY ]

视图,参考 -> 这里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
CREATE [ OR REPLACE ] VIEW <view_name> [ COMMENT <string_literal> ]
 [ ( <column_name_list> ) ] [ ( <parameterized_view_clause> ) ] 
 <as_subquery> [ <with_association_clause> ]
 [ WITH MASK ( <column_name> USING <mask_expression> [,...] ) ]
 [ WITH EXPRESSION MACROS( <expression_macro_list> ) ]
 [ WITH <annotations> ]
 [ WITH STRUCTURED PRIVILEGE CHECK ]
 [ WITH [ <cache_type> ] CACHE ]
 [ RETENTION <minute_value> ]
 [ OF <projection_list> ] ]
 [ FILTER <filter_condition> ]
 [ <location_clause> ]
 [ FORCE ]
 [ WITH CHECK OPTION ]
 [ WITH DDL ONLY ]
 [ WITH READ ONLY ]
 [ WITH ANONYMIZATION ( ALGORITHM <algorithm_name> { [ <view_level_parameters> ] [ <column_level_parameters> ] } ) ]

建表、scheme、同义词等

schema和同义词, 参考 -> schema
同义词

1
2
3
CREATE SCHEMA <schema_name> [OWNED BY <user_name>]   

CREATE [OR REPLACE] SCHEMA SYNONYM <synonym_name> FOR <schema_reference>

同义词 , 参考 -> 这里

1
CREATE [ OR REPLACE ] [ PUBLIC ] SYNONYM <synonym_name> FOR <synonym_source_object_name>

建表, 参考 -> 这里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CREATE [ <table_type> ] { <table_name> | <replica_name> }
   [<table_contents_source>
   [<system_versioning_spec>]
   [<application_time_period_configuration>]
   [<bi_temporal_table_spec>]
   [<with_association_clause>] 
   [<with_annotation_clause>]
   [<with_mask_clause>]        
   [<logging_option>] 
   [<auto_merge_option>]
   [<unload_priority_clause>]
   [<schema_flexibility_option>] 
   [<partition_clause>] 
   [<persistent_memory_spec_clause>] 
   [<group_option_list>]
   [<location_clause>] 
   [<replica_clause>] 
   [<global_temporary_option>] 
   [<series_clause>] 
   [<unused_retention_period_option>] 
   [<record_commit_timestamp_clause>]            
   [COMMENT <comment_string>]
   [<numa_node_preference_clause>]
   [<load_unit>]

虚拟表,参考 -> 这里

1
2
CREATE VIRTUAL TABLE <virtual_table_name> 
    AT <remote_location_clause> [ <remote_property_clause> ]

存储过程

函数,参考 -> 这里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
CREATE [ OR REPLACE ] FUNCTION <function_name> 
  [ ( <input_parameter_clause> ) ] 
   RETURNS <return_type>
   [ LANGUAGE <lang> ]
   [ SQL SECURITY <mode> ] 
   [ DEFAULT SCHEMA <default_schema_name> ]
   [ DETERMINISTIC ] 
   [ <cache_clause> ]
   [ <variable_cache_clause> ]
   [ WITH ENCRYPTION ]
    AS
     { BEGIN 
          <statement_body> 
       END 
       | HEADER ONLY }

虚拟函数,参考 -> 这里

1
2
3
CREATE VIRTUAL FUNCTION <func_name> [(<parameter_clause>)]
   RETURNS <return_table_type> [SQL SECURITY <mode>] [<package_clause>]
   CONFIGURATION <remote_proc_properties> AT <source_name>

存储过程,参考 -> 这里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
CREATE [ OR REPLACE ] PROCEDURE <proc_name> [ (<input_output_parameter_clause>) ]
 [ LANGUAGE <lang> ]
 [ SQL SECURITY <mode> ]
 [ DEFAULT SCHEMA <default_schema_name> ]
 [ DETERMINISTIC ] 
 [ READS SQL DATA [ WITH RESULT VIEW <view_name> ] ] 
 [ <route_target_element> ] 
 [ <variable_cache_clause> ]
 [ WITH ENCRYPTION ]
 [ AUTOCOMMIT DDL { ON | OFF } ]
 AS 
   { BEGIN [ SEQUENTIAL EXECUTION ]
      <statement_body>
     END
     | HEADER ONLY }

虚拟存储过程,参考 -> 这里

1
2
3
4
5
6
7
8
9
CREATE VIRTUAL PROCEDURE <proc_name> [(<parameter_clause>)]
  [SQL SECURITY <mode>] 
  [<package_clause>] LANGUAGE <lang>
   AT <source_name>
[AS
  BEGIN 
          <procedure_body>
   END
   ]

用户定义的类型,参考 -> 这里

1
2
CREATE TYPE <type_name>
   AS TABLE (<column_definition>[{,<column_definition>}...]) [ SQLSCRIPT SEARCH KEY( <column_name> [,…] ) ]

自增列

序列 ,参考 -> 这里

1
2
CREATE SEQUENCE <sequence_name> [ <sequence_parameter_list> ]
   [ RESET BY <subquery> ]

也可以设置某一列为自增的

1
2
3
CREATE COLUMN TABLE "TEST" (
    "ID" bigint NOT NULL primary key GENERATED BY DEFAULT AS IDENTITY
);

安全

详细语法,参考 -> 1 2

1
2
3
4
5
CREATE CLIENTSIDE ENCRYPTION COLUMN KEY [ <schema_name>.]<key_name> 
   [ ALGORITHM <algorithm_name> ]
   { ENCRYPTED WITH KEYPAIR <keypair_name> | HEADER ONLY }

CREATE CLIENTSIDE ENCRYPTION KEYPAIR <keypair_name> [ ALGORITHM <algorithm_name> ]

数据集成

详细语法细节,参考 -> 这里

 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
CREATE ADAPTER <adapter_name> [PROPERTIES <properties>] AT LOCATION 
{DPSERVER | AGENT <agent_name>}

CREATE AGENT <agent_name> PROTOCOL { 'HTTP' | 'TCP' HOST <agent_hostname> PORT <agent_port_number> [{ENABLE | DISABLE} SSL]} [AGENT GROUP <agent_group_name>]

CREATE AGENT GROUP <agent_group_name>

<audit_action_name> ::= CREATE AGENT                 
	| ALTER AGENT                  
	| DROP AGENT                   
	| CREATE AGENT GROUP           
	| DROP AGENT GROUP             
	| CREATE ADAPTER               
	| ALTER ADAPTER                
	| DROP ADAPTER                 
	| CREATE REMOTE SUBSCRIPTION   
	| ALTER REMOTE SUBSCRIPTION    
	| DROP REMOTE SUBSCRIPTION     
	| PROCESS REMOTE SUBSCRIPTION EXCEPTION

CREATE REMOTE SUBSCRIPTION [<schema_name>.]<subscription_name> 
{ 
   {ON [<schema_name>.]<virtual_table_name> } | 
   {AS (<subquery>)}
}
[ WITH SCHEMA CHANGES ]
{ TARGET TABLE <table_spec> <load_behavior> } | 
{ TARGET TASK <task_spec> } | 
{ PROCEDURE <proc_spec> } 

其他

图表工作区 ,参考 -> 这里

1
2
3
4
5
6
7
CREATE GRAPH WORKSPACE <workspace_name>
   EDGE TABLE <edge_table_name>
      SOURCE COLUMN <source_column_name>
      TARGET COLUMN <target_column_name>
      KEY COLUMN <edge_key_column_name>
   VERTEX TABLE <vertex_table_name>
      KEY COLUMN <vertex_key_column_name>

统计信息 ,参考 -> 这里

1
2
3
4
CREATE STATISTICS [ <data_statistics_name> ] ON <data_sources> 
 [ <data_statistics_type> ] 
 [ <data_statistics_properties> ]
 [ <initial_refresh> ]

触发器,参考 -> 这里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CREATE [ OR REPLACE ] TRIGGER <trigger_name> <trigger_action_time> <trigger_event_list>
   ON <subject_table_name>
   [ REFERENCING <transition_list> ] 
   [ <for_each> ] 
   [ <trigger_order_clause> ]
   [ ONLINE ]
      BEGIN 
         [ <trigger_decl_list> ]
         [ <proc_handler_list> ]
         <trigger_stmt_list>
      END

租户,参考 -> 这里

1
2
3
4
5
6
CREATE DATABASE <database_name>  [ AT [ LOCATION ] '<hostname>[:<port_number> ] ' ]
 { ADD '<servicetype>' [ AT [ LOCATION ] '<hostname>[:<port_number> ] ' ] }... 
 [ { AS REPLICA OF [ <source_database_name> ] AT [ LOCATION ] '<hostname>[:<port_number> ]' | SYSTEM USER PASSWORD <password> } ]
 [ OS USER '<username>' OS GROUP '<groupname>' ] 
 [ NO START ]
 [ <restart_mode> RESTART ]

workload 和映射,参考 -> 这里
映射

1
2
3
4
CREATE WORKLOAD CLASS <workload_class_name> [ <property_list> ] [ ENABLE | DISABLE ]

CREATE WORKLOAD MAPPING <mapping_name> 
WORKLOAD CLASS <workloadclass_name> [ <property_list> [ <wildcard_option> ] ]

WAL

没有找到相关资料

案例

TeraData

数据类型

参考自 官方文档
文档中没有列出数据的范围

数据类型 描述
ARRAY
VARRAY 同上,长度可变
BYTE 二进制类型
VARBYTE 同上,长度可变
BLOB 二进制大对象
CHAR
VARCHAR
CLOB
AVRO 带有schema 自描述的数据类型
DATE
TIME
TIMESTAMP
TIME WITH TIME ZONE
TIMESTAMP WITH TIME ZONE
ST_Geometry 地理位置数据,TD提供了对这个数据的检索、分析接口
MBR 也是地理位置数据
INTERVAL YEAR 时间间隔类型
INTERVAL YEAR TO MONTH
INTERVAL MONTH
INTERVAL DAY
INTERVAL DAY TO HOUR
INTERVAL DAY TO MINUTE
INTERVAL DAY TO SECOND
INTERVAL HOUR
INTERVAL HOUR TO MINUTE
INTERVAL HOUR TO SECOND
INTERVAL MINUTE
INTERVAL MINUTE TO SECOND
INTERVAL SECOND
JSON json格式数据
BYTEINT 整数类型
SMALLINT
INTEGER
BIGINT
DECIMAL/NUMERIC 浮点类型
FLOAT/REAL/DOUBLE PRECISION
NUMBER
TD_ANYTYPE 参数类型,这种类型只能用在 函数、方法、存储过程、外部存储过程的输入/输出中
VARIANT_TYPE 同上
PERIOD(DATE) 时间周期数据,其周期是一系列时间粒度数据,包含开头边界,但没有结束边界
PERIOD(TIME) 同上
PERIOD(TIME WITH TIME ZONE) 同上
PERIOD(TIMESTAMP) 同上
PERIOD(TIMESTAMP WITH TIME ZONE) 同上
Distinct 用户自定义的数据类型
Structured 同上
XML XML格式数据,内部是按照二进制方式存储的

内置的函数

由于内置的函数很多,这里只给出官方文档的链接

特殊语法

设置多重集表

Teradata基于如何处理重复记录将表分类为SET或MULTISET表。

  • SET表的表不存储重复记录
  • MULTISET表可存储重复记录

建表语句如下: 注意,建表时有一个关键字FALLBACK ,这相当于配置了一个副本,用于高可用的

1
2
3
4
5
6
7
8
9
CREATE SET TABLE EMPLOYEE,FALLBACK ( 
   EmployeeNo INTEGER, 
   FirstName VARCHAR(30), 
   LastName VARCHAR(30), 
   DOB DATE FORMAT 'YYYY-MM-DD', 
   JoinedDate DATE FORMAT 'YYYY-MM-DD', 
   DepartmentNo BYTEINT 
) 
UNIQUE PRIMARY INDEX ( EmployeeNo );

这是TD的特殊语法,类似存储过程,也有输入参数
宏里面包括多个SQL语句,而且整个 宏,是当做事务来执行的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CREATE MACRO <macroname> [(parameter1, parameter2,...)] ( 
   <sql statements> 
);

CREATE MACRO Get_Emp AS ( 
   SELECT EmployeeNo, FirstName, LastName 
   FROM  
   employee 
   ORDER BY EmployeeNo; 
);

-- 执行宏
EXEC <macroname>; 

临时表

TD包含三种临时表:

  • 一次查询中的,这是自动产生的
  • 一次session中的
  • 全局的

主要考虑后两种
session临时表

1
2
3
4
5
6
7
8
CREATE VOLATILE TABLE dept_stat ( 
   dept_no INTEGER, 
   avg_salary INTEGER, 
   max_salary INTEGER, 
   min_salary INTEGER 
) 
PRIMARY INDEX(dept_no) 
ON COMMIT PRESERVE ROWS;

全局临时表

1
2
3
4
5
6
7
CREATE SET GLOBAL TEMPORARY TABLE dept_stat ( 
   dept_no INTEGER, 
   avg_salary INTEGER, 
   max_salary INTEGER, 
   min_salary INTEGER 
) 
PRIMARY INDEX(dept_no);

物化视图

join索引其实就是物化视图,包含这么几种:

  • 单表连接索引(STJI)
  • 多表连接索引(MTJI)
  • 聚合连接索引(AJI)

语法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
CREATE JOIN INDEX <index name> 
AS 
<SELECT Query> 
<Index Definition>;

-- 创建索引
CREATE JOIN INDEX Employee_JI 
AS 
SELECT EmployeeNo,FirstName,LastName, 
BirthDate,JoinedDate,DepartmentNo 
FROM Employee 
PRIMARY INDEX(FirstName);

索引

主索引,类似主键

  • 唯一主索引(UPI)
  • 非唯一主索引(NUPI),允许重复
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
-- 唯一主索引
CREATE SET TABLE Salary ( 
   EmployeeNo INTEGER, 
   Gross INTEGER,  
   Deduction INTEGER, 
   NetPay INTEGER 
) 
UNIQUE PRIMARY INDEX(EmployeeNo);

-- 非唯一主索引
CREATE SET TABLE Employee _Accounts ( 
   EmployeeNo INTEGER, 
   employee_bank_account_type BYTEINT. 
   employee_bank_account_number INTEGER, 
   employee_bank_name VARCHAR(30), 
   employee_bank_city VARCHAR(30) 
) 
PRIMARY INDEX(EmployeeNo);

二级索引
包括:

  • 唯一二级指数(USI)
  • 非唯一二级指数(NUSI),允许重复
1
2
3
4
5
-- 唯一二级索引
CREATE UNIQUE INDEX(EmployeeNo) on employee;

-- 非唯一二级索引
CREATE INDEX(FirstName) on Employee;

分区索引

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
CREATE SET TABLE Orders (
   StoreNo SMALLINT, 
   OrderNo INTEGER, 
   OrderDate DATE FORMAT 'YYYY-MM-DD', 
   OrderTotal INTEGER 
) 
PRIMARY INDEX(OrderNo) 
PARTITION BY RANGE_N  (
   OrderDate BETWEEN DATE '2010-01-01' AND '2016-12-31' EACH INTERVAL '1' DAY
);

压缩

限制:

  • 每列只能压缩255个值
  • 主索引列不能压缩
  • 不能压缩易失性表

列应用压缩时,此列的值不与该行一起存储。 相反,值存储在每个AMP中的表头中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- 压缩第1、2、3列
CREATE SET TABLE employee ( 
   EmployeeNo integer, 
   FirstName CHAR(30), 
   LastName CHAR(30), 
   BirthDate DATE FORMAT 'YYYY-MM-DD-', 
   JoinedDate DATE FORMAT 'YYYY-MM-DD-', 
   employee_gender CHAR(1), 
   DepartmentNo CHAR(02) COMPRESS(1,2,3) 
) 
UNIQUE PRIMARY INDEX(EmployeeNo);

自增列

语法如下,官方文档

1
2
3
4
5
6
7
8
9
create table TEST.i2 (
i INTEGER GENERATED BY DEFAULT AS IDENTITY
(START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO CYCLE
),
STR VARCHAR(20)
) unique primary index i2 (i)

其他

存储过程

1
2
3
4
5
6
7
CREATE PROCEDURE <procedurename> ( [parameter 1 data type, parameter 2 data type..] ) 
BEGIN 
   <SQL or SPL statements>; 
END;

--调用
CALL <procedure name> [(parameter values)];

统计信息

1
2
3
4
COLLECT STATISTICS COLUMN(EmployeeNo) ON Employee;   

-- 查看表的统计信息
HELP STATISTICS employee;

不区分大小写 CASESPECIFIC

1
2
3
CREATE TABLE xxx (
   name VARCHAR(200) CHARACTER SET LATIN NOT CASESPECIFIC,
);

COALESCE
返回表达式的第一个非空值的语句。 如果表达式的所有参数计算结果为NULL,则返回NULL

1
2
3
4
5
SELECT 
   EmployeeNo, 
   COALESCE(dept_no, 'Department not found') 
FROM  
   employee;

WAL

没有找到WAL的具体格式
TD是OLAP类型的,所以WAL这块不用特别考虑,增量这块就用停机迁移来解决
像 TD自己的大版本迁移都是会有停机迁移情况

案例

建行数据从Teradata迁移到Greenplum
Teradata应用迁移至AnalyticDB PostgreSQL
Teradata SQL迁移GaussDB
传统数仓从 Teradata 迁移到 Hadoop平台实践
如何从Teradata迁移到Greenplum(上篇)
如何从Teradata迁移到Greenplum(下篇)

其他情况

1、转义字符
2、大小写

参考

TeraData官方文档
TeraData在线语法手册

HANA官方手册
HANA官方SQL语法文档

TeraData在线教程
HANA在线教程

Oracle应用迁移至AnalyticDB PostgreSQL
TBASE和ORACLE语法兼容清单.pdf
腾讯DBbridge产品-典型交付案例介绍.pptx