Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. SQL very slow
Qt 6.11 is out! See what's new in the release blog

SQL very slow

Scheduled Pinned Locked Moved General and Desktop
12 Posts 5 Posters 6.4k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    jech
    wrote on last edited by
    #1

    Hi,

    last two days I spend a lot of time on finding the right solution for my problem. Everything I did was unusable for some reason in the end.

    I think it will be better to be specific about what I want to do. I'm working on a music player and I want to have a library (database). I already made a first version in PySide (Python) which works well. But now I'm converting it to C++/QML. The database has about 30 columns and possibly ten thousands of rows. I use TagLib to scan the media files and then I want to write the information to my database.

    As I'm not really familiar with C++, I want to have as much as possible in QML. My first idea was to call a slot in QML and pass all the tags to it. This works fine but unfortunately it freezes the QML GUI for quite a long time, because I can not run the query in a background thread. QML simply doesn't allows it, AFAIK.

    So I tried it in C++. I create a QList<QStringList>. Each item of the QList is a QStringList containing information about one media file (genre, artist, album, title etc.) Then I run in a background thread following code:

    @bool Database::storeLibrary() {
    QSqlQuery query;
    QString queryString;
    for (int i=0; i<mediaTagsList.size(); i++) {
    queryString = QString("INSERT INTO MainLibrary VALUES ('");
    for (int j=0; j<mediaTagsList[0].size(); j++) {
    if (j != 0)
    queryString += "', '";
    queryString += mediaTagsList[i][j].replace("'","''");
    }
    queryString += "')";
    query.exec(queryString);
    }
    }
    @

    Unfortunately this code is extremely slow. It takes a whole minute to store only 500 records. The code is very similar to what I am using in Python and it is fast. What am I doing wrong? I see a high activity of HDD during the operation. Maybe I should keep the db in memory and then store it when everything is done.

    Could you please advise me what am I doing wrong? What would be the best way to scan media files and store them to database?

    1 Reply Last reply
    0
    • P Offline
      P Offline
      puterk
      wrote on last edited by
      #2

      Have you tried using QSqlQuery::prepare and QSqlQuery::addBindValue ?

      I don't know enough about your data but perhaps you could try something like this:
      @
      QSqlQuery query;
      query.prepare("INSERT INTO table1 VALUES (?,?);

      for (i =0; i<rows; i++)
      {
      variable1=column1[i];
      query.addBindValue(variable1);
      variable2=column2[i];
      query.addBindValue( variable2);
      }
      q.execBatch();
      @

      I myself haven't tried a batch insert on Qt yet. I just read it from other posts and I did something similar in Java.

      1 Reply Last reply
      0
      • C Offline
        C Offline
        ChrisW67
        wrote on last edited by
        #3

        If you are using Sqlite (you don't say) then you want to do all the inserts in a single transaction:
        @
        db.transaction();
        // one prepare() of the query
        // many thousands of inserts using bindValue() in your loop
        if (no errors)
        db.commit();
        else
        db.rollback();
        @
        http://sqlite.org/faq.html#q19

        You should definitely prepare() and bindValue(); it avoids having to think about escape illegal/dangerous characters.

        1 Reply Last reply
        0
        • J Offline
          J Offline
          jech
          wrote on last edited by
          #4

          puterk> I tried it with the same result. :-(

          ChrisW67> Yes, I'm using SQLite. This is exactly what I wanted to do, but didn't know how.

          I have one more question. In JavaScript (QML) I coud do
          @var dataStr = "INSERT INTO myTable VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
          db.transaction(function(tx) {
          tx.executeSql(dataStr, dataList);
          })
          @

          Is it possible to do something similar? All documentation on bindValue I've seen looks like this:
          @ QSqlQuery query;
          query.prepare("INSERT INTO person (id, forename, surname) "
          "VALUES (:id, :forename, :surname)");
          query.bindValue(":id", 1001);
          query.bindValue(":forename", "Bart");
          query.bindValue(":surname", "Simpson");
          query.exec();
          @

          Is there any possibility to bind a complete QStringList or QVariantList? Thank you.

          1 Reply Last reply
          0
          • mrdebugM Offline
            mrdebugM Offline
            mrdebug
            wrote on last edited by
            #5

            Maybe the solution is very easy. You have to use the transactions in this mode:

            begin
            insert into
            insert into
            insert into
            ...
            commit

            Need programmers to hire?
            www.labcsp.com
            www.denisgottardello.it
            GMT+1
            Skype: mrdebug

            1 Reply Last reply
            0
            • J Offline
              J Offline
              jech
              wrote on last edited by
              #6

              mrdebug> Could you be more specific, please? I'm quite lost between the commands of SQLite and Qt. :-( I don't need anything complicated, just to fill one table as fast as possible.

              1 Reply Last reply
              0
              • mrdebugM Offline
                mrdebugM Offline
                mrdebug
                wrote on last edited by
                #7

                @ query.exec("begin exclusive transaction;")
                query.prepare("insert into MyTable (Name, Surname) values (:Name, :Surname)");
                query.bindValue(":Name", "Ciao");
                query.bindValue(":Surname", "Ciao Ciao");
                query.exec()
                query.prepare("insert into MyTable (Name, Surname) values (:Name, :Surname)");
                query.bindValue(":Name", "Ciao");
                query.bindValue(":Surname", "Ciao Ciao");
                query.exec()
                query.prepare("insert into MyTable (Name, Surname) values (:Name, :Surname)");
                query.bindValue(":Name", "Ciao");
                query.bindValue(":Surname", "Ciao Ciao");
                query.exec()
                query.prepare("insert into MyTable (Name, Surname) values (:Name, :Surname)");
                query.bindValue(":Name", "Ciao");
                query.bindValue(":Surname", "Ciao Ciao");
                ...
                query.exec("commit;")
                @

                Need programmers to hire?
                www.labcsp.com
                www.denisgottardello.it
                GMT+1
                Skype: mrdebug

                1 Reply Last reply
                0
                • J Offline
                  J Offline
                  jech
                  wrote on last edited by
                  #8

                  mrdebug> Thanks a lot, it works perfectly.

                  1 Reply Last reply
                  0
                  • P Offline
                    P Offline
                    puterk
                    wrote on last edited by
                    #9

                    I'm just wondering. Shouldn't this have the same effect (provided that the database driver supports it)?

                    @
                    QSqlQuery query;
                    db.transaction();
                    query.prepare("INSERT INTO table1 VALUES (?,?)");

                    for (i =0; i<rows; i++)
                    {
                      variable1=column1[i];
                      query.addBindValue(variable1);
                      variable2=column2[i];  
                      query.addBindValue( variable2);
                    }
                    
                    if ( q.execBatch())
                      db.commit();
                    else
                      db.rollback();
                    

                    @

                    1 Reply Last reply
                    0
                    • C Offline
                      C Offline
                      ChrisW67
                      wrote on last edited by
                      #10

                      @
                      var dataStr = "INSERT INTO myTable VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
                      db.transaction(function(tx) {
                      tx.executeSql(dataStr, dataList);
                      })
                      @
                      becomes something like (untested):
                      @
                      bool massInsert(const QList<QStringList> &valueSets) {
                      bool success(false);
                      QSqlDatabase db = QSqlDatabase::database();
                      QSqlQuery qry(db);
                      if (qry.prepare("INSERT INTO blah VALUES(?, ?, ?, ... )")) {
                      db.transaction();
                      foreach (const QStringList &valueSet, valueSets) {
                      foreach(const QString &value, valueSet)
                      qry.addBindValue(value);
                      if (!qry.exec())
                      break;
                      }
                      if (qry.lasterror().type() == QSqlError::NoError)
                      success = db.commit();
                      else {
                      // log the error
                      db.rollback();
                      }
                      }
                      return success;
                      }
                      @
                      You could use QVariantList in place of QStringList.

                      1 Reply Last reply
                      0
                      • M Offline
                        M Offline
                        mehrdadsilver
                        wrote on last edited by
                        #11

                        if you use Mysql change engine from InoDB to ISAM :)

                        Mehrdad Abdolghafari, Be silver

                        1 Reply Last reply
                        0
                        • J Offline
                          J Offline
                          jech
                          wrote on last edited by
                          #12

                          ChrisW67> Thanks a lot, I did it like this. Of course some of the values are strings, some are float and some are integers. I pass them all as string. Does it make sense to use QVariantList instead and then convert the values to their types?

                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved