SQLite Primärschlüssel hinzufügen

99

Ich habe eine Tabelle in SQLite mithilfe der CREATE TABLE ASSyntax erstellt, um eine Tabelle basierend auf einer SELECTAnweisung zu erstellen . Jetzt hat diese Tabelle keinen Primärschlüssel, aber ich möchte einen hinzufügen.

Beim Ausführen ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)wird ein Syntaxfehler "in der Nähe von PRIMARY" ausgegeben.

Gibt es eine Möglichkeit, einen Primärschlüssel entweder während der Tabellenerstellung oder danach in Sqlite hinzuzufügen?

Mit "während der Schöpfung" meine ich während der Schöpfung mit CREATE TABLE AS.

Jack Edmonds
quelle
1
Sie können beliebige DB-Browser zum Bearbeiten der Datenbank verwenden. Sie löschen und erstellen auch die Tabellen. aber wir wollen uns nicht darum kümmern. Sie können db Browser für jedes Betriebssystem von hier herunterladen sqlitebrowser.org
vichu

Antworten:

121

Sie können SQLite-Tabellen nach ihrer Erstellung nicht wesentlich ändern. Die akzeptierte vorgeschlagene Lösung besteht darin, eine neue Tabelle mit den richtigen Anforderungen zu erstellen, Ihre Daten darin zu kopieren und dann die alte Tabelle zu löschen.

Hier ist die offizielle Dokumentation dazu: http://sqlite.org/faq.html#q11

Nathan Ridley
quelle
6
Dieser Link ( sqlite.org/omitted.html ) erklärt detaillierter, was weggelassen wurde.
Martin Velez
1
aber wir können neue Spalten hinzufügen
umesh
1
Es ist seltsam, dass Sie nach der Tabellenerstellung keine PK hinzufügen können, aber Sie können einen index ( CREATE UNIQUE INDEX pkName ON tableName(columnName)) hinzufügen, wenn DB-Frameworks wie MS SQLs SMO Sie tatsächlich dazu bringen , eine PK hinzuzufügen, nachdem die Tabelle erstellt wurde!
SteveCinq
@deFreitas Bitte geben Sie uns Ihre Weisheit. Natürlich möchten Sie, dass die Leute wissen, dass Sie die Antwort oder etwas, das einer der Kommentatoren gesagt hat, missbilligen. Ihr Kommentar enthält jedoch überhaupt keine Informationen, abgesehen von der offensichtlichen Absicht, Überlegenheit und Schnupfen zu vermitteln.
Nathan Ridley
31

Solange Sie CREATE TABLEden Primärschlüssel in einem einzelnen Feld erstellen , können Sie Folgendes verwenden:

CREATE TABLE mytable (
field1 TEXT,
field2 INTEGER PRIMARY KEY,
field3 BLOB,
);

Mit CREATE TABLEkönnen Sie auch immer den folgenden Ansatz verwenden, um einen Primärschlüssel für ein oder mehrere Felder zu erstellen :

CREATE TABLE mytable (
field1 TEXT,
field2 INTEGER,
field3 BLOB,
PRIMARY KEY (field2, field1)
);

Referenz: http://www.sqlite.org/lang_createtable.html

Diese Antwort behandelt keine Tabellenänderungen.

Scharfsinn
quelle
10

Ich habe versucht, den Primärschlüssel anschließend hinzuzufügen, indem ich die Tabelle sqlite_master direkt geändert habe. Dieser Trick scheint zu funktionieren. Es ist natürlich eine Hack-Lösung.

Kurz gesagt: Erstellen Sie einen regulären (eindeutigen) Index für die Tabelle, machen Sie das Schema beschreibbar und ändern Sie den Namen des Index in das von sqlite reservierte Formular, um einen Primärschlüsselindex zu identifizieren (dh sqlite_autoindex_XXX_1, wobei XXX der Tabellenname ist). und setzen Sie die SQL-Zeichenfolge auf NULL. Ändern Sie zuletzt die Tabellendefinition selbst. Ein Pittfal: Bei SQLite wird der Indexname erst geändert, wenn die Datenbank erneut geöffnet wird. Dies scheint ein Fehler zu sein, aber kein schwerwiegender (auch ohne erneutes Öffnen der Datenbank können Sie sie weiterhin verwenden).

Angenommen, die Tabelle sieht folgendermaßen aus:

CREATE TABLE tab1(i INTEGER, j INTEGER, t TEXT);

Dann habe ich folgendes gemacht:

BEGIN;
CREATE INDEX pk_tab1 ON tab1(i,j);
pragma writable_schema=1;
UPDATE sqlite_master SET name='sqlite_autoindex_tab1_1',sql=null WHERE name='pk_tab1';
UPDATE sqlite_master SET sql='CREATE TABLE tab1(i integer,j integer,t text,primary key(i,j))' WHERE name='tab1';
COMMIT;

Einige Tests (in SQLite-Shell):

sqlite> explain query plan select * from tab1 order by i,j;
0|0|0|SCAN TABLE tab1 USING INDEX sqlite_autoindex_tab1_1
sqlite> drop index sqlite_autoindex_tab1_1;
Error: index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped    
user3098421
quelle
2
Nur eine Warnung, dass Sie (soweit ich das beurteilen kann) Ihre gesamte Datenbank unzugänglich machen können, wenn Sie dies falsch machen. Ich habe herumgespielt und versehentlich die WHERE-Klausel in der zweiten Update-Abfrage übersehen. SQLite gefiel das nicht: P
Andrew Magee
7

Gemäß den SQLite- Dokumenten zur Tabellenerstellung wird durch die Verwendung der Tabelle zum Erstellen als Auswahl eine neue Tabelle ohne Einschränkungen und ohne Primärschlüssel erstellt.

In der Dokumentation heißt es jedoch auch, dass Primärschlüssel und eindeutige Indizes logisch äquivalent sind ( siehe Abschnitt Einschränkungen ):

In den meisten Fällen werden UNIQUE- und PRIMARY KEY-Einschränkungen implementiert, indem ein eindeutiger Index in der Datenbank erstellt wird. (Die Ausnahmen sind INTEGER PRIMARY KEY und PRIMARY KEYs für WITHOUT ROWID-Tabellen.) Daher sind die folgenden Schemata logisch äquivalent:

CREATE TABLE t1(a, b UNIQUE);

CREATE TABLE t1(a, b PRIMARY KEY);

CREATE TABLE t1(a, b);
CREATE UNIQUE INDEX t1b ON t1(b); 

Selbst wenn Sie Ihre Tabellendefinition nicht über die SQL-Änderungssyntax ändern können, können Sie durch die Verwendung eines eindeutigen Index denselben Primärschlüsseleffekt erzielen.

Außerdem hat jede Tabelle (mit Ausnahme derjenigen, die ohne die Rowid-Syntax erstellt wurden) eine innere Ganzzahlspalte, die als "Rowid" bezeichnet wird. Gemäß den Dokumenten können Sie diese innere Spalte verwenden, um Datensatztabellen abzurufen / zu ändern.

Carlito's Weg
quelle
Wenn Sie EntityFramework verwenden, um eine Verbindung zu Ihrer Datenbank herzustellen, wird ein eindeutiger Index nicht als Primärschlüssel erkannt. Während es in SQLite logisch und funktional äquivalent ist, ist es nicht überall gleich.
Kristen Hammack
5

Sie können es so machen:

CREATE TABLE mytable (
field1 text,
field2 text,
field3 integer,
PRIMARY KEY (field1, field2)
);
Nick Dandoulakis
quelle
3

Einführung

Dies basiert auf Android Java und ist ein gutes Beispiel für das Ändern der Datenbank, ohne Ihre Anwendungsfans / Kunden zu stören. Dies basiert auf der Idee der SQLite-FAQ-Seite http://sqlite.org/faq.html#q11

Das Problem

Ich habe nicht bemerkt, dass ich eine Zeilennummer oder eine Datensatz-ID festlegen muss, um einen einzelnen gekauften Artikel in einer Quittung zu löschen, und gleichzeitig hat mich die Artikel-Barcode-Nummer dazu gebracht, darüber nachzudenken, ihn als Schlüssel zum Löschen dieses Artikels zu verwenden. Ich speichere eine Belegdetails in der Tabelle Receipt_Barcode. Wenn Sie es ohne record_id belassen, können Sie alle Datensätze desselben Artikels in einer Quittung löschen, wenn ich den Artikel-Barcode als Schlüssel verwendet habe.

Beachten

Bitte haben Sie Verständnis dafür, dass dies eine Kopie meines Codes ist, an dem ich zum Zeitpunkt des Schreibens arbeite. Verwenden Sie es nur als Beispiel. Das zufällige Kopieren und Einfügen hilft Ihnen nicht weiter. Ändern Sie dies zuerst an Ihre Bedürfnisse

Bitte vergessen Sie auch nicht, die Kommentare im Code zu lesen.

Der Code

Verwenden Sie dies als Methode in Ihrer Klasse, um zunächst zu überprüfen, ob die Spalte, die Sie hinzufügen möchten, fehlt. Wir tun dies nur, um den Vorgang des Änderns der Tabelle Receipt_Barcode nicht zu wiederholen. Erwähne es einfach als Teil deiner Klasse. Im nächsten Schritt werden Sie sehen, wie wir es verwenden werden.

public boolean is_column_exists(SQLiteDatabase mDatabase , String table_name,
String     column_name) {
    //checks if table_name has column_name
    Cursor cursor = mDatabase.rawQuery("pragma table_info("+table_name+")",null);
    while (cursor.moveToNext()){
    if (cursor.getString(cursor.getColumnIndex("name")).equalsIgnoreCase(column_name)) return true;
    }
    return false;
}

Der folgende Code wird dann verwendet, um die Tabelle Receipt_Barcode zu erstellen, wenn sie bereits zum ersten Mal von Benutzern Ihrer App NICHT beendet wird . Und bitte beachten Sie das "WENN NICHT EXISTIERT" im Code. Es hat Bedeutung.

//mDatabase should be defined as a Class member (global variable) 
//for ease of access : 
//SQLiteDatabse mDatabase=SQLiteDatabase.openOrCreateDatabase(dbfile_path, null);
creation_query = " CREATE TABLE if not exists receipt_barcode ( ";
creation_query += "\n record_id        INTEGER PRIMARY KEY AUTOINCREMENT,";
creation_query += "\n rcpt_id INT( 11 )       NOT NULL,";
creation_query += "\n barcode VARCHAR( 255 )  NOT NULL ,";
creation_query += "\n barcode_price VARCHAR( 255 )  DEFAULT (0),";
creation_query += "\n PRIMARY KEY ( record_id ) );";
mDatabase.execSQL(creation_query);

//This is where the important part comes in regarding the question in this page:

//adding the missing primary key record_id in table receipt_barcode for older versions
        if (!is_column_exists(mDatabase, "receipt_barcode","record_id")){
            mDatabase.beginTransaction();
            try{
                Log.e("record_id", "creating");


                 creation_query="CREATE TEMPORARY TABLE t1_backup(";
                 creation_query+="record_id INTEGER        PRIMARY KEY AUTOINCREMENT,";
                 creation_query+="rcpt_id INT( 11 )       NOT NULL,";
                 creation_query+="barcode VARCHAR( 255 )  NOT NULL ,";
                 creation_query+="barcode_price VARCHAR( 255 )  NOT NULL DEFAULT (0) );";
                 mDatabase.execSQL(creation_query);

                 creation_query="INSERT INTO t1_backup(rcpt_id,barcode,barcode_price) SELECT rcpt_id,barcode,barcode_price  FROM receipt_barcode;";
                 mDatabase.execSQL(creation_query);

                 creation_query="DROP TABLE receipt_barcode;";
                 mDatabase.execSQL(creation_query);

                 creation_query="CREATE TABLE receipt_barcode (";
                 creation_query+="record_id INTEGER        PRIMARY KEY AUTOINCREMENT,";
                 creation_query+="rcpt_id INT( 11 )       NOT NULL,";
                 creation_query+="barcode VARCHAR( 255 )  NOT NULL ,";
                 creation_query+="barcode_price VARCHAR( 255 )  NOT NULL DEFAULT (0) );";
                 mDatabase.execSQL(creation_query);

                 creation_query="INSERT INTO receipt_barcode(record_id,rcpt_id,barcode,barcode_price) SELECT record_id,rcpt_id,barcode,barcode_price  FROM t1_backup;";
                 mDatabase.execSQL(creation_query);

                 creation_query="DROP TABLE t1_backup;";
                 mDatabase.execSQL(creation_query);


                 mdb.setTransactionSuccessful();
            } catch (Exception exception ){
                Log.e("table receipt_bracode", "Table receipt_barcode did not get a primary key (record_id");
                exception.printStackTrace();
            } finally {
                 mDatabase.endTransaction();
            }
Superlinux
quelle
1

Ich hatte das gleiche Problem und die beste Lösung, die ich gefunden habe, besteht darin, zuerst die Tabelle zu erstellen, die den Primärschlüssel definiert, und dann insert in Anweisung zu verwenden.

CREATE TABLE mytable (
field1 INTEGER PRIMARY KEY,
field2 TEXT
);

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;
Spas
quelle
schlechte Idee für
Masseneinsätze
0

Ich habe die CREATE TABLE AS-Syntax verwendet, um mehrere Spalten zusammenzuführen, und bin auf dasselbe Problem gestoßen. Hier ist ein AppleScript, das ich geschrieben habe, um den Prozess zu beschleunigen.

set databasePath to "~/Documents/Databases/example.db"
set tableOne to "separate" -- Table from which you are pulling data
set tableTwo to "merged" -- Table you are creating
set {tempCol, tempColEntry, permColEntry} to {{}, {}, {}}
set permCol to {"id integer primary key"}

-- Columns are created from single items  AND from the last item of a list
-- {{"a", "b", "c"}, "d", "e"} Columns "a" and "b" will be merged into a new column "c".  tableTwo will have columns "c", "d", "e"

set nonCoal to {"City", "Contact", "Names", {"Address 1", "Address", "address one", "Address1", "Text4", "Address 1"}, {"E-Mail", "E-Mail Address", "Email", "Email Address", "EmailAddress", "Email"}, {"Zip", "Zip Code", "ZipCode", "Zip"}, {"Telephone", "BusinessPhone", "Phone", "Work Phone", "Telephone"}, {"St", "State", "State"}, {"Salutation", "Mr/Ms", "Mr/s", "Salutations", "Sautation", "Salutation"}}

-- Build the COALESCE statements
repeat with h from 1 to count of nonCoal
set aColumn to item h of nonCoal
if class of aColumn is not list then
    if (count of words of aColumn) > 1 then set aColumn to quote & aColumn & quote
    set end of tempCol to aColumn
    set end of permCol to aColumn
else
    set coalEntry to {}
    repeat with i from 1 to count of aColumn
        set coalCol to item i of aColumn as string
        if (count of words of coalCol) > 1 then set coalCol to quote & coalCol & quote
        if i = 1 then
            set end of coalEntry to "TRIM(COALESCE(" & coalCol & ", '') || \" \" || "
        else if i < ((count of aColumn) - 1) then
            set end of coalEntry to "COALESCE(" & coalCol & ", '') || \" \" || "
        else if i = ((count of aColumn) - 1) then
            set as_Col to item (i + 1) of aColumn as string
            if (count of words of as_Col) > 1 then set as_Col to quote & as_Col & quote
            set end of coalEntry to ("COALESCE(" & coalCol & ", '')) AS " & as_Col) & ""
            set end of permCol to as_Col
        end if
    end repeat
    set end of tempCol to (coalEntry as string)
end if
end repeat

-- Since there are ", '' within the COALESCE statement, you can't use "TID" and "as string" to convert tempCol and permCol for entry into sqlite3. I rebuild the lists in the next block.
repeat with j from 1 to count of tempCol
if j < (count of tempCol) then
    set end of tempColEntry to item j of tempCol & ", "
    set end of permColEntry to item j of permCol & ", "
else
    set end of tempColEntry to item j of tempCol
    set end of permColEntry to item j of permCol
end if
end repeat
set end of permColEntry to ", " & item (j + 1) of permCol
set permColEntry to (permColEntry as string)
set tempColEntry to (tempColEntry as string)

-- Create the new table with an "id integer primary key" column
set createTable to "create table " & tableTwo & " (" & permColEntry & "); "
do shell script "sqlite3 " & databasePath & space & quoted form of createTable

-- Create a temporary table and then populate the permanent table
set createTemp to "create temp table placeholder as select " & tempColEntry & " from " & tableOne & ";  " & "insert into " & tableTwo & " select Null, * from placeholder;"
do shell script "sqlite3 " & databasePath & space & quoted form of createTemp

--export the new table as a .csv file
do shell script "sqlite3 -header -column -csv " & databasePath & " \"select * from " & tableTwo & " ; \"> ~/" & tableTwo & ".csv"
adayzdone
quelle
0

Ich denke, das Hinzufügen eines Index für diese Spalte kann fast den gleichen Effekt erzielen.

Qiulang
quelle
0
sqlite>  create table t(id int, col2 varchar(32), col3 varchar(8));
sqlite>  insert into t values(1, 'he', 'ha');
sqlite>
sqlite>  create table t2(id int primary key, col2 varchar(32), col3 varchar(8));
sqlite>  insert into t2 select * from t;
sqlite> .schema
CREATE TABLE t(id int, col2 varchar(32), col3 varchar(8));
CREATE TABLE t2(id int primary key, col2 varchar(32), col3 varchar(8));
sqlite> drop table t;
sqlite> alter table t2 rename to t;
sqlite> .schema
CREATE TABLE IF NOT EXISTS "t"(id int primary key, col2 varchar(32), col3 varchar(8));
sqlite>
Sanshaoye
quelle
0

Verwenden Sie ein Tool wie DB Browser für SQLite. Es ermöglicht das Hinzufügen von PK, AI durch einfaches Klicken mit der rechten Maustaste auf Tabelle -> Ändern.

codeapply
quelle