// 一次性探针:检查 rond 备份里是否存在「跨备份稳定」的对齐键。 // 用完即可删。 package main import ( "database/sql" "fmt" "os" "strings" "rond-with-you/internal/rond" ) func main() { if len(os.Args) < 3 { fmt.Println("用法: idprobe ") os.Exit(2) } a := load(os.Args[1]) b := load(os.Args[2]) fmt.Printf("\n=== A=%s ===\n=== B=%s ===\n", os.Args[1], os.Args[2]) fmt.Printf("\n---------- ZVISIT.ZIDENTIFIER_ 稳定性 ----------\n") fmt.Printf("A 有 UUID %d / %d,B 有 UUID %d / %d\n", len(a.visitUUID), len(a.visitPK), len(b.visitUUID), len(b.visitPK)) inter := 0 for u := range a.visitUUID { if _, ok := b.visitUUID[u]; ok { inter++ } } fmt.Printf("两次备份 UUID 交集: %d(A 独有 %d,B 独有 %d)\n", inter, len(a.visitUUID)-inter, len(b.visitUUID)-inter) // UUID 相同则对比 pk 是否相同 samePK, diffPK := 0, 0 for u, pa := range a.visitUUID { if pb, ok := b.visitUUID[u]; ok { if pa == pb { samePK++ } else { diffPK++ } } } fmt.Printf("UUID 相同者中:Z_PK 也相同 %d,Z_PK 已变 %d\n", samePK, diffPK) fmt.Printf("\n---------- ZACTIVITY.ZUID_ ----------\n") fmt.Printf("A UUID %d / %d,B UUID %d / %d\n", len(a.actUUID), len(a.actPK), len(b.actUUID), len(b.actPK)) same, dif := 0, 0 for u, pa := range a.actUUID { if pb, ok := b.actUUID[u]; ok { if pa == pb { same++ } else { dif++ } } } fmt.Printf("交集配对:Z_PK 相同 %d,Z_PK 已变 %d\n", same, dif) fmt.Printf("\n---------- ZLOCATION 可用稳定列 ----------\n") fmt.Printf("A 列: %s\n", strings.Join(a.locCols, ", ")) fmt.Printf("A 地点 %d,B 地点 %d\n", len(a.locPK), len(b.locPK)) // 名称+粗坐标是否可作键 type lk struct { name string lat float64 lon float64 } keyA := map[string][]int{} for pk, l := range a.locPK { k := fmt.Sprintf("%s|%.5f|%.5f", l.name, l.lat, l.lon) keyA[k] = append(keyA[k], pk) } hit, miss := 0, 0 for pk, l := range b.locPK { k := fmt.Sprintf("%s|%.5f|%.5f", l.name, l.lat, l.lon) if _, ok := keyA[k]; ok { hit++ } else { miss++ if miss <= 5 { fmt.Printf(" B 独有地点 #%d %q %.6f,%.6f\n", pk, l.name, l.lat, l.lon) } } } fmt.Printf("按「名称+坐标(1e-5)」键:B 命中 A %d,未命中 %d\n", hit, miss) } type loc struct { name string lat, lon float64 } type snap struct { visitUUID map[string]int visitPK map[int]bool actUUID map[string]int actPK map[int]bool locPK map[int]loc locCols []string } func load(path string) *snap { dir, err := os.MkdirTemp("", "idprobe") must(err) defer os.RemoveAll(dir) db, err := rond.Open(path, dir) must(err) defer db.Close() s := &snap{visitUUID: map[string]int{}, visitPK: map[int]bool{}, actUUID: map[string]int{}, actPK: map[int]bool{}, locPK: map[int]loc{}} rows, err := db.Query(`SELECT Z_PK, hex(ZIDENTIFIER_) FROM ZVISIT`) must(err) for rows.Next() { var pk int var u sql.NullString must(rows.Scan(&pk, &u)) s.visitPK[pk] = true if u.Valid && u.String != "" && strings.Trim(u.String, "0") != "" { s.visitUUID[u.String] = pk } } rows.Close() rows, err = db.Query(`SELECT Z_PK, hex(ZUID_), COALESCE(ZNAME_,'') FROM ZACTIVITY`) must(err) for rows.Next() { var pk int var u sql.NullString var n string must(rows.Scan(&pk, &u, &n)) s.actPK[pk] = true if u.Valid && u.String != "" && strings.Trim(u.String, "0") != "" { s.actUUID[u.String] = pk } } rows.Close() rows, err = db.Query(`PRAGMA table_info(ZLOCATION)`) must(err) for rows.Next() { var cid int var name, typ string var notnull int var dflt sql.NullString var pk int must(rows.Scan(&cid, &name, &typ, ¬null, &dflt, &pk)) s.locCols = append(s.locCols, name) } rows.Close() rows, err = db.Query(`SELECT Z_PK, COALESCE(ZNAME_,''), COALESCE(ZLATITUDE,0), COALESCE(ZLONGITUDE,0) FROM ZLOCATION`) must(err) for rows.Next() { var pk int var l loc must(rows.Scan(&pk, &l.name, &l.lat, &l.lon)) s.locPK[pk] = l } rows.Close() return s } func must(err error) { if err != nil { fmt.Fprintln(os.Stderr, "错误:", err) os.Exit(1) } }