Pregunta ¿Cómo obtener la última consulta preparada y ejecutada usando QsqlQuery?


Estoy haciendo una inserción:

QSqlQuery myQuery(db);
myQuery.prepare("INSERT INTO mytable VALUES (:val1, :val2)");
myQuery.bindValue(":val1", 1);
myQuery.bindValue(":val2", 2);
myQuery.exec();

Entonces necesito obtener la consulta SQL ejecutada para fines de registro.

myQuery.executedQuery() devoluciones "INSERT INTO mytable VALUES (?, ?)".

¿Cómo puedo obtener una consulta ejecutada con los valores reales encuadernados que se usaron?


15
2018-04-25 10:19


origen


Respuestas:


Una alternativa a lo que paso de luz sugerido, es preparar cadenas de consulta y luego llamar a una función que primero escribe la consulta en el registro y solo luego llama a ejecutar real (). Yo personalmente uso QString :: arg () y "% number" para argumentos para hacer una cadena de consulta en lugar de bindValue ().

Vamos a resumir las cosas:

Solución n. ° 1 (paso de luz)

Se me ocurrió esta solución:

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;
}

Solución n. ° 2 (yo):

// 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);

4
2018-03-25 07:22



Una mejor función (inspirada en el código fuente de Qt: http://qt.gitorious.org/qt/qt/blobs/4.7/src/sql/kernel/qsqlresult.cpp#line644)

Esta función debería manejar casi todos los casos: Este código no funciona con Oracle DB cuando se usa Enlace de nombre (Esta es la única BD que admite nativamente Enlace de nombre => executeQuery () no devuelve la consulta con '?' Sino la consulta original. ..)

Para poder admitir el enlace de nombre de soporte nativo de DB, las claves de los valores encuadernados se deben ordenar por longitud, luego recorrer el mapa ordenado ...

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;
}

Editar: encontré un error en la función anterior, si un '?' existe dentro de una cadena citada, el '?' es reemplazado por el siguiente valor disponible. El error ya existe en el código fuente de Qt. Esta función debería solucionar este problema (podría mejorarse mucho, pero la idea está ahí)

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;
}

6
2018-05-17 17:58



Debe iterar sobre los elementos en orden inverso para obtener el resultado correcto.

Example:
Query: " :a :aa " 
query.bindValue(":a",1);
query.bindValue(":aa",1);
getLastExecutedQuery will return: "1 1a"

Solución fija n. ° 1 (paso de luz)

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;
}

1
2018-05-20 08:53



Si el usuario de la base de datos tiene derechos "SUPER", el registro se puede establecer durante el tiempo de ejecución. Encontré algo de inspiración para esta respuesta en esta publicación: ¿Cómo mostrar las últimas consultas ejecutadas en MySQL?

Agregue el siguiente código al frente de la declaración de preparación:

QSqlQuery query("SET GLOBAL log_output = 'TABLE'");
query.exec("SET GLOBAL general_log = 'ON'");

Agregue el siguiente código después de las instrucciones prepare, bindValue y exec:

query.exec("SET GLOBAL general_log = 0");

Las consultas ejecutadas se almacenan en la tabla "general_log" de la base de datos "mysql". La tabla "general_log" mostrará el preparado sin variables, así como las consultas con las variables rellenas. No lo probé, pero podría ser posible configurar la variable de sesión de MySQL "sql_log_off" y que el usuario no necesita derechos "SUPER". Ver Documentación de MySQL.

Solo funciona con MySQL> = 5.1.12.


0
2017-08-16 15:20