如何使用QsqlQuery获取最后准备和执行的查询
How to get last prepared and executed query using QsqlQuery ?
插入:
QSqlQuery myQuery(db);
myQuery.prepare("INSERT INTO mytable VALUES (:val1, :val2)");
myQuery.bindValue(":val1", 1);
myQuery.bindValue(":val2", 2);
myQuery.exec();
然后我需要得到执行的SQL查询用于日志记录。
myQuery.executedQuery()
返回"INSERT INTO mytable VALUES (?, ?)"
。
如何使用实际绑定值来执行查询?
一个更好的函数(灵感来自Qt源代码:http://qt.gitorious.org/qt/qt/blobs/4.7/src/sql/kernel/qsqlresult.cpp#line644)。
这个函数应该处理几乎所有的情况:当使用名称绑定时,这个代码不与Oracle DB一起工作(这是唯一一个本地支持名称绑定的DB) => executedQuery()不返回查询'?’但是原来的查询…)
为了能够支持本地支持DB的Name Binding,绑定值的键必须按长度排序,然后在排序映射上循环…
QString getLastExecutedQuery(const QSqlQuery& query)
{
QString sql = query.executedQuery();
const int nbBindValues = query.boundValues().size();
for(int i = 0, j = 0; j < nbBindValues; ++j)
{
i = sql.indexOf(QLatin1Char('?'), i);
if (i <= 0)
{
break;
}
const QVariant &var = query.boundValue(j);
QSqlField field(QLatin1String(""), var.type());
if (var.isNull())
{
field.clear();
}
else
{
field.setValue(var);
}
QString formatV = query.driver()->formatValue(field);
sql.replace(i, 1, formatV);
i += formatV.length();
}
return sql;
}
编辑:我在前面的函数中发现了一个bug,如果a '?'存在于一个带引号的字符串中,'?'将被下一个可用值替换。这个bug已经存在于Qt源代码中。这个函数应该解决这个问题(可以改进很多,但想法是存在的)
QString getLastExecutedQuery(const QSqlQuery& query)
{
QString sql = query.executedQuery();
int nbBindValues = query.boundValues().size();
for(int i = 0, j = 0; j < nbBindValues;)
{
int s = sql.indexOf(QLatin1Char('''), i);
i = sql.indexOf(QLatin1Char('?'), i);
if (i < 1)
{
break;
}
if(s < i && s > 0)
{
i = sql.indexOf(QLatin1Char('''), s + 1) + 1;
if(i < 2)
{
break;
}
}
else
{
const QVariant &var = query.boundValue(j);
QSqlField field(QLatin1String(""), var.type());
if (var.isNull())
{
field.clear();
}
else
{
field.setValue(var);
}
QString formatV = query.driver()->formatValue(field);
sql.replace(i, 1, formatV);
i += formatV.length();
++j;
}
}
return sql;
}
lightstep建议的另一种方法是准备查询字符串,然后调用一个函数,该函数首先将查询写入日志,然后才调用真正的execute()。我个人使用QString::arg()和"%number"作为参数来制作查询字符串,而不是bindValue()。
让我们总结一下:
Solution #1 (lightstep)
我想出了这个解决方案:
QString getLastExecutedQuery(const QSqlQuery& query) { QString str = query.lastQuery(); QMapIterator<QString, QVariant> it(query.boundValues()); while (it.hasNext()) { it.next(); str.replace(it.key(),it.value().toString()); } return str; }
解决方案#2(我):
// my helper function
#define SQLDB_SHOW_QUERIES
#define SQLDB_LOG_QUERIES
#define SQLDB_LOG_FILENAME "sqlite.db.log"
bool executeQuery(QSqlQuery& queryObject, const QString& query)
{
bool result = true;;
#ifdef SQLDB_SHOW_QUERIES
std::cout<<query.toStdString()<<std::endl;
#endif
#ifdef SQLDB_LOG_QUERIES
std::fstream fs_log;
fs_log.open(SQLDB_LOG_FILENAME,std::ios::out|std::ios::app);
if (fs_log.is_open())
{
fs_log<<query.toUtf8().data()<<std::endl;
}
#endif
result &= queryObject.exec(query);
#ifdef SQLDB_SHOW_QUERIES
if (!result) std::cout<<queryObject.lastError().text().toStdString()<<std::endl;
std::cout<<std::endl;
#endif
#ifdef SQLDB_LOG_QUERIES
if (fs_log.is_open())
{
if (!result) fs_log<<queryObject.lastError().text().toUtf8().data()<<std::endl;
fs_log<<std::endl;
fs_log.close();
}
#endif
return result;
}
// your sample code
QSqlQuery myQuery(db);
QString query = QString("INSERT INTO mytable VALUES (%1,%2)")
.arg(1).arg(2);
executeQuery(myQuery,query);
必须以相反的顺序遍历元素才能得到正确的结果。
Example:
Query: " :a :aa "
query.bindValue(":a",1);
query.bindValue(":aa",1);
getLastExecutedQuery will return: "1 1a"
修复解决方案#1 (lightstep)
QString getLastExecutedQuery(const QSqlQuery& query)
{
QString str = query.lastQuery();
QMapIterator<QString, QVariant> it(query.boundValues());
it.toBack();
while (it.hasPrevious())
{
it.previous();
str.replace(it.key(),it.value().toString());
}
return str;
}
如果数据库用户具有"SUPER"权限,则可以在运行时设置日志记录。我在这篇文章中找到了一些灵感:如何显示MySQL上执行的最后查询?
在prepare语句前面添加以下代码:
QSqlQuery query("SET GLOBAL log_output = 'TABLE'");
query.exec("SET GLOBAL general_log = 'ON'");
在prepare、bindValue和exec语句之后添加以下代码:
query.exec("SET GLOBAL general_log = 0");
执行的查询存储在数据库"mysql"的"general_log"表中。"general_log"表将显示准备好的不带变量的查询,以及带已填写变量的查询。我没有尝试过,但是设置MySQL会话变量"sql_log_off"并且用户不需要"SUPER"权限是可能的。参考MySQL文档
仅适用于MySQL>= 5.1.12.
- 在C++中异步执行 sql 查询
- 在 MySQL 连接器C++ API 中使用一个函数调用执行多个查询的正确方法是什么?
- 使用C 打开PHP页面(执行查询)
- 如何在 c++ 中设置 ODBC 连接以执行多个查询 (SQLExecDirect)
- 执行“提升几何图形”最近的查询始终首先按最小距离排序结果
- 在什么条件下,数据库在从 cpp 执行选择查询时不会关闭游标
- QODBC连接到SQL Server,但无法执行查询
- 执行子查询
- 正确重复使用ADODB命令对象执行多个查询
- 如何执行异步屏幕外查询
- 使用用户定义的函数对循环/对称值执行Sqlite(C API)和查询(选择)
- C++ 和SQLite - 如何执行由用户输入形成的查询
- 如何使用oracle在sqlapi++中执行多个查询
- 无法使用 Oracle ODBC 执行查询
- 关于条件C++如何执行的简单查询
- 在终止/非活动会话上执行简单查询需要几分钟时间
- 程序在每次执行查询时挂起
- 非常长的查询执行使用c++, Sql Server和ODBC连接
- 插入两个相互引用的SQL表,而无需执行单独的查询
- 每次执行查询时都有新的连接