起因

最近工作上有个需要,由于在系统迭代过程中,有一些情况修改了数据表索引,但是没有更新到客户系统中等情况,因此要在代码里检查多个客户的数据库的索引是否和模版的一致。这其中遇到了一点问题,记录一下。
创建索引的语句如: 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里是有这个信息的,因此我觉得是可以获取的。如下:

Java 出现的第一次索引 java获取索引字段_Java 出现的第一次索引

初步尝试

  • 尝试获取了一下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信息。

Java 出现的第一次索引 java获取索引字段_java获取索引_02

对比两个元数据类 com.mysql.jdbc.DatabaseMetaDatacom.mysql.jdbc.DatabaseMetaDataUsingInfoSchema 发现一个特点, com.mysql.jdbc.DatabaseMetaData里面的实现大部分用的是show的方式。
com.mysql.jdbc.DatabaseMetaDataUsingInfoSchema则是使用select的方式。
由此对 show xxxxinformation_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 processlistselect * from information_schema.innodb_trx 在客户端对比查询MYSQL的线程和事务线程。
但却从没有想过用这个去查元信息,典型的 知其然不知其所以然,所以导致不会灵活运用,这个也对自己学知识一个启发。