SQLite добавить первичный ключ - PullRequest
93 голосов
/ 03 июня 2009

Я создал таблицу в Sqlite, используя синтаксис CREATE TABLE AS, чтобы создать таблицу на основе оператора SELECT. Теперь у этой таблицы нет первичного ключа, но я бы хотел добавить ее.

Выполнение ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...) дает синтаксическую ошибку "около ПЕРВИЧНОГО"

Есть ли способ добавить первичный ключ во время создания таблицы или после нее в Sqlite?

РЕДАКТИРОВАТЬ: под "во время создания" я имею в виду во время создания с CREATE TABLE AS.

Ответы [ 11 ]

117 голосов
/ 03 июня 2009

Вы не можете каким-либо существенным образом изменять таблицы SQLite после их создания. Принятое предлагаемое решение - создать новую таблицу с правильными требованиями и скопировать в нее свои данные, а затем удалить старую таблицу.

вот официальная документация по этому поводу: http://sqlite.org/faq.html#q11

30 голосов
/ 21 февраля 2012

Пока вы используете CREATE TABLE, если вы создаете первичный ключ для одного поля , вы можете использовать:

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

С CREATE TABLE вы также всегда можете использовать следующий подход для создания первичного ключа в одном или нескольких полях :

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

Ссылка: http://www.sqlite.org/lang_createtable.html

Этот ответ не касается изменения таблицы.

10 голосов
/ 13 декабря 2013

После этого я попытался добавить первичный ключ, напрямую изменив таблицу sqlite_master. Этот трюк, кажется, работает. Это взломанное решение, конечно.

Вкратце: создайте обычный (уникальный) индекс для таблицы, затем сделайте схему доступной для записи и измените имя индекса на форму, зарезервированную sqlite для идентификации индекса первичного ключа (то есть sqlite_autoindex_XXX_1, где XXX - имя таблицы) и установите строку sql в NULL. Наконец измените само определение таблицы. Один pittfal: sqlite не видит изменения имени индекса, пока база данных не будет вновь открыта. Это похоже на ошибку, но не серьезную (даже без повторного открытия базы данных, вы все равно можете ее использовать).

Предположим, таблица выглядит так:

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

Тогда я сделал следующее:

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;

Некоторые тесты (в оболочке 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    
6 голосов
/ 14 апреля 2016

Согласно sqlite docs о создании таблицы, использование create table как select создает новую таблицу без ограничений и без первичного ключа.

Однако в документации также говорится, что первичные ключи и уникальные индексы логически эквивалентны ( см. Раздел ограничений ):

В большинстве случаев ограничения UNIQUE и PRIMARY KEY реализуются путем создания уникального индекса в базе данных. (Исключения составляют INTEGER PRIMARY KEY и PRIMARY KEYs для таблиц БЕЗ ROWID.) Следовательно, следующие схемы логически эквивалентны:

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

Таким образом, даже если вы не можете изменить определение таблицы с помощью синтаксиса изменения SQL, вы можете получить тот же эффект первичного ключа, используя уникальный индекс.

Кроме того, любая таблица (кроме таблиц, созданных без синтаксиса rowid) имеет внутренний целочисленный столбец, известный как «rowid». Согласно документации, вы можете использовать этот внутренний столбец для извлечения / изменения таблиц записей.

5 голосов
/ 03 июня 2009

Вы можете сделать это так:

CREATE TABLE mytable (
field1 text,
field2 text,
field3 integer,
PRIMARY KEY (field1, field2)
);
3 голосов
/ 23 апреля 2014

Введение

Это основано на java для Android, и это хороший пример изменения базы данных без раздражения поклонников / клиентов вашего приложения. Это основано на идее страницы FAQ по SQLite http://sqlite.org/faq.html#q11

Проблема

Я не заметил, что мне нужно установить row_number или record_id для удаления одного купленного предмета в квитанции, и в то же время номер штрих-кода предмета обманул меня в мысли, что он станет ключом для удаления этого предмета. Я сохраняю детали квитанции в таблице квитанции_кода. Оставить его без record_id может означать удаление всех записей одного и того же элемента в квитанции, если в качестве ключа я использовал штрих-код элемента.

Примечание

Пожалуйста, поймите, что это копия моего кода, над которой я работаю на момент написания этой статьи. Используйте это только в качестве примера, случайное копирование не поможет вам. Сначала измените это в соответствии с вашими потребностями

Также, пожалуйста, не забудьте прочитать комментарии в коде.

Код

Используйте это как метод в своем классе, чтобы проверить 1-й, отсутствует ли столбец, который вы хотите добавить. Мы делаем это только для того, чтобы не повторять процесс изменения таблицы receive_barcode. Просто упомяните это как часть вашего класса. На следующем шаге вы увидите, как мы будем его использовать.

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

Затем следующий код используется для создания таблицы receive_barcode, если он уже NOT завершает работу для пользователей вашего приложения, которые в первый раз. И, пожалуйста, обратите внимание на «ЕСЛИ НЕ СУЩЕСТВУЕТ» в коде. Это имеет значение.

//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 голос
/ 02 августа 2015

У меня была та же проблема, и лучшее решение, которое я нашел, - это сначала создать таблицу, определяющую первичный ключ, а затем использовать вставку в оператор.

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;
0 голосов
/ 11 апреля 2019

Используйте такой инструмент, как DB Browser для SQLite, он позволяет добавлять PK, AI простым щелчком правой кнопкой мыши по таблице -> изменить.

0 голосов
/ 07 июля 2017
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 голосов
/ 24 июня 2014

Я думаю, что добавление индекса для этого столбца может дать почти такой же эффект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...