C SQLite Anzahl Ergebnisse.

asdfman

Commander
Registriert
März 2008
Beiträge
2.315
Ich bastel im Moment ein Projekt mit SQLite. Aus meiner Datenbank frage ich zuerst nach
Einer Auswahl von Monstern die im aktuellen Level auftauchen können und wähle dann
daraus (noch nicht implementiert) diejenigen Monster aus, die letztendlich in das Level
gesetzt werden:

Code:
void selectmobs(int dlvl, sqlite3 *db) {
    /* int maxsize = 4000; */
    int i, j, ret, row, lastrow = 0;
    int group[32], size[32];
    sqlite3_stmt *statement;

    sqlite3_prepare(db, "SELECT grp FROM mobs WHERE"\
        "((dlvlmin <= :dlvl) AND (dlvlmax >= :dlvl)) ORDER BY grp;",
        BUFSIZE, &statement, NULL);

    sqlite3_bind_int(statement, 1, dlvl);

    i = 0;
    do {
        ret = sqlite3_step(statement);
        if(ret == SQLITE_ROW) {
            row = sqlite3_column_int(statement, 0);
            if(row != lastrow) {
                group[i++] = row;
                lastrow = row;
            }
        }
    } while(ret != SQLITE_DONE);

    sqlite3_finalize(statement);

    sqlite3_prepare(db, "SELECT size FROM mobgroups WHERE id = :grp;",
        BUFSIZE, &statement, NULL);

    for(j = 0; j < i; j++) {
        sqlite3_bind_int(statement, 1, group[j]);
        do {
            ret = sqlite3_step(statement);
            if( ret == SQLITE_ROW) {
                size[j] = sqlite3_column_int(statement, 0);
                printf("size[%d] = %d\n", group[j], size[j]);
            }
        } while(ret != SQLITE_DONE);
        sqlite3_reset(statement);
    }

    sqlite3_finalize(statement);
}

Das Problem liegt bei "int group[32], size[32];". Bei der Datenbank, wie sie im Moment
aussieht, können nicht mehr als 32 Monsterklassen ausgewählt werden. Mir wäre es aber
wesentlich lieber, SQLite zu fragen, wie viele Ergebnisse die Abfrage liefern wird, so dass
ich die beiden Arrays dynamisch bemessen kann. Geht das?

Davon abgesehen: Ich bin auch alles andere als ein SQL Experte. Also, wenn meine State-
ments scheiße sind, sagt das auch, damit ichs besser machen kann.
 
Das sieht sehr gut aus. Melde mich ggf. zurück, falls das nicht funktioniert.

€: Ging. Dachte, ich müsste die Bibliothek selbst fragen. Gut, dass es dafür einen SQL-Befehl gibt.
Ergänzung ()

Oh, ich hatte recht! Man kann die Lib fragen. So ists gleich viel schöner:
Code:
void selectmobs(int dlvl, sqlite3 *db) {
    /* int maxsize = 4000; */
    char sql[BUFSIZE], **table;
    int i, j, ret;
    int *group, *size;
    sqlite3_stmt *statement;

    sprintf(sql, "SELECT DISTINCT grp FROM mobs WHERE"\
        "((dlvlmin <= %d) AND (dlvlmax >= %d)) ORDER BY GRP;",
        dlvl, dlvl);
    sqlite3_get_table(db, sql, &table, &i, NULL, NULL);
    sqlite3_free_table(table);

    if((group = malloc(i * sizeof(int))) == NULL) {
        fprintf(stderr, "ERROR: malloc() failed.\n");
        return;
    }

    sqlite3_prepare(db, sql, BUFSIZE, &statement, NULL);
    sqlite3_bind_int(statement, 1, dlvl);

    i = 0;
    do {
        ret = sqlite3_step(statement);
        if(ret == SQLITE_ROW)
            group[i++] = sqlite3_column_int(statement, 0);
    } while(ret != SQLITE_DONE);
    sqlite3_finalize(statement);

    if((size = malloc(i * sizeof(int))) == NULL) {
        fprintf(stderr, "ERROR: malloc() failed.\n");
        free(group);
        return;
    }

    sqlite3_prepare(db, "SELECT size FROM mobgroups WHERE id = :grp;",
        BUFSIZE, &statement, NULL);
    for(j = 0; j < i; j++) {
        sqlite3_bind_int(statement, 1, group[j]);
        do {
            ret = sqlite3_step(statement);
            if( ret == SQLITE_ROW) {
                size[j] = sqlite3_column_int(statement, 0);
                printf("size[%d] = %d\n", group[j], size[j]);
            }
        } while(ret != SQLITE_DONE);
        sqlite3_reset(statement);
    }
    sqlite3_finalize(statement);
    free(group);
    free(size);
}
 
Zuletzt bearbeitet:
sqlite3_get_table liefert dir aber auch das Ergebnis gleich mit. Das könntest du hier direkt benutzen, anstatt nochmal eine neue Query zu starten.
Ich glaube, der "richtige" Ansatz wäre gewesen, einfach ein dynamisches Array zu benutzen. Wenn man sich den Quellcode von sqlite3_get_table ansieht, machen die das dort ebenfalls so.
 
Auch du hast recht. Das verkürzt die Sache natürlich nochmal. Nun meint aber jemand im IRC, das wäre nicht
atomic und deshalb anfällig für eine race condition :(

Code:
void selectmobs(int dlvl, sqlite3 *db) {
    /* int maxsize = 4000; */
    char sql[BUFSIZE], **table;
    int i, j, ret;
    int *group, *size;
    sqlite3_stmt *statement;

    sprintf(sql, "SELECT DISTINCT grp FROM mobs WHERE"\
        "((dlvlmin <= %d) AND (dlvlmax >= %d)) ORDER BY GRP;",
        dlvl, dlvl);
    sqlite3_get_table(db, sql, &table, &i, NULL, NULL);

    if((group = malloc(i * sizeof(int))) == NULL) {
        fprintf(stderr, "ERROR: malloc() failed.\n");
        return;
    }

    for(j = 0; j < i; j++)
        group[j] = atoi(table[j + 1]);
    sqlite3_free_table(table);

    if((size = malloc(i * sizeof(int))) == NULL) {
        fprintf(stderr, "ERROR: malloc() failed.\n");
        free(group);
        return;
    }

    sqlite3_prepare(db, "SELECT size FROM mobgroups WHERE id = :grp;",
        BUFSIZE, &statement, NULL);
    for(j = 0; j < i; j++) {
        sqlite3_bind_int(statement, 1, group[j]);
        do {
            ret = sqlite3_step(statement);
            if( ret == SQLITE_ROW) {
                size[j] = sqlite3_column_int(statement, 0);
                printf("size[%d] = %d\n", group[j], size[j]);
            }
        } while(ret != SQLITE_DONE);
        sqlite3_reset(statement);
    }
    sqlite3_finalize(statement);
    free(group);
    free(size);
}
 
Du meinst, dass die beiden SQL-Statements inkonsistente Ergebnisse liefern können, falls zwischendrin die DB geändert wird? Das ließe sich lösen, indem du beides in eine Transaktion packst. Dann musst du nur sicherstellen, dass auch alle Updates beider Tables innerhalb einer Transaktion stattfinden.
 
Zurück
Oben