Files
jiebanke/backend/scripts/check-table-structure.js

181 lines
5.5 KiB
JavaScript

#!/usr/bin/env node
/**
* 检查数据库表结构脚本
* 对比设计文档与实际表结构
*/
const mysql = require('mysql2/promise');
const config = require('../config/env');
async function checkTableStructure() {
let connection;
try {
console.log('🔍 开始检查数据库表结构...');
const dbConfig = {
host: config.mysql.host,
port: config.mysql.port,
user: config.mysql.user,
password: config.mysql.password,
database: config.mysql.database,
charset: config.mysql.charset || 'utf8mb4',
timezone: config.mysql.timezone || '+08:00'
};
connection = await mysql.createConnection(dbConfig);
console.log('✅ 数据库连接成功');
// 检查所有表的详细结构
const [tables] = await connection.execute(
`SELECT table_name FROM information_schema.tables
WHERE table_schema = ? ORDER BY table_name`,
[dbConfig.database]
);
console.log(`\n📊 数据库 ${dbConfig.database} 中共有 ${tables.length} 个表:`);
for (const table of tables) {
const tableName = table.TABLE_NAME || table.table_name;
console.log(`\n🔍 检查表: ${tableName}`);
// 获取表结构
const [columns] = await connection.execute(
`SELECT
COLUMN_NAME,
DATA_TYPE,
IS_NULLABLE,
COLUMN_DEFAULT,
COLUMN_KEY,
EXTRA,
COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE table_schema = ? AND table_name = ?
ORDER BY ORDINAL_POSITION`,
[dbConfig.database, tableName]
);
// 获取表记录数
const [countResult] = await connection.execute(`SELECT COUNT(*) AS count FROM ${tableName}`);
const recordCount = countResult[0].count;
console.log(` 📊 记录数: ${recordCount}`);
console.log(` 📋 字段结构 (${columns.length} 个字段):`);
columns.forEach(col => {
const nullable = col.IS_NULLABLE === 'YES' ? 'NULL' : 'NOT NULL';
const key = col.COLUMN_KEY ? `[${col.COLUMN_KEY}]` : '';
const extra = col.EXTRA ? `[${col.EXTRA}]` : '';
const defaultVal = col.COLUMN_DEFAULT !== null ? `DEFAULT: ${col.COLUMN_DEFAULT}` : '';
const comment = col.COLUMN_COMMENT ? `// ${col.COLUMN_COMMENT}` : '';
console.log(` - ${col.COLUMN_NAME}: ${col.DATA_TYPE} ${nullable} ${key} ${extra} ${defaultVal} ${comment}`);
});
// 获取索引信息
const [indexes] = await connection.execute(
`SELECT
INDEX_NAME,
COLUMN_NAME,
NON_UNIQUE,
INDEX_TYPE
FROM information_schema.STATISTICS
WHERE table_schema = ? AND table_name = ?
ORDER BY INDEX_NAME, SEQ_IN_INDEX`,
[dbConfig.database, tableName]
);
if (indexes.length > 0) {
console.log(` 🔑 索引信息:`);
const indexGroups = {};
indexes.forEach(idx => {
if (!indexGroups[idx.INDEX_NAME]) {
indexGroups[idx.INDEX_NAME] = {
columns: [],
unique: idx.NON_UNIQUE === 0,
type: idx.INDEX_TYPE
};
}
indexGroups[idx.INDEX_NAME].columns.push(idx.COLUMN_NAME);
});
Object.entries(indexGroups).forEach(([indexName, info]) => {
const uniqueStr = info.unique ? '[UNIQUE]' : '';
console.log(` - ${indexName}: (${info.columns.join(', ')}) ${uniqueStr} [${info.type}]`);
});
}
}
// 检查外键约束
console.log(`\n🔗 检查外键约束:`);
const [foreignKeys] = await connection.execute(
`SELECT
TABLE_NAME,
COLUMN_NAME,
REFERENCED_TABLE_NAME,
REFERENCED_COLUMN_NAME,
CONSTRAINT_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE table_schema = ? AND REFERENCED_TABLE_NAME IS NOT NULL
ORDER BY TABLE_NAME, COLUMN_NAME`,
[dbConfig.database]
);
if (foreignKeys.length > 0) {
foreignKeys.forEach(fk => {
console.log(` - ${fk.TABLE_NAME}.${fk.COLUMN_NAME} -> ${fk.REFERENCED_TABLE_NAME}.${fk.REFERENCED_COLUMN_NAME} [${fk.CONSTRAINT_NAME}]`);
});
} else {
console.log(' ⚠️ 未发现外键约束');
}
// 检查表大小
console.log(`\n💾 检查表存储大小:`);
const [tableSizes] = await connection.execute(
`SELECT
table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'size_mb',
table_rows
FROM information_schema.tables
WHERE table_schema = ?
ORDER BY (data_length + index_length) DESC`,
[dbConfig.database]
);
tableSizes.forEach(size => {
console.log(` - ${size.table_name}: ${size.size_mb} MB (${size.table_rows} 行)`);
});
console.log('\n🎉 表结构检查完成!');
return {
success: true,
tableCount: tables.length,
foreignKeyCount: foreignKeys.length
};
} catch (error) {
console.error('❌ 检查表结构失败:', error.message);
return {
success: false,
error: error.message
};
} finally {
if (connection) {
await connection.end();
console.log('🔒 数据库连接已关闭');
}
}
}
// 如果是直接运行此文件,则执行检查
if (require.main === module) {
checkTableStructure()
.then((result) => {
process.exit(result.success ? 0 : 1);
})
.catch(() => process.exit(1));
}
module.exports = { checkTableStructure };