SQLite aggiunge la chiave primaria


100

Ho creato una tabella in Sqlite utilizzando la CREATE TABLE ASsintassi per creare una tabella basata su SELECTun'istruzione. Ora questa tabella non ha una chiave primaria ma vorrei aggiungerne una.

L'esecuzione ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)restituisce un errore di sintassi "near PRIMARY"

C'è un modo per aggiungere una chiave primaria durante la creazione della tabella o successivamente in Sqlite?

Con "durante la creazione" intendo durante la creazione con CREATE TABLE AS.


1
puoi usare qualsiasi browser db per modificare il database. Inoltre stanno eliminando e creando le tabelle. ma non vogliamo preoccuparcene. puoi scaricare il browser db per qualsiasi sistema operativo da qui sqlitebrowser.org
vichu

Risposte:


122

Non è possibile modificare le tabelle SQLite in modo significativo dopo che sono state create. La soluzione suggerita accettata è creare una nuova tabella con i requisiti corretti e copiare i dati in essa, quindi rilasciare la vecchia tabella.

ecco la documentazione ufficiale su questo: http://sqlite.org/faq.html#q11


7
Questo collegamento ( sqlite.org/omitted.html ) spiega cosa è stato omesso in modo più dettagliato.
Martin Velez

1
ma possiamo aggiungere nuove colonne
umesh


1
È strano che tu non possa aggiungere un PK dopo la creazione della tabella, ma puoi aggiungere un index ( CREATE UNIQUE INDEX pkName ON tableName(columnName)) quando i framework DB come SMO di MS SQL ti fanno effettivamente aggiungere un PK dopo che la tabella è stata creata!
SteveCinq

@deFreitas Per favore, concedici la tua saggezza. Chiaramente vuoi che le persone sappiano che disapprovi la risposta o qualcosa che ha detto uno dei commentatori, tuttavia il tuo commento non contiene alcuna informazione, a parte un apparente intento di trasmettere superiorità e sarcastico.
Nathan Ridley

31

Finché stai usando CREATE TABLE, se stai creando la chiave primaria su un singolo campo , puoi usare:

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

Con CREATE TABLE, puoi anche utilizzare sempre il seguente approccio per creare una chiave primaria su uno o più campi :

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

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

Questa risposta non affronta l'alterazione della tabella.


10

Ho provato ad aggiungere la chiave primaria in seguito modificando direttamente la tabella sqlite_master. Questo trucco sembra funzionare. Ovviamente è una soluzione hack.

In breve: crea un indice regolare (univoco) sulla tabella, quindi rendi lo schema scrivibile e cambia il nome dell'indice nel modulo riservato da sqlite per identificare un indice di chiave primaria, (cioè sqlite_autoindex_XXX_1, dove XXX è il nome della tabella) e imposta la stringa sql su NULL. Infine cambia la definizione della tabella stessa. Un problema: sqlite non vede la modifica del nome dell'indice fino alla riapertura del database. Sembra un bug, ma non grave (anche senza riaprire il database, puoi comunque usarlo).

Supponiamo che la tabella abbia questo aspetto:

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

Poi ho fatto quanto segue:

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;

Alcuni test (nella shell sqlite):

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    

2
Solo un avvertimento che puoi (per quanto ne so) rendere inaccessibile l'intero database se lo fai male. Stavo giocando e ho perso accidentalmente la clausola WHERE nella seconda query di aggiornamento. A SQLite non è piaciuto: P
Andrew Magee

7

Secondo la documentazione di sqlite sulla creazione di tabelle, l'utilizzo di create table as select produce una nuova tabella senza vincoli e senza chiave primaria.

Tuttavia, la documentazione dice anche che le chiavi primarie e gli indici univoci sono logicamente equivalenti ( vedere la sezione sui vincoli ):

Nella maggior parte dei casi, i vincoli UNIQUE e PRIMARY KEY vengono implementati creando un indice univoco nel database. (Le eccezioni sono INTEGER PRIMARY KEY e PRIMARY KEY sulle tabelle SENZA ROWID.) Pertanto, gli schemi seguenti sono logicamente equivalenti:

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); 

Quindi, anche se non è possibile modificare la definizione della tabella tramite la sintassi di alterazione SQL, è possibile ottenere lo stesso effetto della chiave primaria utilizzando un indice univoco.

Inoltre, qualsiasi tabella (eccetto quelle create senza la sintassi rowid) ha una colonna intera interna nota come "rowid". Secondo i documenti, è possibile utilizzare questa colonna interna per recuperare / modificare le tabelle dei record.


Se stai usando EntityFramework per connetterti al tuo database, non riconosce un indice univoco come chiave primaria. Quindi, sebbene sia logicamente e funzionalmente equivalente all'interno di SQLite, non è del tutto equivalente ovunque.
Kristen Hammack

5

Puoi farlo in questo modo:

CREATE TABLE mytable (
field1 text,
field2 text,
field3 integer,
PRIMARY KEY (field1, field2)
);

3

introduzione

Si basa su java di Android ed è un buon esempio per modificare il database senza infastidire i fan / clienti dell'applicazione. Questo si basa sull'idea della pagina delle domande frequenti su SQLite http://sqlite.org/faq.html#q11

Il problema

Non ho notato che dovevo impostare un row_number o record_id per eliminare un singolo articolo acquistato in una ricevuta, e allo stesso tempo il numero del codice a barre dell'articolo mi ha indotto a pensare di utilizzarlo come chiave per eliminare quell'articolo. Sto salvando i dettagli di una ricevuta nella tabella Receiver_barcode. Lasciarlo senza record_id può significare l'eliminazione di tutti i record dello stesso articolo in una ricevuta se ho utilizzato il codice a barre dell'articolo come chiave.

Avviso

Ti preghiamo di comprendere che questo è un copia-incolla del mio codice su cui sto lavorando al momento della stesura di questo documento. Usalo solo come esempio, il copia-incolla in modo casuale non ti aiuterà. Modificare prima questo in base alle proprie esigenze

Inoltre, non dimenticare di leggere i commenti nel codice.

Il codice

Usa questo come metodo nella tua classe per verificare prima se la colonna che desideri aggiungere è mancante. Lo facciamo solo per non ripetere il processo di alterazione della tabella Receiver_barcode. Menzionalo come parte della tua classe. Nel passaggio successivo vedrai come lo useremo.

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;
}

Quindi, il codice seguente viene utilizzato per creare la tabella Receiver_barcode se NON esce già per la prima volta dagli utenti della tua app. E si prega di notare "SE NON ESISTE" nel codice. Ha importanza.

//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();
            }

1

Ho avuto lo stesso problema e la soluzione migliore che ho trovato è stata quella di creare prima la tabella che definisce la chiave primaria e quindi di utilizzare l'istruzione insert into.

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;

cattiva idea per inserti in
blocco

0

Ho usato la sintassi CREATE TABLE AS per unire più colonne e ho riscontrato lo stesso problema. Ecco un AppleScript che ho scritto per accelerare il processo.

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"

0

Penso che l'aggiunta di un indice su quella colonna possa ottenere più o meno lo stesso effetto.


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>

0

Utilizza uno strumento come DB Browser per SQLite, consente di aggiungere PK, AI con un semplice clic destro sulla tabella -> modifica.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.