起因
最近工作上有个需要,由于在系统迭代过程中,有一些情况修改了数据表索引,但是没有更新到客户系统中等情况,因此要在代码里检查多个客户的数据库的索引是否和模版的一致。这其中遇到了一点问题,记录一下。
创建索引的语句如: alter table table_name add index index_name(column_name(length))
通用方式获取索引信息
按照通用方式获取索引,使用 Connnection
获取到 DatabaseMetaData
, 然后通过java.util.sql.DatabaseMetaData.getIndexInfo
获取到结果集ResultSet
。
但是有一个需要的信息——创建索引时的 length 却获取不到
在客户端使用 show index from table_name
里是有这个信息的,因此我觉得是可以获取的。如下:
初步尝试
- 尝试获取了一下SUB_PART字段,出现字段不存在的错误。
- 想到,是不是这个字段在这里不叫这个名称,于是又把
ResultSet
的元数据ResultSetMetaData
取出来,通过把结果集所有列名和值都输出来,如下,发现还是没有自己想要的信息。
public static void checkIndex() throws SQLException {
DatabaseMetaData metaData = getDatabaseMetaDataByDriver();
ResultSet rs = metaData.getIndexInfo(null, null, "jt_cs_follow_0_copy", false, false);
ResultSetMetaData resultSetMetaData = (ResultSetMetaData) rs.getMetaData();
int length = resultSetMetaData.getColumnCount();
while (rs.next()) {
String indexName = rs.getString("INDEX_NAME");
if (!indexName.trim().equalsIgnoreCase("primary")) {
for (int column = 1; column <= length; column++) {
String columnLabelName = resultSetMetaData.getColumnLabel(column);
Object columnValue = rs.getObject(columnLabelName);
System.out.println(columnLabelName+":"+columnValue);
}
}
}
}
// 输出结果:
// TABLE_CAT:test_jtsoft_csfollows
// TABLE_SCHEM:null
// TABLE_NAME:jt_cs_follow_0_copy
// NON_UNIQUE:true
// INDEX_QUALIFIER:
// INDEX_NAME:index_entity_id_
// TYPE:3
// ORDINAL_POSITION:1
// COLUMN_NAME:entity_id_
// ASC_OR_DESC:A
// CARDINALITY:0
// PAGES:0
// FILTER_CONDITION:null
- 这就有点奇怪了,难道真没有这个信息?
- 这时我想到,JDBC是一个比较标准化的JAVA数据库连接,这就说明JDBC只能取一些比较共同的信息。那是不是设置索引的length是不是只有MYSQL才有的性质?
- 确认之后发现常规
DatabaseMetaData.getIndexInfo
方式没有办法直接获取到这个值之后。
二次尝试
- 这时我就在考虑,既然没有办法直接获取,那么能不能直接执行
show index from table_name
来得到结果。 - 想到这个,就想看下 MYSQL 里面是怎么获取 索引 信息以及为什么没有 length
- 于是打开
com.mysql.jdbc.DatabaseMetaData.getIndexInfo
,发现也是通过 执行show index from table_name
来获取结果,只是没有把我们需要那个字段作为结果集返回来。因此我们只需要我们自己执行这个语句就可以获得Sub_part。
额外收获
在找 com.mysql.jdbc.DatabaseMetaData
发现同包下还有一个实现了java.util.sql.DatabaseMetaData
类,即com.mysql.jdbc.DatabaseMetaDataUsingInfoSchema
于是发现其getIndexInfo
的实现是通过如下SQL:
SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM,TABLE_NAME,NON_UNIQUE,TABLE_SCHEMA AS INDEX_QUALIFIER,INDEX_NAME,3 AS TYPE,SEQ_IN_INDEX AS ORDINAL_POSITION,COLUMN_NAME,COLLATION AS ASC_OR_DESC,CARDINALITY,NULL AS PAGES,NULL AS FILTER_CONDITION FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ?
也是没有把Sub_part拿出来的,但是从这个类,可以发现,show index from table_name
的信息就是从 INFORMATION_SCHEMA
库里拿的,因此我们也可以通过自己写代码select这个表来取得 Sub_part信息。
对比两个元数据类 com.mysql.jdbc.DatabaseMetaData
和com.mysql.jdbc.DatabaseMetaDataUsingInfoSchema
发现一个特点, com.mysql.jdbc.DatabaseMetaData
里面的实现大部分用的是show
的方式。
而com.mysql.jdbc.DatabaseMetaDataUsingInfoSchema
则是使用select
的方式。
由此对 show xxxx
和 information_schema
的关系也更加深刻了一点,也从里面看到了一些MYSQL命令和一些不一样的select用法。
如 show tables
获取全部表和如下的CASE WHEN xxxx THEN XXX WHEN xxxx2 ELSE XXXXX END
可以把select的值转换。
SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, CASE WHEN TABLE_TYPE='BASE TABLE' THEN 'TABLE' WHEN TABLE_TYPE='TEMPORARY' THEN 'LOCAL_TEMPORARY' ELSE TABLE_TYPE END AS TABLE_TYPE, TABLE_COMMENT AS REMARKS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND TABLE_TYPE IN (?,?,?) ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME
思考
虽然之前知道information_schame
里查的都是MYSQL的元信息,也用过 show full processlist
和 select * from information_schema.innodb_trx
在客户端对比查询MYSQL的线程和事务线程。
但却从没有想过用这个去查元信息,典型的 知其然不知其所以然,所以导致不会灵活运用,这个也对自己学知识一个启发。