Russian Qt Forum

Qt => Базы данных => Тема начата: Alex_C от Август 10, 2012, 09:08



Название: QSqlQuery - почему не работает last/size?
Отправлено: Alex_C от Август 10, 2012, 09:08
Переделываю свой проект на Qt. До того, как не столкнулся с работой с БД Access все было отлично. Но такое впечатление, что с Access Qt принципиально не хочет работать.
Делаю запрос:
Код
C++ (Qt)
   QSqlQuery q(queryStr);
   if(q.isSelect())
   {
       q.last();
       qDebug() << q.value(2).toString();
   }
 
Выдает
QSqlQuery::value: not positioned on a valid record

Меняешь last на first - все ок!
И size выдает -1.
Проверял - q.last() выдает true.
Может что не так делаю?


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: LisandreL от Август 10, 2012, 09:10
А exec где?


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Alex_C от Август 10, 2012, 09:44
Из документации:
If query is not an empty string, it will be executed.

queryStr - как раз строка "SELECT * ... "


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Странник от Август 10, 2012, 09:44
А exec где?
в генетическом кодЕ:
Цитировать
QSqlQuery::QSqlQuery ( const QString & query = QString(), QSqlDatabase db = QSqlDatabase() )
Constructs a QSqlQuery object using the SQL query and the database db. If db is not specified, or is invalid, the application's default database is used. If query is not an empty string, it will be executed.
что касается last() - если возвращает true, логично ожидать успешного выполнения. похоже на баг.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: LisandreL от Август 10, 2012, 10:10
в генетическом кодЕ
Всегда через prepare использую, поэтому забыл, что так сразу выполняться будет.
Код
C++ (Qt)
QVariant QSqlQuery::value(int index) const
{
   if (isActive() && isValid() && (index > QSql::BeforeFirstRow))
       return d->sqlResult->data(index);
   qWarning("QSqlQuery::value: not positioned on a valid record");
   return QVariant();
}
Проверяйте, какое из 3 условий у вас не срабатывает.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Alex_C от Август 10, 2012, 15:14
Код
C++ (Qt)
   QSqlQuery q;
   q.exec(queryStr);
 
   qDebug() << q.isActive();
   qDebug() << q.isSelect();
   q.last();
   qDebug() << q.value(2).toString();
 

ответ

Код:
true 
true
QSqlQuery::value: not positioned on a valid record

Итог: все условия срабатывают.

... q.last() тоже true возвращает.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: LisandreL от Август 10, 2012, 15:25
Итог: все условия срабатывают
Я имел в виду вот эти 3:
1) isActive()
2) isValid()
3) index > QSql::BeforeFirstRow

Первый проверен, третий очевиден, остался isValid().

Может запрос завершается с ошибкой? Что q.exec возвращает?


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Alex_C от Август 13, 2012, 14:40
Вот что получается.
Код:

Код
C++ (Qt)
   qDebug() << q.exec(queryStr);
   qDebug() << q.first();
   qDebug() << q.isActive();
   qDebug() << q.isSelect();
   qDebug() << q.isValid();
   qDebug() << q.last();
   qDebug() << q.isValid();
   qDebug() << q.value(2).toString();
 

вывод
Код
C++ (Qt)
true
true
true
true
true
true
false
QSqlQuery::value: not positioned on a valid record
 

Итог: last() не срабатывает. Почему?
Чтобы добраться до последней записи нужно while(q.next());
Но разве так правильно с БД работать?


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: LisandreL от Август 13, 2012, 15:05
И size выдает -1.
Если это всегда так, то можно предположить, что
QSqlDriver::hasFeature( QSqlDriver::QuerySize ) == false
а это скорее всего означает, что данная БД позволяет осуществлять только последовательный доступ к записям и даже правильная реализация last() сводилось бы к while(q.next());
Но то что q.last(); возвращает true, но при этом устанавливает курсор на невалидную позицию - это безусловно баг. Было бы не плохо оформить багрепорт.

P.S. queryStr не покажете?


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Alex_C от Август 13, 2012, 16:19
Да тут похоже баг существенно больше: БД - Access, и методы last/first/RecordCount в том же Дельфи через ADO работают без проблем. 
А строка запроса самая простоя - SELECT * FROM table.
Мда, перечитал все что нашел по работе Qt с БД - по хоже у Qt это самое слабое место. Дельфи/Билдер куда как больше возможностей предоставляют.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Странник от Август 13, 2012, 22:29
Да тут похоже баг существенно больше: БД - Access, и методы last/first/RecordCount в том же Дельфи через ADO работают без проблем. 
А строка запроса самая простоя - SELECT * FROM table.
Мда, перечитал все что нашел по работе Qt с БД - по хоже у Qt это самое слабое место. Дельфи/Билдер куда как больше возможностей предоставляют.
а то, что вы пользуетесь ODBC, не смущает?) напишите свой ADOвый драйвер, с маджонгом и гейшами - нация оценит.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Noel от Август 14, 2012, 15:35
Цитировать
Мда, перечитал все что нашел по работе Qt с БД - по хоже у Qt это самое слабое место. Дельфи/Билдер куда как больше возможностей предоставляют.
Как писали классики : "Ну ты, батенька, да... Ну ты, батенька, тут сглупил, конечно! Сказал не подумавши!".
В своё время (года два-три назад) пилил курсачи за еду на Qt. И как раз с QODBC драйвером очень часто сталкивался. Всё работает нормально. Никаких багов я в нём не заметил, тем более, что он из коробки идёт.
Читать надо не "всё, что нашёл по работе.." а доку и екземплы. Тем более, что екземплов - тьма-тьмущая.

If не нужен. На самом деле мне кажется, что QSqlQuery просто не привязан к твоей БД. Т.е. он выполняется куда-то в холостую.
Код:
QSqlQuery q(queryStr);
if(q.isSelect())
{
    q.last();
    qDebug() << q.value(2).toString();
}

Попробуй, к примеру, этот код.

Код:
QSqlDataBase m_db = QSqlDataBase::addDatabase("QODBC", "newConnection");
m_db.setDatabaseName("mydb");
m_db.open();

QSqlQuery que(m_db);
if (que.exec("SELECT * FROM table"))
    que.last();
int someVal = que.value(0).toInt();



Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Странник от Август 14, 2012, 15:50
Всё работает нормально. Никаких багов я в нём не заметил, тем более, что он из коробки идёт.
багов-то нет, но функционал уступает ADO. да и помедленнее он будет.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Noel от Август 14, 2012, 16:45
Цитировать
функционал уступает ADO.
Пожалуйста, можно примеры?
Цитировать
да и помедленнее он будет.
Я пологаю, что не критично медленней.

Тем более, что у Qt есть контейнер ActiveX. При желании возни с ADO можно идти прямиком туда. Только особых преймуществ - не будет.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Странник от Август 14, 2012, 17:31
Пожалуйста, можно примеры?
если идти по фичам драйвера: QSqlDriver::QuerySize, QSqlDriver::LastInsertId (пустячок, а неприятно), QSqlDriver::MultipleResultSets (намучался в свое время - ODBC вел себя крайне неадекватно).
Alex_C недавно напомнил, что ODBC не поддерживает хранение резалтсета не сервере. и в производительности разница заметна, работаю с MS SQL по ADO и ODBC. c ODBC, правда, еще и Qt-оверхед.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Alex_C от Август 15, 2012, 10:51
Да, курсачи на Qt пишутся на славу, а вот серьезное что-то сделать - это уже другая история. На счет "На самом деле мне кажется, что QSqlQuery просто не привязан к твоей БД." даже отвечать не хочется... По моему в моих первых сообщениях все очень подробно описано.

Цитировать
функционал уступает ADO.
Пожалуйста, можно примеры?

Как я уже писал: на ADO при использовании RecordSet можно указать св-во CursorLocation - зачем оно описывать тут лень, в моих темах в форуме по БД я это уже описывал. Приведу такой пример: в БД 2-3 млн. записей. Пользователь задает выборку записей. Результат выборки - 200 тыс записей. Пользователю нужно в табличном виде показать все записи. Тут многие спрашивали - зачем? Ответ очень простой: пользователь хочет просмотреть последние 10-100 записей выборки. А захочет и все 200 тыс просмотрит. При использовании ADO с св-вом CursorLocation=Server это реализуется без проблем. На Qt такое реализовать в принципе можно, но ждать результата выборки придется очень долго.
Ну последней каплей конечно стало, что даже метод last() не работает.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Alex_C от Август 15, 2012, 13:05
Тем более, что у Qt есть контейнер ActiveX. При желании возни с ADO можно идти прямиком туда. Только особых преймуществ - не будет.

Кстати, есть как работать через ADO - кому интересно - код скину. Но! Нужно писать ИМЕННО драйвер - иначе QSqlTableModel и т.п. работать не будут. Или же самому писать свои модели.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Странник от Август 15, 2012, 14:10
Кстати, есть как работать через ADO - кому интересно - код скину. Но! Нужно писать ИМЕННО драйвер - иначе QSqlTableModel и т.п. работать не будут. Или же самому писать свои модели.
написать плагин драйвера не так уж сложно, на самом деле. писал когда-то для трехзвенки.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: xokc от Август 16, 2012, 09:22
Я правильно понимаю, что для работы с ADO был собственный плагин разработан? Если - да, то тогда проблемы неработающего last(), серверных курсоров и т.п. - это ведь проблемы именно функционала плагина.


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Alex_C от Август 16, 2012, 11:01
Еще не разработан :) Есть наработки в плане того, как работать с ADO из Qt.

Кстати - сейчас ищу документацию по написанию драйвера для работы с БД. Но пока подробной документации не нашел..


Название: Re: QSqlQuery - почему не работает last/size?
Отправлено: Странник от Август 16, 2012, 11:25
Кстати - сейчас ищу документацию по написанию драйвера для работы с БД. Но пока подробной документации не нашел..
что там искать-то: How to Write Your Own Database Driver (http://"http://qt-project.org/doc/qt-4.8/sql-driver.html#how-to-write-your-own-database-driver")

если вкратце, вам нужно написать наследников:
QSqlDriverPlugin
QSqlDriver
QSqlResult

смотрите документацию по соответствующим классам, созданию плагинов и исходники существующих плагинов в качестве примера.
накидал примерный шаблон, для чего нужно написать реализацию:
main.cpp
Код:
#include "AdoDriver.h"
#include <QSqlDriverPlugin>

QT_BEGIN_NAMESPACE

class AdoDriverPlugin : public QSqlDriverPlugin
{
public:
    AdoDriverPlugin();

    QSqlDriver *create(const QString &name);
    QStringList keys() const;
};

AdoDriverPlugin::AdoDriverPlugin()
    : QSqlDriverPlugin()
{
}

QSqlDriver *AdoDriverPlugin::create(const QString &name)
{
    if (name == QLatin1String("QADO"))
    {
        AdoDriver *driver = new AdoDriver();
        return driver;
    }

    return 0;
}

QStringList AdoDriverPlugin::keys() const
{
    return QStringList("QADO");
}

Q_EXPORT_STATIC_PLUGIN(AdoDriverPlugin)
Q_EXPORT_PLUGIN2(sqlado, AdoDriverPlugin)

QT_END_NAMESPACE

AdoDriver.h
Код:
#ifndef ADODRIVER_H
#define ADODRIVER_H

#ifdef QT_PLUGIN
#define Q_EXPORT_SQLDRIVER_ADO
#else
#define Q_EXPORT_SQLDRIVER_ADO Q_SQL_EXPORT
#endif

#include <QSqlResult>
#include <QSqlDriver>

QT_BEGIN_HEADER

QT_BEGIN_NAMESPACE

class AdoDriver;

class AdoResult : public QSqlResult
 {
 public:
     AdoResult(const AdoDriver *dr);
     ~AdoResult();

 protected:
     QVariant data(int index);
     bool isNull(int index);
     bool reset(const QString &query);
     bool fetch(int index);
     bool fetchFirst() ;
     bool fetchLast();
     int size();
     int numRowsAffected();
     QSqlRecord record() const;
 };

class Q_EXPORT_SQLDRIVER_ADO AdoDriver : public QSqlDriver
{
    Q_OBJECT
public:
    explicit AdoDriver(QObject *parent = 0);
    ~AdoDriver();

    bool hasFeature(DriverFeature feature) const;

    bool open(const QString &db, const QString &user, const QString &password, const QString &host, int port, const QString &options);
    void close();

    QSqlResult *createResult() const;

    QStringList tables(QSql::TableType tableType) const;
    QSqlRecord record(const QString &tableName) const;
    QSqlIndex primaryIndex(const QString &tableName) const;
};

QT_END_NAMESPACE

QT_END_HEADER

#endif // ADODRIVER_H