diff --git a/MySQL/my_query_table_date.go b/MySQL/my_query_table_date.go index 241ce94a9d2868e4d1dd71197e79e158794b0a5b..49b16e569ad243cbef1bf132b081a8a7323b5845 100644 --- a/MySQL/my_query_table_date.go +++ b/MySQL/my_query_table_date.go @@ -19,7 +19,7 @@ func (my *QueryTable) QueryTableIndexColumnInfo(db *sql.DB, logThreadSeq int64) tableData []map[string]interface{} err error ) - strsql = fmt.Sprintf("select isc.COLUMN_NAME as columnName,isc.COLUMN_TYPE as columnType,isc.COLUMN_KEY as columnKey,isc.EXTRA as autoIncrement,iss.NON_UNIQUE as nonUnique,iss.INDEX_NAME as indexName,iss.SEQ_IN_INDEX IndexSeq,isc.ORDINAL_POSITION columnSeq from information_schema.columns isc inner join (select NON_UNIQUE,INDEX_NAME,SEQ_IN_INDEX,COLUMN_NAME from information_schema.STATISTICS where table_schema='%s' and table_name='%s') as iss on isc.column_name =iss.column_name where isc.table_schema='%s' and isc.table_name='%s';", my.Schema, my.Table, my.Schema, my.Table) + strsql = fmt.Sprintf("select isc.COLUMN_NAME as columnName,isc.COLUMN_TYPE as columnType,isc.COLUMN_KEY as columnKey,isc.EXTRA as autoIncrement,iss.NON_UNIQUE as nonUnique,iss.INDEX_NAME as indexName,iss.SEQ_IN_INDEX as IndexSeq,isc.ORDINAL_POSITION as columnSeq from information_schema.columns isc inner join (select NON_UNIQUE,INDEX_NAME,SEQ_IN_INDEX,COLUMN_NAME from information_schema.STATISTICS where table_schema='%s' and table_name='%s') as iss on isc.column_name =iss.column_name where isc.table_schema='%s' and isc.table_name='%s';", my.Schema, my.Table, my.Schema, my.Table) vlog = fmt.Sprintf("(%d) [%s] Generate a sql statement to query the index statistics of table %s.%s under the %s database.sql messige is {%s}", logThreadSeq, Event, my.Schema, my.Table, DBType, strsql) global.Wlog.Debug(vlog) dispos := dataDispos.DBdataDispos{DBType: DBType, LogThreadSeq: logThreadSeq, Event: Event, DB: db} @@ -41,50 +41,84 @@ func (my *QueryTable) QueryTableIndexColumnInfo(db *sql.DB, logThreadSeq int64) */ func (my *QueryTable) IndexDisposF(queryData []map[string]interface{}, logThreadSeq int64) (map[string][]string, map[string][]string, map[string][]string) { var ( - nultiseriateIndexColumnMap = make(map[string][]string) - multiseriateIndexColumnMap = make(map[string][]string) - priIndexColumnMap = make(map[string][]string) - PriIndexCol, uniIndexCol, mulIndexCol []string - indexName string - currIndexName string - Event = "E_Index_Filter" + nultiseriateIndexColumnMap = make(map[string][]string) + multiseriateIndexColumnMap = make(map[string][]string) + priIndexColumnMap = make(map[string][]string) + indexName string + currIndexName string + Event = "E_Index_Filter" ) vlog = fmt.Sprintf("(%d) [%s] Start to filter the primary key index, unique index, and common index based on the index information of the specified table %s.%s under the %s library", logThreadSeq, Event, my.Schema, my.Table, DBType) global.Wlog.Debug(vlog) + + // 用于临时存储每个索引的列顺序 + indexColumns := make(map[string]map[string]string) + for _, v := range queryData { currIndexName = fmt.Sprintf("%s", v["indexName"]) if my.LowerCaseTableNames == "no" { currIndexName = strings.ToUpper(fmt.Sprintf("%s", v["indexName"])) } - //判断唯一索引(包含主键索引和普通索引) - if v["nonUnique"].(string) == "0" { - if currIndexName == "PRIMARY" { - if currIndexName != indexName { - indexName = currIndexName - } - PriIndexCol = append(PriIndexCol, fmt.Sprintf("%s", v["columnName"])) - priIndexColumnMap["pri"] = PriIndexCol - } else { - if currIndexName != indexName { - indexName = currIndexName - nultiseriateIndexColumnMap[indexName] = append(uniIndexCol, fmt.Sprintf("%s /*actions Column Type*/ %s", v["columnName"], v["columnType"])) - } else { - nultiseriateIndexColumnMap[indexName] = append(nultiseriateIndexColumnMap[indexName], fmt.Sprintf("%s /*actions Column Type*/ %s", v["columnName"], v["columnType"])) + + columnName := fmt.Sprintf("%s", v["columnName"]) + indexSeq := fmt.Sprintf("%s", v["IndexSeq"]) + columnType := fmt.Sprintf("%s", v["columnType"]) + + // 初始化map + if _, exists := indexColumns[currIndexName]; !exists { + indexColumns[currIndexName] = make(map[string]string) + } + + // 存储列的顺序信息 + indexColumns[currIndexName][indexSeq] = columnName + "/*seq*/" + indexSeq + "/*type*/" + columnType + + // 更新当前索引名 + if currIndexName != indexName { + indexName = currIndexName + } + } + + // 按照索引序号排序并添加到最终的map中 + for idxName, columns := range indexColumns { + // 获取所有序号并排序 + var seqNums []int + for seq := range columns { + seqNum, _ := strconv.Atoi(seq) + seqNums = append(seqNums, seqNum) + } + sort.Ints(seqNums) + + // 按序号顺序添加列 + var orderedColumns []string + for _, seq := range seqNums { + seqStr := strconv.Itoa(seq) + orderedColumns = append(orderedColumns, columns[seqStr]) + } + + // 根据索引类型添加到相应的map中 + if idxName == "PRIMARY" { + priIndexColumnMap["pri"] = orderedColumns + } else { + // 检查第一个匹配的索引列来确定是否为唯一索引 + isUnique := false + for _, v := range queryData { + if fmt.Sprintf("%s", v["indexName"]) == idxName { + isUnique = v["nonUnique"].(string) == "0" + break } } - } - //处理普通索引 - if v["nonUnique"].(string) != "0" { - if currIndexName != indexName { - indexName = currIndexName - multiseriateIndexColumnMap[indexName] = append(mulIndexCol, fmt.Sprintf("%s /*actions Column Type*/ %s", v["columnName"], v["columnType"])) + + if isUnique { + nultiseriateIndexColumnMap[idxName] = orderedColumns } else { - multiseriateIndexColumnMap[indexName] = append(multiseriateIndexColumnMap[indexName], fmt.Sprintf("%s /*actions Column Type*/ %s", v["columnName"], v["columnType"])) + multiseriateIndexColumnMap[idxName] = orderedColumns } } } + vlog = fmt.Sprintf("(%d) [%s] The index information screening of the specified table %s.%s under the %s library is completed", logThreadSeq, Event, my.Schema, my.Table, DBType) global.Wlog.Debug(vlog) + return priIndexColumnMap, nultiseriateIndexColumnMap, multiseriateIndexColumnMap } diff --git a/Oracle/or_query_table_date.go b/Oracle/or_query_table_date.go index fbf5f7c7d4b516093b4423ca68f8b3d15a1e9a77..4388db1f98db4f081c72cf071dccc69d9fa2fda5 100644 --- a/Oracle/or_query_table_date.go +++ b/Oracle/or_query_table_date.go @@ -5,6 +5,7 @@ import ( "fmt" "gt-checksum/dataDispos" "gt-checksum/global" + "sort" "strconv" "strings" ) @@ -41,50 +42,84 @@ func (or *QueryTable) QueryTableIndexColumnInfo(db *sql.DB, logThreadSeq int64) */ func (or *QueryTable) IndexDisposF(queryData []map[string]interface{}, logThreadSeq int64) (map[string][]string, map[string][]string, map[string][]string) { var ( - nultiseriateIndexColumnMap = make(map[string][]string) - multiseriateIndexColumnMap = make(map[string][]string) - priIndexColumnMap = make(map[string][]string) - PriIndexCol, uniIndexCol, mulIndexCol []string - indexName string - currIndexName string - Event = "E_Index_Filter" + nultiseriateIndexColumnMap = make(map[string][]string) + multiseriateIndexColumnMap = make(map[string][]string) + priIndexColumnMap = make(map[string][]string) + indexName string + currIndexName string + Event = "E_Index_Filter" ) vlog = fmt.Sprintf("(%d) [%s] Start to filter the primary key index, unique index, and common index based on the index information of the specified table %s.%s under the %s library", logThreadSeq, Event, or.Schema, or.Table, DBType) global.Wlog.Debug(vlog) + + // 用于临时存储每个索引的列顺序 + indexColumns := make(map[string]map[string]string) + for _, v := range queryData { currIndexName = fmt.Sprintf("%s", v["indexName"]) if or.LowerCaseTableNames == "no" { currIndexName = strings.ToUpper(fmt.Sprintf("%s", v["indexName"])) } - //判断唯一索引(包含主键索引和普通索引) - if v["nonUnique"].(string) == "UNIQUE" { - if v["columnKey"].(string) == "1" { - if currIndexName != indexName { - indexName = currIndexName - } - PriIndexCol = append(PriIndexCol, fmt.Sprintf("%s", v["columnName"])) - priIndexColumnMap["pri"] = PriIndexCol - } else { - if currIndexName != indexName { - indexName = currIndexName - nultiseriateIndexColumnMap[indexName] = append(uniIndexCol, fmt.Sprintf("%s /*actions Column Type*/ %s", v["columnName"], v["columnType"])) - } else { - nultiseriateIndexColumnMap[indexName] = append(nultiseriateIndexColumnMap[indexName], fmt.Sprintf("%s /*actions Column Type*/ %s", v["columnName"], v["columnType"])) + + columnName := fmt.Sprintf("%s", v["columnName"]) + indexSeq := fmt.Sprintf("%s", v["IndexSeq"]) + columnType := fmt.Sprintf("%s", v["columnType"]) + + // 初始化map + if _, exists := indexColumns[currIndexName]; !exists { + indexColumns[currIndexName] = make(map[string]string) + } + + // 存储列的顺序信息 + indexColumns[currIndexName][indexSeq] = columnName + "/*seq*/" + indexSeq + "/*type*/" + columnType + + // 更新当前索引名 + if currIndexName != indexName { + indexName = currIndexName + } + } + + // 按照索引序号排序并添加到最终的map中 + for idxName, columns := range indexColumns { + // 获取所有序号并排序 + var seqNums []int + for seq := range columns { + seqNum, _ := strconv.Atoi(seq) + seqNums = append(seqNums, seqNum) + } + sort.Ints(seqNums) + + // 按序号顺序添加列 + var orderedColumns []string + for _, seq := range seqNums { + seqStr := strconv.Itoa(seq) + orderedColumns = append(orderedColumns, columns[seqStr]) + } + + // 根据索引类型添加到相应的map中 + if idxName == "PRIMARY" { + priIndexColumnMap["pri"] = orderedColumns + } else { + // 检查第一个匹配的索引列来确定是否为唯一索引 + isUnique := false + for _, v := range queryData { + if fmt.Sprintf("%s", v["indexName"]) == idxName { + isUnique = v["nonUnique"].(string) == "0" + break } } - } - //处理普通索引 - if v["nonUnique"].(string) == "NONUNIQUE" { - if currIndexName != indexName { - indexName = currIndexName - multiseriateIndexColumnMap[indexName] = append(mulIndexCol, fmt.Sprintf("%s /*actions Column Type*/ %s", v["columnName"], v["columnType"])) + + if isUnique { + nultiseriateIndexColumnMap[idxName] = orderedColumns } else { - multiseriateIndexColumnMap[indexName] = append(multiseriateIndexColumnMap[indexName], fmt.Sprintf("%s /*actions Column Type*/ %s", v["columnName"], v["columnType"])) + multiseriateIndexColumnMap[idxName] = orderedColumns } } } + vlog = fmt.Sprintf("(%d) [%s] The index information screening of the specified table %s.%s under the %s library is completed", logThreadSeq, Event, or.Schema, or.Table, DBType) global.Wlog.Debug(vlog) + return priIndexColumnMap, nultiseriateIndexColumnMap, multiseriateIndexColumnMap } diff --git a/actions/schema_tab_struct.go b/actions/schema_tab_struct.go index 74684ae176ee94a2dfd439f471f1e929c649a449..2661f890b3c193c654a5999619b18a4b7766f528 100644 --- a/actions/schema_tab_struct.go +++ b/actions/schema_tab_struct.go @@ -8,6 +8,8 @@ import ( "gt-checksum/global" "gt-checksum/inputArg" "os" + "sort" + "strconv" "strings" ) @@ -1063,22 +1065,109 @@ func (stcls *schemaTable) Index(dtabS []string, logThreadSeq, logThreadSeq2 int6 sqlS []string aa = &CheckSumTypeStruct{} event string + // 辅助函数:提取列名和序号 + extractColumnInfo = func(columnStr string) (string, int) { + // 从格式 "columnName/*seq*/1/*type*/columnType" 中提取信息 + parts := strings.Split(columnStr, "/*seq*/") + colName := strings.TrimSpace(parts[0]) + seqStr := strings.Split(parts[1], "/*type*/")[0] + seq, _ := strconv.Atoi(seqStr) + return colName, seq + } + + // 辅助函数:按序号排序列并返回纯列名 + sortColumns = func(columns []string) []string { + type ColumnInfo struct { + name string + seq int + } + var columnInfos []ColumnInfo + + // 提取列信息 + for _, col := range columns { + name, seq := extractColumnInfo(col) + columnInfos = append(columnInfos, ColumnInfo{name: name, seq: seq}) + } + + // 按序号排序 + sort.Slice(columnInfos, func(i, j int) bool { + return columnInfos[i].seq < columnInfos[j].seq + }) + + // 返回排序后的纯列名 + var result []string + for _, col := range columnInfos { + result = append(result, fmt.Sprintf("%s", col.name)) + } + return result + } + indexGenerate = func(smu, dmu map[string][]string, a *CheckSumTypeStruct, indexType string) []string { var cc, c, d []string - for k, _ := range smu { + dbf := dbExec.DataAbnormalFixStruct{ + Schema: stcls.schema, + Table: stcls.table, + SourceDevice: stcls.sourceDrive, + DestDevice: stcls.destDrive, + IndexType: indexType, + DatafixType: stcls.datefix, + } + + // 首先比较索引名称 + for k := range smu { c = append(c, k) } - for k, _ := range dmu { + for k := range dmu { d = append(d, k) } + + // 如果索引名称不同,生成修复SQL if a.CheckMd5(strings.Join(c, ",")) != a.CheckMd5(strings.Join(d, ",")) { e, f := a.Arrcmp(c, d) - dbf := dbExec.DataAbnormalFixStruct{Schema: stcls.schema, Table: stcls.table, SourceDevice: stcls.sourceDrive, DestDevice: stcls.destDrive, IndexType: indexType, DatafixType: stcls.datefix} - cc = dbf.DataAbnormalFix().FixAlterIndexSqlExec(e, f, smu, stcls.sourceDrive, logThreadSeq) + // 对于新增的索引,需要处理列顺序 + newIndexMap := make(map[string][]string) + for _, idx := range e { + if cols, ok := smu[idx]; ok { + // 对列进行排序并去除序号信息 + newIndexMap[idx] = sortColumns(cols) + } + } + cc = dbf.DataAbnormalFix().FixAlterIndexSqlExec(e, f, newIndexMap, stcls.sourceDrive, logThreadSeq) + } else { + // 即使索引名称相同,也要比较索引的具体内容 + for k, sColumns := range smu { + if dColumns, exists := dmu[k]; exists { + // 比较同名索引的列及其顺序(包含序号信息的比较) + if a.CheckMd5(strings.Join(sColumns, ",")) != a.CheckMd5(strings.Join(dColumns, ",")) { + // 1. 先生成删除旧索引的SQL + if indexType == "pri" { + cc = append(cc, fmt.Sprintf("ALTER TABLE `%s`.`%s` DROP PRIMARY KEY;", stcls.schema, stcls.table)) + } else { + cc = append(cc, fmt.Sprintf("ALTER TABLE `%s`.`%s` DROP INDEX `%s`;", stcls.schema, stcls.table, k)) + } + + // 2. 获取排序后的纯列名 + sortedColumns := sortColumns(sColumns) + + // 3. 生成创建索引的SQL + if indexType == "pri" { + cc = append(cc, fmt.Sprintf("ALTER TABLE `%s`.`%s` ADD PRIMARY KEY(%s);", + stcls.schema, stcls.table, strings.Join(sortedColumns, ","))) + } else if indexType == "uni" { + cc = append(cc, fmt.Sprintf("ALTER TABLE `%s`.`%s` ADD UNIQUE INDEX `%s`(%s);", + stcls.schema, stcls.table, k, strings.Join(sortedColumns, ","))) + } else { + cc = append(cc, fmt.Sprintf("ALTER TABLE `%s`.`%s` ADD INDEX `%s`(%s);", + stcls.schema, stcls.table, k, strings.Join(sortedColumns, ","))) + } + } + } + } } return cc } ) + fmt.Println("-- gt-checksum checksum table index info -- ") event = fmt.Sprintf("[%s]", "check_table_index") //校验索引 @@ -1097,7 +1186,15 @@ func (stcls *schemaTable) Index(dtabS []string, logThreadSeq, logThreadSeq2 int6 return err } spri, suni, smul := idxc.TableIndexColumn().IndexDisposF(squeryData, logThreadSeq2) - vlog = fmt.Sprintf("(%d) %s The index column data of the source %s database table %s is {primary:%v,unique key:%v,index key:%v}", logThreadSeq, event, stcls.sourceDrive, i, spri, suni, smul) + vlog = fmt.Sprintf("(%d) %s The index column data of the source %s database table %s.%s is {primary:%v,unique key:%v,index key:%v}", + logThreadSeq, + event, + stcls.sourceDrive, + stcls.schema, + stcls.table, + spri, + suni, + smul) global.Wlog.Debug(vlog) idxc.Drivce = stcls.destDrive @@ -1110,12 +1207,21 @@ func (stcls *schemaTable) Index(dtabS []string, logThreadSeq, logThreadSeq2 int6 return err } dpri, duni, dmul := idxc.TableIndexColumn().IndexDisposF(dqueryData, logThreadSeq2) - vlog = fmt.Sprintf("(%d) %s The index column data of the source %s database table %s is {primary:%v,unique key:%v,index key:%v}", logThreadSeq, event, stcls.destDrive, i, dpri, duni, dmul) + vlog = fmt.Sprintf("(%d) %s The index column data of the dest %s database table %s.%s is {primary:%v,unique key:%v,index key:%v}", + logThreadSeq, + event, + stcls.destDrive, + stcls.schema, + stcls.table, + dpri, + duni, + dmul) global.Wlog.Debug(vlog) var pods = Pod{ Datafix: stcls.datefix, CheckObject: "Index", + Differences: "no", Schema: stcls.schema, Table: stcls.table,