2009-06-03 20 views
86

Ho creato una tabella in Sqlite utilizzando la sintassi CREATE TABLE AS per creare una tabella basata su un'istruzione SELECT. Ora questa tabella non ha una chiave primaria ma vorrei aggiungerne una.SQLite aggiungi chiave primaria

Esecuzione ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...) dà un errore di sintassi "vicino PRIMARIA"

C'è un modo per aggiungere una chiave primaria sia durante la creazione delle tabelle o successivamente in Sqlite?

MODIFICA: Per "durante la creazione" intendo durante la creazione con CREATE TABLE AS.

+0

è possibile utilizzare qualsiasi browser db per modificare il database. Inoltre stanno eliminando e creando le tabelle. ma non vogliamo preoccuparcene. da qui puoi scaricare il browser db per qualsiasi sistema operativo http://sqlitebrowser.org/ – vichu

risposta

110

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

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

+5

Questo link (http://www.sqlite.org/omitted.html) spiega cosa è stato omesso in modo più dettagliato. –

+1

ma possiamo aggiungere nuove colonne – umesh

+0

@umesh http://stackoverflow.com/questions/4253804/insert-new-column-into-table-in-sqlite –

5

si può fare in questo modo:

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

Finché si utilizza CREATE TABLE, se si sta creando la chiave primaria su un singolo campo, è possibile utilizzare:

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

Con CREATE TABLE, è inoltre possibile utilizzare sempre il seguente approccio per creare una chiave primaria su uno o campi multipli:

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 tavolo alterazione.

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" 
9

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

In breve: creare un indice (univoco) regolare sulla tabella, quindi rendere scrivibile lo schema e modificare il nome dell'indice nel modulo riservato da sqlite per identificare un indice di chiave primaria, ad esempio sqlite_autoindex_XXX_1, dove XXX è il nome della tabella) e impostare la stringa sql su NULL. Finalmente cambia la definizione della tabella stessa. Un pittfal: sqlite non vede la modifica del nome dell'indice fino alla riapertura del database. Questo sembra un bug, ma non grave (anche senza riaprire il database, puoi ancora usarlo).

Supponiamo che il tavolo si presenta come:

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

Poi ho fatto la seguente:

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 (in guscio 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  
+1

+1 hack ma funziona! –

+1

Solo un avvertimento che puoi (per quanto posso dire) rendere il tuo intero database inaccessibile se lo fai male. Stavo giocando e ho perso per errore la clausola WHERE nella seconda query di aggiornamento. A SQLite non piaceva quello: P –

2

Introduzione

Questo è basato su Java di Android ed è un buon esempio sulla modifica del database senza infastidire i fan/clienti dell'applicazione. Questo si basa sull'idea della pagina SQLite FAQ http://sqlite.org/faq.html#q11

Il problema

Non ho notato che ho bisogno di impostare un row_number o record_id per eliminare un singolo elemento acquistato in una ricevuta, e al allo stesso tempo il numero di codice a barre dell'oggetto mi ha ingannato pensando di renderlo come la chiave per cancellare quell'elemento. Sto salvando i dettagli di una ricevuta nella tabella receipt_barcode. Lasciandolo senza un record_id può significare cancellare tutti i record dello stesso articolo in una ricevuta se ho usato il codice a barre dell'oggetto come chiave.

Avviso

Vi preghiamo di comprendere che questo è un copia-incolla del mio codice Sono lavorare al momento in cui scriviamo. Usalo solo come esempio, incollare casualmente non ti aiuterà. Modificalo in base alle tue esigenze

Inoltre, non dimenticare di leggere i commenti nel codice.

Il Codice

utilizzare questo come un metodo nella classe di verificare prima se la colonna che si desidera aggiungere è mancante. Lo facciamo solo per non ripetere il processo di modifica della tabella receipt_barcode. Basta menzionarlo come parte della tua classe. Nel prossimo passaggio 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; 
} 

Poi, il seguente codice viene utilizzato per creare la receipt_barcode tavolo se lo fa già NON uscita per il 1 ° tempo gli 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

Penso che l'aggiunta di un indice su quella colonna possa ottenere praticamente lo stesso effetto.

0

Ho avuto lo stesso problema e la soluzione migliore che ho trovato è quella di creare prima la tabella che definisce la chiave primaria e quindi di usare insert in statement.

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable; 
5

Secondo la sqlite docs sulla creazione tabella, usando l'create table as select produce una nuova tabella senza vincoli e senza chiave primaria.

Tuttavia, la documentazione dice anche che le chiavi primarie e indici univoci sono logicamente equivalenti (see constraints section):

Nella maggior parte dei casi, vincoli UNIQUE e PRIMARY KEY sono implementati mediante la creazione di un indice univoco nel database. (Le eccezioni sono INTEGER PRIMARY KEY e chiavi primarie avanti senza tabelle ROWID.) Quindi, i seguenti schemi 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 vostra definizione della tabella tramite SQL alterare la sintassi, è può ottenere lo stesso effetto chiave primaria attraverso l'uso di un indice univoco.

Inoltre, qualsiasi tabella (ad eccezione di quelle create senza la sintassi del rowid) ha una colonna intera interna nota come "rowid". Secondo i documenti, puoi usare questa colonna interna per recuperare/modificare le tabelle dei record.

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> 
Problemi correlati