SQLite
 sql >> Datenbank >  >> RDS >> SQLite

Android Room – So setzen Sie den Primärschlüssel der automatisch generierten Tabelle bei jedem App-Lauf zurück

Um die Tabellen beim Beenden zu löschen, setzt dies jedoch nicht den Keystarting-Index zurück, sondern beginnt dort, wo er beim letzten Lauf aufgehört hat.

....

"delete from sqlite_sequence where name='Sequence Action'" Kein Fehler, aber der Index wird auch nicht zurückgesetzt.

Sie müssen alle Zeilen in der SequenceAction löschen table UND lösche die entsprechende Zeile aus sqlite_sequence.

Das heißt, wenn das Schlüsselwort AUTOINCREMENT verwendet wird, wird ein anderer Algorithmus verwendet. Dies ist in etwa so:-

Finden Sie den höchsten Wert von entweder a) dem Wertspeicher für die Tabelle in der sqlite_sequence-Nummer und b) dem höchsten rowid-Wert

Eine Alternative wäre, das AUTOINCREMENT nicht zu verwenden Schlüsselwort, anstatt nur ?? INTEGER PRIMARY KEY (wobei ?? den Spaltennamen darstellt).

Sie hätten immer noch eine eindeutige ID, die ein Alias ​​der rowid ist koulmn, aber es gibt keine Garantie dafür, dass sie immer steigen würde. AUTOINCREMENT garantiert eine ansteigende eindeutige ID, aber keine monoton ansteigende eindeutige Rowid.

Bei jedem Anwendungslauf brauche ich diesen Schlüssel, um bei 0 zu beginnen.

SQLite setzt jedoch den ersten Wert auf 1 und nicht auf 0.

Folgendes funktioniert, und wie Sie sehen, mit AUTOINCREMENT (wenn auch ein bisschen ein Hack) :-

DROP TABLE IF EXISTS SequenceAction;
DROP TRIGGER IF EXISTS use_zero_as_first_sequence;
CREATE TABLE IF NOT EXISTS SequenceAction (id INTEGER PRIMARY KEY AUTOINCREMENT, otherdata TEXT);
CREATE TRIGGER IF NOT EXISTS use_zero_as_first_sequence AFTER INSERT ON SequenceAction
    BEGIN 
        UPDATE SequenceAction SET id = id - 1 WHERE id = new.id;
    END
;
INSERT INTO SequenceAction VALUES(null,'TEST1'),(null,'TEST2'),(null,'TEST3');
SELECT * FROM SequenceAction;
-- RESET and RESTART FROM 0
DELETE FROM SequenceAction;
DELETE FROM sqlite_sequence WHERE name = 'SequenceAction';
INSERT INTO SequenceAction VALUES(null,'TEST4'),(null,'TEST5'),(null,'TEST6');
SELECT * FROM SequenceAction
  • Die 2 DROP-Anweisungen, die nur zum Testen zum Löschen und Neudefinieren erforderlich sind.

Dies ergibt :-

Die erste Abfrage, die :-

zurückgibt

und die 2. Rückkehr :-

Im Wesentlichen wollen Sie also :-

DELETE FROM SequenceAction;
DELETE FROM sqlite_sequence WHERE name = 'SequenceAction';

Und auch der Auslöser, wenn Sie möchten, dass die Nummerierung bei 0 statt bei 1 beginnt.

Wenn Sie AUTOINCREMENT weglassen, könnten Sie alternativ einen leicht veränderten Trigger verwenden:-

CREATE TRIGGER IF NOT EXISTS use_zero_as_first_sequence 
    AFTER INSERT ON SequenceAction 
    WHEN (SELECT count() FROM SequenceAction) = 1
    BEGIN 
        UPDATE SequenceAction SET id = 0;
    END
;
  • Dies nummeriert nur die allererste eingefügte Zeile neu (der Algorithmus fügt dann 1 für nachfolgende Einfügungen hinzu)

Und dann löschen Sie einfach alle Zeilen nur aus der SequenceAction-Tabelle, um die Nummerierung zurückzusetzen.

Beispiel mit Raum:-

Basierend auf Ihrem Code zusammen mit dem obigen Beispiel scheint die folgende Methode zu funktionieren:-

private void resetSequenceAction() {
    SQLiteDatabase dbx;
    String sqlite_sequence_table = "sqlite_sequence";
    long initial_sacount;
    long post_sacount;
    long initial_ssn =0;
    long post_ssn = 0;
    Cursor csr;

    /*
        Need to Create Database and table if it doesn't exist
     */
    File f = this.getDatabasePath(TestDatabase.DBNAME);
    if (!f.exists()) {
        File d = new File(this.getDatabasePath(TestDatabase.DBNAME).getParent());
        d.mkdirs();
        dbx = SQLiteDatabase.openOrCreateDatabase(f,null);
        String crtsql = "CREATE TABLE IF NOT EXISTS " + SequenceAction.tablename + "(" +
                SequenceAction.id_column + " INTEGER PRIMARY KEY AUTOINCREMENT," +
                SequenceAction.actionType_column + " TEXT," +
                SequenceAction.extraInfo_column + " TEXT" +
                ")";
        dbx.execSQL(crtsql);
        /*
           Might as well create the Trigger as well
         */
        String triggerSql = "CREATE TRIGGER IF NOT EXISTS user_zero_as_first_rowid AFTER INSERT ON " +
                SequenceAction.tablename +
                " BEGIN " +
                " UPDATE " + SequenceAction.tablename +
                " SET " +
                SequenceAction.id_column + " = " + SequenceAction.id_column + " - 1 " +
                " WHERE " + SequenceAction.id_column + " = new." + SequenceAction.id_column + ";" +
                " END ";
        dbx.execSQL(triggerSql);

    } else {
        dbx = SQLiteDatabase.openDatabase(this.getDatabasePath(TestDatabase.DBNAME).getPath(),null, Context.MODE_PRIVATE);
    }

    /*
        Add trigger to set id's to 1 less than they were set to
     */
    initial_sacount = DatabaseUtils.queryNumEntries(dbx,SequenceAction.tablename);
    /*
        Delete all the rows at startup
     */
    String deleteAllSequenceIdRowsSql = "DELETE FROM " + SequenceAction.tablename;
    dbx.execSQL(deleteAllSequenceIdRowsSql);
    post_sacount = DatabaseUtils.queryNumEntries(dbx,SequenceAction.tablename);
    /*
        delete the sequence row from the sqlite_sequence table
     */
    csr = dbx.query(sqlite_sequence_table,
            new String[]{"seq"},"name=?",
            new String[]{SequenceAction.tablename},
            null,null,null
    );
    if (csr.moveToFirst()) {
        initial_ssn = csr.getLong(csr.getColumnIndex("seq"));
    }
    String deleteSqlLiteSequenceRow = "DELETE FROM " +
            sqlite_sequence_table +
            " WHERE name = '" + SequenceAction.tablename + "'";
    dbx.execSQL(deleteSqlLiteSequenceRow);
    csr = dbx.query(
            sqlite_sequence_table,
            new String[]{"seq"},
            "name=?",
            new String[]{SequenceAction.tablename},
            null,null,null
    );
    if (csr.moveToFirst()) {
        post_ssn = csr.getLong(csr.getColumnIndex("seq"));
    }
    csr.close();
    Log.d("SEQACTSTATS",
            "Initial Rowcount=" + String.valueOf(initial_sacount) +
                    " Initial Seq#=" + String.valueOf(initial_ssn) +
                    " Post Delete Rowcount =" + String.valueOf(post_sacount) +
                    " Post Delete Seq#=" + String.valueOf(post_ssn)
    );
    dbx.close();
}

Ergebnis eines ersten Laufs (d. h. kein DB vorhanden) :-

D/SEQACTSTATS: Initial Rowcount=0 Initial Seq#=0 Post Delete Rowcount =0 Post Delete Seq#=0

Aus einem nachfolgenden Durchlauf (nachdem 40 Zeilen hinzugefügt wurden):-

D/SEQACTSTATS: Initial Rowcount=40 Initial Seq#=40 Post Delete Rowcount =0 Post Delete Seq#=0

Hinzufügen einer Methode zum Auflisten aller Zeilen gemäß :-

private void listAllRows() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            salist = mTestDB.SequenceActionDaoAccess().getAll();
            getSequenceActionList(salist);
        }
    }).start();
}

Zusammen mit :-

@Override
public void getSequenceActionList(List<SequenceAction> sequenceActionList) {
    for (SequenceAction sa: sequenceActionList) {
        Log.d("SA","ID=" + String.valueOf(sa.getSequenceId()) + " AT=" + sa.getActionType() + " EI=" + sa.getExtraInfo());
    }
}

Ergebnisse in (erste Zeile ist ID=0 AT=X0 EI=Y0 also die ID Spalte der ersten Zeile ist 0 ):-

06-17 02:56:47.867 5526-5554/rt_mjt.roomtest D/SA: ID=0 AT=X0 EI=Y0
    ID=1 AT=X0 EI=Y0
    ID=2 AT=X0 EI=Y0
    ID=3 AT=X0 EI=Y0
    ID=4 AT=X1 EI=Y1
    ID=5 AT=X1 EI=Y1
    ID=6 AT=X1 EI=Y1
    ID=7 AT=X1 EI=Y1
06-17 02:56:47.868 5526-5554/rt_mjt.roomtest D/SA: ID=8 AT=X2 EI=Y2
    ID=9 AT=X2 EI=Y2
    ID=10 AT=X2 EI=Y2
    ID=11 AT=X2 EI=Y2
    ID=12 AT=X3 EI=Y3
    ID=13 AT=X3 EI=Y3
    ID=14 AT=X3 EI=Y3
    ID=15 AT=X3 EI=Y3
    ID=16 AT=X4 EI=Y4
06-17 02:56:47.869 5526-5554/rt_mjt.roomtest D/SA: ID=17 AT=X4 EI=Y4
    ID=18 AT=X4 EI=Y4
    ID=19 AT=X4 EI=Y4
    ID=20 AT=X5 EI=Y5
    ID=21 AT=X5 EI=Y5
    ID=22 AT=X5 EI=Y5
    ID=23 AT=X5 EI=Y5
    ID=24 AT=X6 EI=Y6
    ID=25 AT=X6 EI=Y6
    ID=26 AT=X6 EI=Y6
    ID=27 AT=X6 EI=Y6
06-17 02:56:47.870 5526-5554/rt_mjt.roomtest D/SA: ID=28 AT=X7 EI=Y7
    ID=29 AT=X7 EI=Y7
    ID=30 AT=X7 EI=Y7
    ID=31 AT=X7 EI=Y7
    ID=32 AT=X8 EI=Y8
    ID=33 AT=X8 EI=Y8
    ID=34 AT=X8 EI=Y8
    ID=35 AT=X8 EI=Y8
    ID=36 AT=X9 EI=Y9
    ID=37 AT=X9 EI=Y9
    ID=38 AT=X9 EI=Y9
    ID=39 AT=X9 EI=Y9
  • Beachten Sie, dass die Ergebnisse aufgrund mehrerer Threads, die ohne Steuerung/Sequenzierung ausgeführt werden, seltsam sein können.

Die addSomeData verwendete Methode ist:-

private void addSomeData() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            SequenceAction sa = new SequenceAction();
            for (int i=0; i < 10; i++) {
                sa.setSequenceId(0);
                sa.setActionType("X" + String.valueOf(i));
                sa.setExtraInfo("Y" + String.valueOf(i));
                mTestDB.SequenceActionDaoAccess().insertSingleRow(sa);
            }
        }
    }) .start();
}

Zusatz zu Kommentaren :-

"Ich glaube, Sie müssen vor Room reinkommen ..." - meinen Sie, dass Sie das SQL ausführen, das den laufenden Index löscht, bevor die Roomdatabase instanziiert wird? - gott

nicht unbedingt, aber bevor Room die Datenbank öffnet, bevor Sie versuchen, irgendetwas damit zu tun. –MikeT

Hier ist ein Beispiel für den Aufruf der resetSequenceAction-Methode, nachdem die RoomDatabase instanziiert wurde, aber bevor sie zum Zugreifen auf/Öffnen der Datenbank verwendet wird (addSomeData öffnet die bereits instanziierte Datenbank und fügt 10 Zeilen ein):-

@Override
protected void onStart() {
    super.onStart();
    mTestDB = Room.databaseBuilder(this,TestDatabase.class,TestDatabase.DBNAME).build(); //<<<< Room DB instantiated
    resetSequenceAction(); //<<<< reset the sequence (adding trigger if needed)
    addSomeData(); // This will be the first access open
    addSomeData();
    addSomeData();
    addSomeData();
    listAllRows();