0%

Sequelize 打印 SQL

问题

在开发过程中,我们经常需要查看 Sequelize 生成的 SQL 语句,以便于调试。

解决

主要是添加 logQueryParameters: truelogging: true 这两个参数

1
2
3
4
5
6
7
var sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mysql',
host: 'localhost',
port: 3306,
logQueryParameters: true,
logging: true
});

其中 logQueryParameters 表示是否打印参数,logging 表示是否打印 SQL 语句

但实际上会出现这样的状况,会发现控制台打印的参数无法在 SQL 里面执行。

1
2
UPDATE `users` SET `age`=10;
INSERT INTO `UserData` (`id`,`name`,`phone`,`provinceCode`,`cityCode`,`createdAt`,`updatedAt`) VALUES (DEFAULT, '张三', '12345678901', '11', '1101', '2023-08-04 05:21:59', '2023-08-04 05:21:59');

会变成

1
2
3
UPDATE `users` SET `age`=?; 10
INSERT INTO `UserData`
(`id`,`name`,`phone`,`provinceCode`,`cityCode`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?,?,?,?,?); "张三", "12345678901", "11", "1101", "2023-08-04 05:21:59", "2023-08-04 05:21:59"

对于使用 Sequelize 进行插入操作,日志输出会显示带有占位符的 SQL 查询语句,这是因为 Sequelize 会使用参数化查询来防止 SQL 注入攻击,并提高查询的性能。

然后我去看了源码

https://github.com/sequelize/sequelize/blob/345981f2eb4c1aa709d5b831136b8dbbb32e72b8/lib/dialects/mysql/query.js#L37

1
const complete = this._logQuery(sql, debug, parameters);

https://github.com/sequelize/sequelize/blob/60d16a34562d4f7ed0fa74ede65a9c641a7e6a95/packages/core/src/dialects/abstract/query.js#L361

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
_logQuery(sql, debugContext, parameters) {
const { connection, options } = this;
const benchmark = this.sequelize.options.benchmark || options.benchmark;
const logQueryParameters = this.sequelize.options.logQueryParameters || options.logQueryParameters;
const startTime = Date.now();
let logParameter = '';

if (logQueryParameters && parameters) {
const delimiter = sql.endsWith(';') ? '' : ';';

logParameter = `${delimiter} with parameters ${NodeUtil.inspect(parameters)}`;
}

const fmt = `(${connection.uuid || 'default'}): ${sql}${logParameter}`;
const queryLabel = options.queryLabel ? `${options.queryLabel}\n` : '';
const msg = `${queryLabel}Executing ${fmt}`;
debugContext(msg);
if (!benchmark) {
this.sequelize.log(`${queryLabel}Executing ${fmt}`, options);
}

return () => {
const afterMsg = `${queryLabel}Executed ${fmt}`;
debugContext(afterMsg);
if (benchmark) {
this.sequelize.log(afterMsg, Date.now() - startTime, options);
}
};
}

发现它并没有直接把参数替换掉,而是传入到了数组中,最后调用了 mysql2 的 execute 方法

https://github.com/sidorares/node-mysql2/tree/cf1a8b7d4c3bb4ee13e4362d85419efe9fb80202/documentation/zh-cn#sql预处理的使用

1
2
3
4
const [rows, fields] = await connection.execute(
'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?',
['Morty', 14]
);

使用 MySQL2,您还可以提前准备好SQL预处理语句。 使用准备好的SQL预处理语句,MySQL 不必每次都为相同的查询做准备,这会带来更好的性能。 如果您不知道为什么它们很重要,请查看这些讨论:

所以我只简单的修改了一下源码,添加了一个输出实际 SQL 的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const mysql = require('mysql2');

const connection = mysql.createConnection({
host: 'your_mysql_host',
user: 'your_mysql_username',
password: 'your_mysql_password',
database: 'your_mysql_database',
});

const sql = 'SELECT * FROM your_table WHERE column = ?';
const values = ['some_value'];

connection.execute(sql, values, (err, results) => {
if (err) {
console.error('Error executing query:', err.message);
return;
}

console.log('Query:', connection.format(sql, values)); // 打印查询语句
console.log('Results:', results);
});

connection.end();