达梦简介

新一代大型通用关系型数据库,全面支持 ANSI SQL 标准和主流编程语言接口/开发框架。行列融合存储技术,在兼顾 OLAP 和 OLTP 的同时,满足 HTAP 混合应用场景。

问题记录

安装问题

  1. 字符集设置

安装完达梦数据库后,需要初始化实例,在初始化实例时,需要注意字符集的设置。字符集默认为GB18030。此时需要根据实际情况设置字符集,防止后面出现从其它数据库迁移过来的数据因为字符集不一致而乱码的问题。

  1. 字符串比较大小写敏感设置

安装完达梦数据库后,需要初始化实例,在初始化实例时,需要注意大小写敏感的设置。大小写敏感只能在初始化数据库的时候设置,默认为大小写敏感,一旦设置成功就无法修改,如果想要修改,只能重新初始化实例。

基本概念的理解问题

  1. 模式

为了便于理解可以把达梦中的模式类比为MySQL中的数据库。从MySQL迁移到达梦时,针对MySQL中的一个数据库,达梦这边一般要相应地创建一个模式。

  1. 用户

创建一个用户的同时也会创建一个同名的模式。用户和模式的关系是1:N。

  1. 表空间

通俗来讲,表空间就是用来存储数据的地方。创建用户的时候可以指定对应的表空间,指定之后用户对应模式下的数据都会存到指定的表空间中。用户和表空间的关系是N:1。

数据迁移问题

  1. 保持对象名大小写

使用达梦提供的数据迁移工具进行数据迁移时,可选择是否保持对象名大小写,默认不勾选,此时迁移到达梦之后对象名默认都是大写。

  1. 非法的基类名[AUTO_INCREMENT]

从MySQL迁移数据到达梦的过程中,当我们的源表有自增字段时,迁移的时候会报“非法的基类名[AUTO_INCREMENT]”的错误,因为达梦不支持“AUTO_INCREMENT”写法。这时不用急,因为数据还是会完整的迁移过去,只是原来的自增字段迁移到达梦之后没有成功设置为自增,这个后面可以手动修改为自增。

  1. 示例:
alter table tableName add column "id" identity(1, 1);
  1. 列[xxx]长度超出定义

从MySQL迁移数据到达梦的过程中,如果列存储的是中文,则在迁移过程中可能报“列[xxx]长度超出定义”错误。因为MySQL中varchar(1) 是以字符为单位,因此可以存一个汉字;而达梦默认是以字节为单位,所以若是gb18030字符集,varchar(2)才可以存一个汉字,若是UTF-8字符集,varchar(3) 才可以存一个汉字。此时可扩大字段长度或者在初始化数据库实例时设置参数length_in_char=1,即VARCHAR类型对象的长度以字符为单位。

开发问题

  1. 设置兼容MySQL语法

在 dm.ini 文件中修改参数 compatible_mode=4,并重启数据库服务。

  1. 达梦关键字/保留字问题

直接对达梦关键字/保留字进行查询会报错,为防止报错需加上双引号处理。(ps:这里需要注意MySQL是否设置了兼容双引号,默认MySQL是不兼容的,需要在my.ini中配置sql_mode=‘ANSI_QUOTES’)

  1. 自增字段赋值问题

达梦默认无法手动对自增字段赋值,可根据实际业务情况判断字段是否有必要设置为自增,否则需要进行特殊处理。

  1. 正则匹配

MySQL语法:对象 REGEXP 正则表达式
达梦语法:REGEXP_LIKE(对象, 正则表达式)

  1. 匹配MySQL information_schema中的表
    针对MySQL information_schema数据库中的各种表,达梦可在SYS模式中的视图中找到匹配的选项。

例如:
information_schema.tables -> SYS.DBA_TABLES
information_schema.columns -> SYS.DBA_COL_COMMENTS

  1. 获取自增字段当前值
    MySQL语法:
SELECT AUTO_INCREMENT-1 FROM information_schema.tables WHERE table_schema = 'tableSchema' AND TABLE_NAME = 'tableName';

达梦语法:

SELECT IDENT_CURRENT('tableSchema.tableName');
  1. 匹配MySQL GROUP_CONCAT()函数

达梦没有GROUP_CONCAT()函数,可使用WM_CONCAT()函数代替。

  1. 别名为关键字/保留字问题

当别名为关键字/保留字时,达梦会自动转换为大写。此时加上双引号可避免自动转为大写。

  1. 匹配MySQL DATE()函数

达梦没有DATE()函数,可使用DATE_FORMAT(对象, ‘%Y-%m-%d’)代替。

  1. 无法匹配MySQL DATE_FORMAT()中%x、%v写法问题 参考文档

细心的读者会发现参考文档中提到——如果服务端使用JAVA编程,那么应该使用模式3来获取周,因为JAVA里面的周,取值范围只有1~53。目前达梦没有符合要求的函数,因此为了匹配MySQL DATE_FORMAT()中%x、%v的特性,在获取对应年的时候需要特殊处理。

  1. 示例:
case when month(create_time) = 1 and week(create_time, 3) >= 52 then year(create_time) - 1
when month(create_time) = 12 and week(create_time, 3) = 1 then year(create_time) + 1
else year(create_time) end as count_year,
week(create_time, 3) as count_week
  1. 对自定义列进行GROUP BY出现无效列名问题

达梦自定义列不能直接加入到group by中,会报无效的列名错误。

  1. 查询语句报不是GROUP BY问题

达梦数据库SELECT后的字段,必须要在GROUP BY后出现,除非是统计函数,否则会报不是GROUP BY的错误。设置兼容模式为MySQL则不会有这个问题。

  1. CAST()函数与MySQL使用效果不一致问题

达梦中的cast()函数的用法虽然和MySQL一致,但使用效果存在不同,MySQL中对于数值类型的value转char类型没有限制,达梦中则存在限制,会报“数据转换失败”的错误,可将cast(数值类型的value as char)转换为cast(数值类型的value as varchar)。

  1. TEXT类型数据返回前端解析报错问题

达梦text类型字段查询后默认转换为dm.jdbc.driver.DmdbNClob类型,spring返回前端解析时会报错,可在jdbcUrl中添加clobAsString=true属性解决该问题。

  1. 使用SQL获取对象创建DDL语句
    MySQL语法:
show create table tableName;

达梦语法:

select dbms_metadata.get_ddl('table', 'tableName', 'schemaName') from dual;
  1. Datax 适配达梦 参考文档

Datax可通过RDBMSReader和RDBMSWriter来适配达梦数据库。

  1. SpringBatch无法兼容达梦问题 参考文档

SpringBatch不支持达梦数据库,可以使用与达梦相近的H2数据库来代替。

  1. Sharding-JDBC 4.0.1适配达梦问题
  • 生成Sharding-JDBC数据源时报For input string: “”问题 参考文档

解决方案:jdbcUrl加上compatibleMode=oracle。

  • 当Sharding-JDBC中包含子查询的语句路由到多个结果的时候,会报“Must have one sharding with subquery”问题

解决方案:在配置Mybatis-Plus分页插件的时候设置达梦的数据库类型为MySQL,使用MySQL语法进行分页查询时不会包含子查询。

PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
  • 使用sharding-jdbc跨表分页查询失效问题

解决方案:使用sharding-jdbc提供的spi以便在分页查询的时候可识别国产数据库。

/**
 * Database type of Dm
 */
public final class DmDatabaseType implements BranchDatabaseType {
    @Override
    public DatabaseType getTrunkDatabaseType() {
        return DatabaseTypes.getActualDatabaseType("MySQL");
    }

    @Override
    public String getName() {
        return "Dm";
    }

    @Override
    public Collection<String> getJdbcUrlPrefixAlias() {
        return Collections.emptyList();
    }

    @Override
    public DataSourceMetaData getDataSourceMetaData(final String url, final String username) {
        return new DmDatasourceMetaData(url, username);
    }
}
/**
 * Data source meta data
 */
@Getter
public final class DmDatasourceMetaData implements DataSourceMetaData {
    private static final int DEFAULT_PORT = 5236;

    private final String hostName;

    private final int port;

    private final String catalog;

    private final String schema;

    private final static Pattern PATTERN = Pattern.compile("jdbc:dm://([\\w\\-.]+):?([0-9]*)([\\w\\-]+)?", Pattern.CASE_INSENSITIVE);

    public DmDatasourceMetaData(final String url, final String username) {
        Matcher matcher = PATTERN.matcher(url);
        if (!matcher.find()) {
            throw new UnrecognizedDatabaseURLException(url, PATTERN.pattern());
        }
        hostName = matcher.group(1);
        port = Strings.isNullOrEmpty(matcher.group(2)) ? DEFAULT_PORT : Integer.parseInt(matcher.group(2));
        catalog = null;
        schema = username;
    }
}