SQLite ajouter une clé primaire

100

J'ai créé une table dans Sqlite en utilisant la CREATE TABLE ASsyntaxe pour créer une table basée sur une SELECTinstruction. Maintenant, cette table n'a pas de clé primaire mais j'aimerais en ajouter une.

L'exécution ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)donne une erreur de syntaxe "proche du PRIMAIRE"

Existe-t-il un moyen d'ajouter une clé primaire lors de la création de la table ou après dans Sqlite?

Par "pendant la création", je veux dire pendant la création avec CREATE TABLE AS.

Jack Edmonds
la source
1
vous pouvez utiliser n'importe quel navigateur db pour modifier la base de données. Ils suppriment et créent également les tables. mais nous ne voulons pas nous en préoccuper. vous pouvez télécharger le navigateur db pour n'importe quel système d'exploitation à partir d'ici sqlitebrowser.org
vichu

Réponses:

122

Vous ne pouvez pas modifier les tables SQLite de manière significative après leur création. La solution suggérée acceptée consiste à créer une nouvelle table avec les exigences correctes et à y copier vos données, puis à supprimer l'ancienne table.

voici la documentation officielle à ce sujet: http://sqlite.org/faq.html#q11

Nathan Ridley
la source
7
Ce lien ( sqlite.org/omitted.html ) explique ce qui a été omis plus en détail.
Martin Velez
1
mais nous pouvons ajouter de nouvelles colonnes
umesh
1
Il est étrange que vous ne pouvez pas ajouter un PK après la création de la table , mais vous pouvez ajouter un index ( CREATE UNIQUE INDEX pkName ON tableName(columnName)) lorsque les cadres DB comme MS SQL de SMO réellement faire vous ajoutez un PK après la table a été créé!
SteveCinq
@deFreitas Veuillez nous accorder votre sagesse. Il est clair que vous voulez que les gens sachent que vous désapprouvez la réponse ou quelque chose que l'un des commentateurs a dit, mais votre commentaire ne contient aucune information, à part une intention apparente de transmettre la supériorité et le snark.
Nathan Ridley
31

Tant que vous utilisez CREATE TABLE, si vous créez la clé primaire sur un seul champ , vous pouvez utiliser:

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

Avec CREATE TABLE, vous pouvez également toujours utiliser l'approche suivante pour créer une clé primaire sur un ou plusieurs champs :

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

Référence: http://www.sqlite.org/lang_createtable.html

Cette réponse ne traite pas de la modification des tables.

Acumenus
la source
10

J'ai essayé d'ajouter la clé primaire par la suite en changeant directement la table sqlite_master. Cette astuce semble fonctionner. C'est une solution de piratage bien sûr.

En bref: créez un index régulier (unique) sur la table, puis rendez le schéma inscriptible et changez le nom de l'index sous la forme réservée par sqlite pour identifier un index de clé primaire, (ie sqlite_autoindex_XXX_1, où XXX est le nom de la table) et définissez la chaîne sql sur NULL. Modifiez enfin la définition de la table elle-même. Un pittfal: sqlite ne voit pas le nom de l'index changer jusqu'à ce que la base de données soit rouverte. Cela semble être un bogue, mais pas grave (même sans rouvrir la base de données, vous pouvez toujours l'utiliser).

Supposons que le tableau ressemble à:

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

Ensuite, j'ai fait ce qui suit:

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;

Quelques tests (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    
user3098421
la source
2
Juste un avertissement que vous pouvez (pour autant que je sache) rendre toute votre base de données inaccessible si vous faites cela mal. Je jouais et j'ai accidentellement manqué la clause WHERE dans la deuxième requête de mise à jour. SQLite n'aimait pas ça: P
Andrew Magee
7

Selon la documentation de sqlite sur la création de table, l'utilisation de la table create comme select produit une nouvelle table sans contraintes et sans clé primaire.

Cependant, la documentation indique également que les clés primaires et les index uniques sont logiquement équivalents ( voir la section sur les contraintes ):

Dans la plupart des cas, les contraintes UNIQUE et PRIMARY KEY sont implémentées en créant un index unique dans la base de données. (Les exceptions sont INTEGER PRIMARY KEY et PRIMARY KEY sur les tables SANS ROWID.) Par conséquent, les schémas suivants sont logiquement équivalents:

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

Ainsi, même si vous ne pouvez pas modifier la définition de votre table via la syntaxe SQL alter, vous pouvez obtenir le même effet de clé primaire en utilisant un index unique.

De plus, toutes les tables (sauf celles créées sans la syntaxe rowid) ont une colonne entière interne appelée "rowid". Selon la documentation, vous pouvez utiliser cette colonne interne pour récupérer / modifier les tables d'enregistrement.

Voie Carlitos
la source
Si vous utilisez EntityFramework pour vous connecter à votre base de données, il ne reconnaît pas un index unique comme clé primaire. Donc, même si c'est logiquement et fonctionnellement équivalent dans SQLite, ce n'est pas tout à fait équivalent partout.
Kristen Hammack
5

Vous pouvez le faire comme ceci:

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

introduction

Ceci est basé sur le java d'Android et c'est un bon exemple pour changer la base de données sans ennuyer les fans / clients de votre application. Ceci est basé sur l'idée de la page FAQ SQLite http://sqlite.org/faq.html#q11

Le problème

Je n'ai pas remarqué que je devais définir un row_number ou record_id pour supprimer un seul article acheté dans un reçu, et en même temps, le numéro de code à barres de l'article m'a trompé en pensant en faire la clé pour supprimer cet article. J'enregistre les détails d'un reçu dans la table reception_barcode. Le laisser sans record_id peut signifier la suppression de tous les enregistrements du même article dans un reçu si j'ai utilisé le code-barres de l'article comme clé.

Remarquer

Veuillez comprendre qu'il s'agit d'un copier-coller de mon code sur lequel je travaille au moment d'écrire ces lignes. Utilisez-le uniquement à titre d'exemple, le copier-coller au hasard ne vous aidera pas. Modifiez-le d'abord selon vos besoins

N'oubliez pas non plus de lire les commentaires dans le code.

Le code

Utilisez-le comme méthode dans votre classe pour vérifier d'abord si la colonne que vous souhaitez ajouter est manquante. Nous faisons cela juste pour ne pas répéter le processus de modification de la table reception_barcode. Mentionnez-le simplement dans le cadre de votre cours. Dans l'étape suivante, vous verrez comment nous l'utiliserons.

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

Ensuite, le code suivant est utilisé pour créer la table receipt_barcode si elle ne déjà pas sortir pour les 1ers utilisateurs de temps de votre application. Et s'il vous plaît noter le "SI PAS EXISTANT" dans le code. Cela a de l'importance.

//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
la source
1

J'ai eu le même problème et la meilleure solution que j'ai trouvée est de créer d'abord la table définissant la clé primaire, puis d'utiliser l'insertion dans l'instruction.

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;
Les thermes
la source
mauvaise idée pour les insertions en vrac
PirateApp
0

J'ai utilisé la syntaxe CREATE TABLE AS pour fusionner plusieurs colonnes et j'ai rencontré le même problème. Voici un AppleScript que j'ai écrit pour accélérer le processus.

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
la source
0

Je pense que l'ajout d'un index sur cette colonne peut obtenir à peu près le même effet.

Qiulang
la source
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
la source
0

Utilisez un outil comme DB Browser pour SQLite, il permet d'ajouter PK, AI par simple clic droit sur la table -> modifier.

codeapply
la source