SQLite agregar clave principal

99

Creé una tabla en Sqlite usando la CREATE TABLE ASsintaxis para crear una tabla basada en una SELECTdeclaración. Ahora bien, esta tabla no tiene una clave principal, pero me gustaría agregar una.

La ejecución ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)da un error de sintaxis "cerca de PRIMARY"

¿Hay alguna forma de agregar una clave principal durante la creación de la tabla o después en Sqlite?

Por "durante la creación" me refiero durante la creación con CREATE TABLE AS.

Jack Edmonds
fuente
1
puede utilizar cualquier navegador de base de datos para editar la base de datos. También están eliminando y creando las tablas. pero no queremos preocuparnos por eso. puede descargar el navegador db para cualquier sistema operativo desde aquí sqlitebrowser.org
vichu

Respuestas:

121

No puede modificar las tablas SQLite de manera significativa después de que se hayan creado. La solución sugerida aceptada es crear una nueva tabla con los requisitos correctos y copiar sus datos en ella, luego eliminar la tabla anterior.

aquí está la documentación oficial sobre esto: http://sqlite.org/faq.html#q11

Nathan Ridley
fuente
6
Este enlace ( sqlite.org/omitted.html ) explica lo que se omitió con más detalle.
Martin Velez
1
pero podemos agregar nuevas columnas
umesh
1
Es extraño que no pueda agregar un PK después de la creación de la tabla, pero puede agregar un índice ( CREATE UNIQUE INDEX pkName ON tableName(columnName)) cuando los marcos de base de datos como el SMO de MS SQL realmente le hacen agregar un PK después de que se haya creado la tabla.
SteveCinq
@deFreitas Por favor, concédenos tu sabiduría. Claramente, quieres que la gente sepa que desapruebas la respuesta o algo que dijo uno de los comentaristas, sin embargo, tu comentario no contiene ninguna información, además de una aparente intención de transmitir superioridad y sarcasmo.
Nathan Ridley
31

Siempre que esté usando CREATE TABLE, si está creando la clave principal en un solo campo , puede usar:

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

Con CREATE TABLE, siempre puede utilizar el siguiente enfoque para crear una clave principal en uno o varios campos :

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

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

Esta respuesta no aborda la alteración de la tabla.

Acumenus
fuente
10

Intenté agregar la clave principal después cambiando la tabla sqlite_master directamente. Este truco parece funcionar. Es una solución de pirateo, por supuesto.

En resumen: cree un índice regular (único) en la tabla, luego haga que el esquema se pueda escribir y cambie el nombre del índice al formulario reservado por sqlite para identificar un índice de clave primaria, (es decir, sqlite_autoindex_XXX_1, donde XXX es el nombre de la tabla) y establezca la cadena sql en NULL. Por último, cambie la definición de la tabla en sí. One pittfal: sqlite no ve el cambio de nombre del índice hasta que se vuelve a abrir la base de datos. Esto parece un error, pero no grave (incluso sin volver a abrir la base de datos, puede seguir utilizándola).

Suponga que la tabla se ve así:

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

Luego hice lo siguiente:

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;

Algunas pruebas (en 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    
usuario3098421
fuente
2
Solo una advertencia de que puede (por lo que puedo decir) hacer que toda su base de datos sea inaccesible si lo hace mal. Estaba jugando y accidentalmente perdí la cláusula WHERE en la segunda consulta de actualización. A SQLite no le gustó eso: P
Andrew Magee
7

Según los documentos de sqlite sobre la creación de tablas, el uso de la tabla de creación como selección produce una nueva tabla sin restricciones y sin clave principal.

Sin embargo, la documentación también dice que las claves primarias y los índices únicos son lógicamente equivalentes ( consulte la sección de restricciones ):

En la mayoría de los casos, las restricciones UNIQUE y PRIMARY KEY se implementan creando un índice único en la base de datos. (Las excepciones son INTEGER PRIMARY KEY y PRIMARY KEYs en tablas SIN ROWID). Por lo tanto, los siguientes esquemas son lógicamente equivalentes:

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

Por lo tanto, incluso si no puede modificar la definición de su tabla a través de la sintaxis de modificación de SQL, puede obtener el mismo efecto de clave primaria mediante el uso de un índice único.

Además, cualquier tabla (excepto aquellas creadas sin la sintaxis de rowid) tiene una columna de entero interna conocida como "rowid". De acuerdo con los documentos, puede usar esta columna interna para recuperar / modificar tablas de registros.

La manera de Carlitos
fuente
Si está utilizando EntityFramework para conectarse a su base de datos, no reconoce un índice único como clave principal. Entonces, si bien es lógica y funcionalmente equivalente dentro de SQLite, no es exactamente equivalente en todas partes.
Kristen Hammack
5

Puedes hacerlo así:

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

Introducción

Esto se basa en Java de Android y es un buen ejemplo de cómo cambiar la base de datos sin molestar a los fanáticos / clientes de la aplicación. Esto se basa en la idea de la página de preguntas frecuentes de SQLite http://sqlite.org/faq.html#q11

El problema

No me di cuenta de que necesitaba establecer un número de fila o id_registro para eliminar un solo artículo comprado en un recibo y, al mismo tiempo, el número de código de barras del artículo me engañó para que pensara en hacerlo como la clave para eliminar ese artículo. Estoy guardando los detalles de un recibo en la tabla recibo_barco Dejarlo sin un record_id puede significar eliminar todos los registros del mismo artículo en un recibo si utilicé el código de barras del artículo como clave.

aviso

Por favor, comprenda que esta es una copia y pegado de mi código en el que estoy trabajando al momento de escribir este artículo. Úselo solo como ejemplo, copiar y pegar al azar no lo ayudará. Modifique esto primero a sus necesidades

Además, no olvide leer los comentarios en el código.

El código

Use esto como un método en su clase para verificar primero si falta la columna que desea agregar. Hacemos esto solo para no repetir el proceso de alteración de la tabla Receive_barcode. Solo menciónalo como parte de tu clase. En el siguiente paso, verá cómo lo usaremos.

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

Luego, el siguiente código se usa para crear la tabla Receta_barcode si ya NO sale por primera vez para los usuarios de su aplicación. Y observe el "SI NO EXISTE" en el código. Tiene importancia.

//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
fuente
1

Tuve el mismo problema y la mejor solución que encontré es crear primero la tabla que define la clave primaria y luego usar insertar en la declaración.

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;
Spas
fuente
mala idea para inserciones a granel
PirateApp
0

Usé la sintaxis CREATE TABLE AS para fusionar varias columnas y encontré el mismo problema. Aquí hay un AppleScript que escribí para acelerar el proceso.

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
fuente
0

Creo que agregar un índice en esa columna puede tener prácticamente el mismo efecto.

Qiulang
fuente
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
fuente
0

Use una herramienta como DB Browser para SQLite, permite agregar PK, AI simplemente haciendo clic derecho en la tabla -> modificar.

aplicar codigo
fuente