_final_compare.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. const fs = require('fs');
  2. // Load data
  3. const dbOverlapCols = JSON.parse(fs.readFileSync('d:/Workspaces/slaj/_db_overlap_cols.json', 'utf8'));
  4. const rohsTables = JSON.parse(fs.readFileSync('d:/Workspaces/slaj/_rohs_tables.json', 'utf8'));
  5. const dbAllNames = JSON.parse(fs.readFileSync('d:/Workspaces/slaj/_slaj_table_names.json', 'utf8'));
  6. // Normalize type for comparison
  7. function normType(t) {
  8. t = t.toUpperCase().trim().replace(/\s+/g, ' ');
  9. // Remove length specifications for comparison
  10. t = t.replace(/\(\d+(?:,\d+)?\)/g, '');
  11. // Map equivalent types
  12. const map = {
  13. 'BPCHAR': 'CHAR',
  14. 'VARCHAR': 'VARCHAR',
  15. 'NUMERIC': 'NUMERIC',
  16. 'DECIMAL': 'NUMERIC',
  17. 'INT4': 'INTEGER',
  18. 'INT': 'INTEGER',
  19. 'INT8': 'BIGINT',
  20. 'BIGINT': 'BIGINT',
  21. 'BOOL': 'BOOLEAN',
  22. 'FLOAT8': 'DOUBLE PRECISION',
  23. 'TIMESTAMPTZ': 'TIMESTAMP',
  24. 'TIMESTAMP': 'TIMESTAMP',
  25. 'DATE': 'DATE',
  26. 'TEXT': 'TEXT',
  27. 'BYTEA': 'BYTEA',
  28. 'FLOAT4': 'REAL',
  29. 'JSONB': 'JSONB',
  30. 'JSON': 'JSON',
  31. 'INT2': 'SMALLINT',
  32. 'SMALLINT': 'SMALLINT',
  33. };
  34. return map[t] || t;
  35. }
  36. // Categorize ROHS tables
  37. const skipPattern = /^(MLOG\$_|MV_|T_VWMS_)|_(BAK\d*$|MW\d*$|MW$|0731$|1125$|BAK25$|0326$)|_20250[47]\d{2}$|BAK2025|_2025011[03]$|_20250110$|_20250113$/;
  38. const rohsSkip = Object.keys(rohsTables).filter(t => skipPattern.test(t));
  39. const rohsReal = Object.keys(rohsTables).filter(t => !skipPattern.test(t));
  40. const rohsRealTables = {};
  41. rohsReal.forEach(t => { rohsRealTables[t] = rohsTables[t]; });
  42. // Map DB table names (lowercase) to ROHS names (uppercase)
  43. const dbLowerMap = {};
  44. dbAllNames.forEach(t => { dbLowerMap[t.toLowerCase()] = t; });
  45. // Classify all ROHS real tables
  46. const overlap = []; // Tables in both
  47. const rohsOnly = []; // ROHS tables not in DB
  48. const rohsBak = []; // ROHS backup/MV tables
  49. for (const t of Object.keys(rohsRealTables)) {
  50. const dbName = dbLowerMap[t.toLowerCase()];
  51. if (dbName) {
  52. overlap.push({rohs: t, db: dbName});
  53. } else {
  54. rohsOnly.push(t);
  55. }
  56. }
  57. for (const t of rohsSkip) {
  58. const dbName = dbLowerMap[t.toLowerCase()];
  59. rohsBak.push({rohs: t, inDb: !!dbName});
  60. }
  61. // Analyze each overlap table
  62. const fullMatch = [];
  63. const realDiff = [];
  64. const trivialTypeDiff = [];
  65. for (const o of overlap) {
  66. const rohsCols = rohsRealTables[o.rohs] || [];
  67. const dbCols = dbOverlapCols[o.db] || [];
  68. const rohsColMap = {};
  69. rohsCols.forEach(c => { rohsColMap[c.name] = c.type; });
  70. const dbColMap = {};
  71. dbCols.forEach(c => { dbColMap[c.name] = c.type; });
  72. const rohsNames = Object.keys(rohsColMap);
  73. const dbNames = Object.keys(dbColMap);
  74. const rohsOnlyCols = rohsNames.filter(n => !dbNames.includes(n));
  75. const dbOnlyCols = dbNames.filter(n => !rohsNames.includes(n));
  76. const common = rohsNames.filter(n => dbNames.includes(n));
  77. // Check real type differences (after normalization)
  78. const realTypeDiffs = [];
  79. const trivialDiffs = [];
  80. for (const cn of common) {
  81. const rt = normType(rohsColMap[cn]);
  82. const dt = normType(dbColMap[cn]);
  83. if (rt !== dt) {
  84. realTypeDiffs.push({col: cn, rohs: rohsColMap[cn], db: dbColMap[cn], rohsNorm: rt, dbNorm: dt});
  85. } else if (rohsColMap[cn] !== dbColMap[cn]) {
  86. trivialDiffs.push({col: cn, rohs: rohsColMap[cn], db: dbColMap[cn]});
  87. }
  88. }
  89. if (rohsOnlyCols.length === 0 && dbOnlyCols.length === 0 && realTypeDiffs.length === 0) {
  90. fullMatch.push({...o, trivialDiffs: trivialDiffs.length});
  91. } else {
  92. realDiff.push({
  93. ...o,
  94. rohsOnlyCols,
  95. dbOnlyCols,
  96. realTypeDiffs,
  97. trivialDiffs: trivialDiffs.length,
  98. });
  99. }
  100. }
  101. // ======== REPORT ========
  102. console.log('========================================');
  103. console.log(' 六项机制(ROHS) 与 安监(slaj) 数据库对比分析报告');
  104. console.log('========================================\n');
  105. console.log('【总体概况】');
  106. console.log(` ROHS SQL文件中表总数: ${Object.keys(rohsTables).length}`);
  107. console.log(` ROHS 真实业务表: ${Object.keys(rohsRealTables).length}`);
  108. console.log(` ROHS 备份/物化视图/日志表: ${rohsSkip.length}`);
  109. console.log(` 安监 slaj schema 表总数: ${dbAllNames.length}`);
  110. console.log(` 重叠表 (两库都有): ${overlap.length}`);
  111. console.log(` ROHS独有表 (安监中没有): ${rohsOnly.length}`);
  112. console.log(` 安监独有表 (ROHS中没有): ${dbAllNames.length - overlap.length}`);
  113. console.log(`\n【重叠表分析】(共 ${overlap.length} 张)`);
  114. console.log(` 字段完全一致: ${fullMatch.length}`);
  115. console.log(` 有结构性差异: ${realDiff.length}`);
  116. if (fullMatch.length > 0) {
  117. console.log(`\n --- 完全一致的表 (仅类型名写法不同) ---`);
  118. for (const m of fullMatch) {
  119. console.log(` ✓ ${m.rohs} = ${m.db} (${rohsRealTables[m.rohs].length} cols, ${m.trivialDiffs > 0 ? m.trivialDiffs + ' trivial type diffs' : 'exact match'})`);
  120. }
  121. }
  122. if (realDiff.length > 0) {
  123. console.log(`\n --- 有真实差异的表 ---`);
  124. for (const m of realDiff) {
  125. const issues = [];
  126. if (m.rohsOnlyCols.length) issues.push(`ROHS独有字段: [${m.rohsOnlyCols.join(', ')}]`);
  127. if (m.dbOnlyCols.length) issues.push(`DB独有字段: [${m.dbOnlyCols.join(', ')}]`);
  128. for (const td of m.realTypeDiffs) {
  129. issues.push(`${td.col}: ROHS=${td.rohs} vs DB=${td.db}`);
  130. }
  131. const sev = (m.rohsOnlyCols.length + m.dbOnlyCols.length + m.realTypeDiffs.length);
  132. const icon = m.dbOnlyCols.length + m.rohsOnlyCols.length === 0 && m.realTypeDiffs.every(d => d.col === 'GUID' && d.rohs === 'CHAR(32)' && d.db === 'BPCHAR') ? '~' : '⚠';
  133. console.log(` ${icon} ${m.rohs} (${rohsRealTables[m.rohs].length}c) <-> ${m.db} (${Object.keys(dbOverlapCols[m.db]||{}).length}c): ${issues.join('; ')}`);
  134. }
  135. }
  136. console.log(`\n【ROHS独有表 - 安监中不存在的新表】(共 ${rohsOnly.length} 张)`);
  137. // Categorize ROHS-only tables
  138. const categoryMap = {
  139. 'SYS_': '系统管理类',
  140. 'T_BUSI_': '业务类',
  141. 'T_DICT_': '字典类',
  142. 'T_CON_': '配置类',
  143. 'T_SSO_': 'SSO单点登录类',
  144. 'T_VWMS_': 'VWMS类',
  145. 'ATT_': '附件/属性类',
  146. 'BIS_': '业务信息类',
  147. 'OBJ_': '对象类',
  148. 'REL_': '关系类',
  149. 'BDMS_': 'BDMS类',
  150. 'HARD_': '硬件类',
  151. 'HAZARD_': '危险源类',
  152. 'HIDD_': '隐患类',
  153. 'PPRTY_': '产权类',
  154. };
  155. const cats = {};
  156. for (const t of rohsOnly) {
  157. let cat = '其他';
  158. for (const [prefix, label] of Object.entries(categoryMap)) {
  159. if (t.startsWith(prefix)) { cat = label; break; }
  160. }
  161. if (!cats[cat]) cats[cat] = [];
  162. cats[cat].push(t);
  163. }
  164. for (const [cat, tables] of Object.entries(cats).sort()) {
  165. console.log(`\n [${cat}] (${tables.length}张):`);
  166. tables.sort().forEach(t => {
  167. const cols = rohsRealTables[t] || [];
  168. console.log(` ${t} (${cols.length} cols)`);
  169. });
  170. }
  171. console.log(`\n【ROHS备份/日志/MV表】(共 ${rohsSkip.length} 张)`);
  172. for (const t of rohsBak) {
  173. console.log(` ${t.rohs} (${(rohsTables[t.rohs]||[]).length} cols) ${t.inDb ? '← 安监中也存在' : '(安监中不存在)'}`);
  174. }
  175. console.log(`\n========================================`);
  176. console.log('【合并可行性分析】');
  177. console.log('========================================');
  178. // Count by severity
  179. const critDiffs = realDiff.filter(m => m.rohsOnlyCols.length > 0 || m.dbOnlyCols.length > 0 || m.realTypeDiffs.some(d => d.rohsNorm !== d.dbNorm));
  180. const minorDiffs = realDiff.filter(m => !critDiffs.includes(m));
  181. console.log(`
  182. 1. 重叠表 (${overlap.length}张):
  183. - 完全一致 (仅CHAR/BPCHAR, VARCHAR长度等PG写法差异): ${fullMatch.length}张
  184. - 有轻微信赖差异 (类型兼容): ${realDiff.length - critDiffs.length}张
  185. - 有真实差异需处理: ${critDiffs.length}张
  186. 2. 新表 (ROHS独有, ${rohsOnly.length}张):
  187. - 这些表可以直接在安监数据库中创建
  188. - 涉及:${Object.keys(cats).join('、')}
  189. 3. 合并建议:
  190. - 重叠表:由于重叠表字段基本一致(差异主要是PG类型名写法不同和STR_TO_DATE<->TO_DATE),
  191. 这些表结构上可以直接兼容,不需要修改
  192. - ROHS独有的${rohsOnly.length}张表需要在安监数据库中新建
  193. - 注意:ROHS表的schema是 SLAJ_ROHS,安监的schema是 slaj,
  194. 合并时需要统一schema或者处理好跨schema访问
  195. 4. 风险点:`);
  196. // Find critical diffs
  197. for (const m of realDiff) {
  198. const issues = [];
  199. if (m.rohsOnlyCols.length) issues.push(`ROHS多出的列: ${m.rohsOnlyCols.join(', ')}`);
  200. if (m.dbOnlyCols.length) issues.push(`DB多出的列: ${m.dbOnlyCols.join(', ')}`);
  201. for (const td of m.realTypeDiffs) {
  202. if (td.rohsNorm !== td.dbNorm) {
  203. issues.push(`类型不兼容: ${td.col} ROHS=${td.rohs}(${td.rohsNorm}) vs DB=${td.db}(${td.dbNorm})`);
  204. }
  205. }
  206. if (issues.length) {
  207. console.log(` - ${m.rohs}: ${issues.join('; ')}`);
  208. }
  209. }
  210. console.log(`\n报告完毕。`);