SQLite adiciona chave primária

99

Criei uma tabela no Sqlite usando a CREATE TABLE ASsintaxe para criar uma tabela com base em uma SELECTinstrução. Agora, esta tabela não tem chave primária, mas gostaria de adicionar uma.

A execução ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)dá um erro de sintaxe "próximo a PRIMARY"

Existe uma maneira de adicionar uma chave primária durante a criação da tabela ou posteriormente no Sqlite?

Por "durante a criação", quero dizer durante a criação com CREATE TABLE AS.

Jack Edmonds
fonte
1
você pode usar qualquer navegador db para editar banco de dados. Eles também estão excluindo e criando as tabelas. mas não queremos nos preocupar com isso. você pode baixar o navegador db para qualquer sistema operacional aqui sqlitebrowser.org
vichu

Respostas:

121

Você não pode modificar as tabelas SQLite de nenhuma maneira significativa após terem sido criadas. A solução sugerida aceita é criar uma nova tabela com os requisitos corretos, copiar seus dados nela e, em seguida, eliminar a tabela antiga.

aqui está a documentação oficial sobre isso: http://sqlite.org/faq.html#q11

Nathan Ridley
fonte
6
Este link ( sqlite.org/omitted.html ) explica o que foi omitido em mais detalhes.
Martin Velez
1
mas podemos adicionar novas colunas
umesh de
1
É estranho que você não possa adicionar um PK após a criação da tabela, mas você pode adicionar um índice ( CREATE UNIQUE INDEX pkName ON tableName(columnName)) quando estruturas de banco de dados como o SMO do MS SQL realmente fazem você adicionar um PK após a tabela ter sido criada!
SteveCinq
@deFreitas Por favor, conceda-nos sua sabedoria. Obviamente, você quer que as pessoas saibam que você desaprova a resposta ou algo que um dos comentadores disse, porém seu comentário não contém nenhuma informação, além de uma aparente intenção de transmitir superioridade e sarcasmo.
Nathan Ridley
31

Enquanto estiver usando CREATE TABLE, se estiver criando a chave primária em um único campo , você pode usar:

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

Com CREATE TABLE, você também pode sempre usar a seguinte abordagem para criar uma chave primária em um ou vários campos :

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

Referência: http://www.sqlite.org/lang_createtable.html

Esta resposta não aborda a alteração da tabela.

Acumenus
fonte
10

Tentei adicionar a chave primária posteriormente, alterando a tabela sqlite_master diretamente. Este truque parece funcionar. É uma solução de hack, claro.

Resumindo: crie um índice regular (único) na tabela, torne o esquema gravável e altere o nome do índice para a forma reservada por sqlite para identificar um índice de chave primária, (ou seja, sqlite_autoindex_XXX_1, onde XXX é o nome da tabela) e defina a string sql como NULL. Por fim, altere a própria definição da tabela. Um pittfal: sqlite não vê a mudança do nome do índice até que o banco de dados seja reaberto. Isso parece um bug, mas não é grave (mesmo sem reabrir o banco de dados, você ainda pode usá-lo).

Suponha que a tabela se pareça com:

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

Então eu fiz o seguinte:

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;

Alguns testes (no 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    
user3098421
fonte
2
Apenas um aviso de que você pode (tanto quanto eu posso dizer) tornar seu banco de dados inteiro inacessível se você fizer isso errado. Eu estava brincando e acidentalmente perdi a cláusula WHERE na segunda consulta de atualização. SQLite não gostou disso: P
Andrew Magee
7

De acordo com a documentação do sqlite sobre a criação de tabelas, o uso de create table como select produz uma nova tabela sem restrições e sem chave primária.

No entanto, a documentação também diz que as chaves primárias e os índices exclusivos são logicamente equivalentes ( consulte a seção de restrições ):

Na maioria dos casos, as restrições UNIQUE e PRIMARY KEY são implementadas criando um índice exclusivo no banco de dados. (As exceções são INTEGER PRIMARY KEY e PRIMARY KEYs em tabelas WITHOUT ROWID.) Portanto, os seguintes esquemas são logicamente 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); 

Portanto, mesmo que não seja possível alterar a definição da tabela por meio da sintaxe de alteração do SQL, você pode obter o mesmo efeito de chave primária usando um índice exclusivo.

Além disso, qualquer tabela (exceto aquelas criadas sem a sintaxe rowid) tem uma coluna inteira interna conhecida como "rowid". De acordo com os documentos, você pode usar esta coluna interna para recuperar / modificar tabelas de registro.

Carlitos Way
fonte
Se você estiver usando EntityFramework para se conectar ao seu banco de dados, ele não reconhecerá um índice exclusivo como chave primária. Portanto, embora seja lógica e funcionalmente equivalente dentro do SQLite, não é totalmente equivalente em todos os lugares.
Kristen Hammack de
5

Você pode fazer assim:

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

Introdução

Ele é baseado em java do Android e é um bom exemplo de como alterar o banco de dados sem incomodar os fãs / clientes de seus aplicativos. Isso é baseado na ideia da página de perguntas frequentes do SQLite http://sqlite.org/faq.html#q11

O problema

Não percebi que preciso definir um row_number ou record_id para excluir um único item comprado em um recibo e, ao mesmo tempo, o número do código de barras do item me enganou ao pensar em torná-lo a chave para excluir aquele item. Estou salvando os detalhes de um recibo na tabela código_de_barras de recebimento. Deixá-lo sem um record_id pode significar a exclusão de todos os registros do mesmo item em um recibo se eu usar o código de barras do item como a chave.

Aviso prévio

Por favor, entenda que este é um copiar e colar do meu código no qual estou trabalhando no momento da redação deste artigo. Use-o apenas como um exemplo, copiar e colar aleatoriamente não o ajudará. Modifique primeiro de acordo com suas necessidades

Além disso, não se esqueça de ler os comentários no código.

O código

Use isso como um método em sua classe para verificar primeiro se a coluna que você deseja adicionar está faltando. Fazemos isso apenas para não repetir o processo de alteração do código_de_barras da tabela. Basta mencioná-lo como parte de sua aula. Na próxima etapa, você verá como o 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;
}

Em seguida, o código a seguir é usado para criar a tabela receive_barcode se ela já NÃO sair para os usuários da primeira vez de seu aplicativo. E observe o "SE NÃO EXISTE" no código. Tem importância.

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

Eu tive o mesmo problema e a melhor solução que encontrei é primeiro criar a tabela que define a chave primária e então usar a instrução insert into.

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;
Spas
fonte
má ideia para inserções em massa
PirateApp
0

Usei a sintaxe CREATE TABLE AS para mesclar várias colunas e encontrei o mesmo problema. Aqui está um AppleScript que escrevi para acelerar o 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"
adayzdone
fonte
0

Acho que adicionar um índice nessa coluna pode ter praticamente o mesmo efeito.

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

Use uma ferramenta como o DB Browser para SQLite, permite adicionar PK, AI simplesmente clicando com o botão direito na tabela -> modificar.

aplicar código
fonte