Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Разработка баз данных _ Как ускорить создание таблицы (через QSqlQuery)?

Автор: Rocky 16.2.2009, 13:32

Создаю таблицу и заполняю ее посредством запроса INSERT. В таблицы больше 1к записей и заполнение идет очень долго... Можно ли как-нибудь ускорить запись? Оптимизировать это дело?

Да, кто знает, обязательно ли перед каждым вызовом QSqlQuery::exec использовать QSqlQuery::prepare (все это происходит в цикле)? Или QSqlQuery::prepare достаточно вызывать один раз перед циклом, а внутри делать только QSqlQuery::bindValue и затем QSqlQuery::exec?

Спасибо!

Автор: Litkevich Yuriy 16.2.2009, 13:45

Цитата(Rocky @ 16.2.2009, 16:32) *
обязательно ли перед каждым вызовом QSqlQuery::exec использовать QSqlQuery::prepare (все это происходит в цикле)?
да он для того и предназначен, чтобы его вынести можно было из цикла:
query.prepare("SELECT F_GROUP FROM APPUSER WHERE (F_ID = :id)");
for(...)
{
    query.bindValue(":id", id);
    if (! query.exec())
    {
        // Обработать ошибку
    }
}


Цитата(Rocky @ 16.2.2009, 16:32) *
Создаю таблицу и заполняю ее посредством запроса INSERT. В таблицы больше 1к записей и заполнение идет очень долго...
Что за СУБД и сколько по времени это занимает?

Автор: Rocky 16.2.2009, 15:15

СУБД SQLite 3.
В базе 11 таблиц, общее число вставляемых записей около 5.3к. 5.3к записей вставляется 18 мин (prepare я уже вынес из тел циклов). Задержек в других местах в коде нет точно (есть только цикл, внутри которого bindValue и exec).

Когда работал через ODBC с MS Access, было все намнооого быстрее (4к записей около минуты)...

Автор: Litkevich Yuriy 16.2.2009, 16:05

Цитата(Rocky @ 16.2.2009, 18:15) *
Когда работал через ODBC с MS Access, было все намнооого быстрее
ну ты сравнил, Лайт придуман для учебых целей, да нвские мелкие настройки хранить. 4К записей для него слишком жирно.

Автор: Rocky 16.2.2009, 16:31

А есть какой-нибудь "легкий" вариант, т.е. чтобы база была под рукой (рядом с прогой), но кроме драйвера ничего не требовалось устанавливать? (никаких серверов БД и пр).

Автор: trdm 16.2.2009, 16:57

Цитата(Rocky @ 16.2.2009, 13:32) *
Создаю таблицу и заполняю ее посредством запроса INSERT. В таблицы больше 1к записей и заполнение идет очень долго... Можно ли как-нибудь ускорить запись? Оптимизировать это дело?

_db.transaction();
+
_db.commit();

http://www.doc.crossplatform.ru/qt/4.3.2/qsqldatabase.html#transaction
и т.п.

Автор: Litkevich Yuriy 16.2.2009, 17:27

trdm, транзакция не ускоряет процесс, она лишь делает его атомарным

Цитата(Rocky @ 16.2.2009, 19:31) *
А есть какой-нибудь "легкий" вариант, т.е. чтобы база была под рукой (рядом с прогой), но кроме драйвера ничего не требовалось устанавливать? (никаких серверов БД и пр).
можно FireBird или MySQL использовать, у них есть встраиваемые версии, dll'ку подкладываешь в каталог приложения и все.
Обе эти СУБД хранять БД в виде одного файла (бывают конечно и многофайловые БД, но это уже другая история)

Автор: trdm 16.2.2009, 18:00

Цитата(Litkevich Yuriy @ 16.2.2009, 17:27) *
trdm, транзакция не ускоряет процесс, она лишь делает его атомарным

еще как ускоряет.. была такая же проблема, выручила транзакция.

Автор: panter_dsd 16.2.2009, 19:38

Полностью согласен с trdm, сам сталкивался. Соотношения времени огромные. У меня до использования транзакции работа занимала минуты три, после использования онной стало несколько секунд. О как. :)

Автор: Константин 16.2.2009, 21:44

Цитата(Litkevich Yuriy @ 16.2.2009, 17:27) *
можно FireBird или MySQL использовать, у них есть встраиваемые версии, dll'ку подкладываешь в каталог приложения и все.
Обе эти СУБД хранять БД в виде одного файла (бывают конечно и многофайловые БД, но это уже другая история)

встроенный мускуль хранит базы так же, как и полноценный :) и с ним чуть больше гемора...

Автор: Tonal 17.2.2009, 9:29

Цитата(Litkevich Yuriy @ 16.2.2009, 20:27) *
trdm, транзакция не ускоряет процесс, она лишь делает его атомарным

Действительно так, но если ты явно не стартуешь транзакцию, то Qt её стартует на каждый запрос.
А старт и окончание транзакции может занять довольно много времени (в зависимости от типа движка). :)

Автор: SABROG 17.2.2009, 9:55

Цитата(Rocky @ 16.2.2009, 15:15) *
СУБД SQLite 3.


На тему скоростей sqlite было несколько статей в интернете с рекомендациями. Можно добавить следующие опции SQL запросом:

PRAGMA synchronous = OFF;
PRAGMA page_size = 4096;


На самом деле page_size зависит от операционной системы/файловой системы. Для винды одно значение оптимальное, для *nix'ов другое. В общем погугли на тему этих комманд.

Еще обращу внимание на такие методы (псевдокод):

    query.prepare(QLatin1String("INSERT INTO table (field1, field2) VALUES (?,?);"));
...
    QVariantList field1, field2;
    field1 << 1 << 2 << 3 << 4;
    field2 << 5 << 6 << 7 << 8;
...
    addBindValue(field1);
    addBindValue(field2);
...
query.execBatch(QSqlQuery::ValuesAsRows);

Т.е. в QSqlQuery можно передавать сразу целые списки для каждого объявленного поля, драйвер сформирует сам запросы.

Автор: Rocky 17.2.2009, 10:21

Ххе, ясно. SABROG, спасибо, погуглю..

Я тут добавил транзакции, я в шоке :o:. Время с 18 мин. упало до 9-ти сек. Я не успел пасьянс открыть, как программа заявила что все таблицы сформированы :)

Эх, осталось разобраться только почему программно не удается менять значения в базе... ((

Вобщем, всем большое спасибо! :)

Автор: Константин 17.2.2009, 11:53

повышай SABROG-а за это.

Автор: Litkevich Yuriy 17.2.2009, 12:01

Цитата(Rocky @ 17.2.2009, 13:21) *
Ххе, ясно. SABROG, спасибо,
просто ткни "Спасибо"

Автор: trdm 17.2.2009, 17:44

а я что :) недостоин плюсика? :) волки :)
нас тупых 1С-ников нихто не любит ))))

Автор: Rocky 17.2.2009, 18:09

Упс, сорри :) Так лучше? :)))))

Автор: trdm 17.2.2009, 18:12

блин, пошутить нельзя без последствий :)

Спасибо.

Автор: Rocky 17.2.2009, 21:37

:)

По-поводу транзакций... Ув. форумчане, не подскажите вот при навигации по таблице БД, нужно их использовать? Или они толку не дадут?

QSqlQuery oQuery;

//вот тут transaction
oQuery.exec(QString("SELECT * FROM %1").arg(m_sInternalDatabaseName));
QSqlRecord oRecord = oQuery.record();

int nPipeType = oRecord.indexOf("PipeType");
int nDOutside = oRecord.indexOf("D_outside");
int nDInside = oRecord.indexOf("d_inside");
int nRoughness = oRecord.indexOf("Roughness");
int nLamda = oRecord.indexOf("Lamda");
    
m_roPipes.clear();
while (oQuery.next())
{
    m_roPipes.insert(CPipe(oQuery.value(nPipeType).toString(), oQuery.value(nDOutside).toString(),
            oQuery.value(nDInside).toString(), oQuery.value(nRoughness).toString(), oQuery.value(nLamda).toString()));
}
//вот тут commit

Или транзакции важны именно при вставке/удалении/обновлении базы данных?

Автор: SABROG 17.2.2009, 21:58

Только для изменения таблиц.

Если не устраивает скорость выборки, то рекомендую создать индексы по полям. Здорово увеличивает скорость.
Индексы обычно создаются по полям, которые участвуют в "order by". Если SQL запрос статичный в принципе, то можно сделать вьюху (create view). Выборку можно будет вызывать например так "select myview" (если память не отшибает).
Индексы создаются так:

CREATE INDEX IF NOT EXISTS mytable_index ON mytable (col1, col2, col3);

Автор: panter_dsd 17.2.2009, 22:01

В данном случае не дадут. В основном транзакции применяются при изменении или если тебе нужны точные данные, т.е. когда подряд делаешь несколько селектов и не хочешь, чтобы за это время кто-либо что-либо изменил. Т.е. грубо транзакция - слепок БД на определенный момент времени.

Автор: SABROG 17.2.2009, 22:06

Цитата(panter_dsd @ 17.2.2009, 22:01) *
если тебе нужны точные данные, т.е. когда подряд делаешь несколько селектов и не хочешь, чтобы за это время кто-либо что-либо изменил


Это если говорить о базах данных вообще. Но sqlite не поддерживает синхронное изменение базы данных, ни через потоки, никак вообще.

Автор: panter_dsd 17.2.2009, 22:16

У меня нормально 5 клиентов на одной базе уживалось, все нормально было. :)

Автор: Rocky 17.2.2009, 22:39

Ага, теперь разобрался.. Спасибо :)

Автор: Kagami 17.2.2009, 22:40

Цитата(Rocky @ 17.2.2009, 21:37) *
Ув. форумчане, не подскажите вот при навигации по таблице БД, нужно их использовать? Или они толку не дадут?

Для ускорения навигации можно использовать следующее:
Цитата
void QSqlQuery::setForwardOnly ( bool forward )
Sets forward only mode to forward. If forward is true, only next() and seek() with positive values, are allowed for navigating the results.
Forward only mode can be (depending on the driver) more memory efficient since results do not need to be cached. It will also improve performance on some databases. For this to be true, you must call setForwardMode() before the query is prepared or executed. Note that the constructor that takes a query and a database may execute the query.
Forward only mode is off by default.

Автор: SABROG 17.2.2009, 22:42

Цитата(panter_dsd @ 17.2.2009, 22:16) *
У меня нормально 5 клиентов на одной базе уживалось, все нормально было. :)

В общем чтение множеством клиентов возможно, а изменение нет.
Цитата
(5) Can multiple applications or multiple instances of the same application access a single database file at the same time?

Multiple processes can have the same database open at the same time. Multiple processes can be doing a SELECT at the same time. But only one process can be making changes to the database at any moment in time, however.


Теперь уже сложно сказать полезен begin transaction для select'a или нет...

Про forwardonly тут http://www.sqlite.org/cvstrac/wiki?p=ScrollingCursor

Автор: panter_dsd 17.2.2009, 23:34

И изменяют нормально. Блокировки работают.

Т.е. пишут поочередно, что дает небольшие потери в скорости.

Автор: trdm 18.2.2009, 0:50

Цитата(panter_dsd @ 17.2.2009, 23:34) *
И изменяют нормально. Блокировки работают.
Т.е. пишут поочередно, что дает небольшие потери в скорости.

авторы скулайта предупреждали вроде,
что на смешанных архитектурах блокировки работать будут через пень колоду,
так что там танцевать надо хорошенько вокруг этого процесса - отключать кеширования дисков и
типа не любит она толи фата то-ли нтфс-а.
не помню, но читал...
Цитата
SQLite uses POSIX advisory locks to implement locking on Unix. On Windows it uses the LockFile(), LockFileEx(), and UnlockFile() system calls. SQLite assumes that these system calls all work as advertised. If that is not the case, then database corruption can result. One should note that POSIX advisory locking is known to be buggy or even unimplemented on many NFS implementations (including recent versions of Mac OS X) and that there are reports of locking problems for network filesystems under Windows. Your best defense is to not use SQLite for files on a network filesystem.

Автор: Litkevich Yuriy 18.2.2009, 1:49

Цитата(Rocky @ 18.2.2009, 0:37) *
при навигации по таблице БД, нужно их использовать? Или они толку не дадут?
для случая с SQLite наверное не нужно. А вот при использовании более продвинутых СУБД, может и понадобится. Зависит от того, как ты потроха реализовал. Напримр, если ты используешь процедуру выбора, в которой вызывается другая процедура, что-либо изменяющая в БД, то не откатывая явно транзакцию в БД будет мусор создаваться (во внутренностях) - БД будет расти. Надо будет ее чистить.

Автор: Tonal 18.2.2009, 11:38

Транзакции нужны для гарантирования атомарности изменений и консистентности данных.
Простой пример:

int sql_count() {
  QSqlQuery query("SELECT COUNT(*) FROM tbl");
  return query.value(0).toInt();
}

int rt_count() {
  QSqlQuery query("SELECT * FROM tbl");
  int count = 0
  while (query.next())
    ++count;
  return count;
}

bool test() {
  return sql_count() == rt_count();
}

Функция test() может вернуть false, если вызвана не внутри транзакции.

Автор: SABROG 18.2.2009, 11:52

Цитата(Tonal @ 18.2.2009, 11:38) *
Функция test() может вернуть false, если вызвана не внутри транзакции.


Мне память не изменяет, сначала будет вызвана rt_count(), затем sql_count()?

Автор: Tonal 18.2.2009, 13:10

Последовательность их вызова зависит от компилятора и оптимизатора.
Но если запросы в них выполняются в разных транзакциях, как это происходит в Qt если не стартовать транзакцию явно, то между первым и вторым может вклиниться кто-то третий и изменить состав таблицы.
Если же транзакция одна (с соответственным уровнем), то вклинится никто не может.

Автор: 512es 20.3.2010, 20:57

Извиняюсь, что тема давнешняя. Но до сих пор, (раз я сюда попал) актуальная.

Так вот, хочу сказать что для ускорения инсерта в склайт базу, лучше всего подходит инсертить в темпорари таблицу, а из неё моментально всё перелетает в обычную.

Вот только с постгресом это не прокатывает. Попробую через execBatch..

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)