SQLiteの主キーの追加


99

ステートメントにCREATE TABLE AS基づいてテーブルを作成する構文を使用して、Sqliteでテーブルを作成しましたSELECT。このテーブルには主キーがありませんが、追加したいと思います。

実行ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)すると「PRIMARYに近い」という構文エラーが発生する

テーブルの作成中または後でSqliteで主キーを追加する方法はありますか?

「作成中」とは、での作成中を意味しCREATE TABLE ASます。


1
データベースの編集には、任意のdbブラウザーを使用できます。また、テーブルを削除および作成しています。しかし、私たちはそれについて気になりたくありません。あなたはここから任意のOS用のDBブラウザをダウンロードすることができsqlitebrowser.org
vichu

回答:


121

SQLiteテーブルを作成した後は、それを大幅に変更することはできません。承認された推奨ソリューションは、正しい要件で新しいテーブルを作成し、そこにデータをコピーしてから、古いテーブルを削除することです。

これはこれに関する公式のドキュメントです:http : //sqlite.org/faq.html#q11


6
このリンク(sqlite.org/omitted.html)は、省略されたものをより詳しく説明しています。
Martin Velez、

1
新しい列を追加できます
umesh


1
それはあなたがテーブルの作成後にPKを追加することはできませんが、あなたは、インデックスを追加することができます奇妙な(CREATE UNIQUE INDEX pkName ON tableName(columnName))時にDBのようなフレームワークMS SQLのSMO実際に作るテーブルが作成された後、あなたがPKを追加します!
SteveCinq 2017

@deFreitas私たちにあなたの知恵を授けてください。明らかに、回答またはコメント投稿者の1人が言ったことの不承認を人々に知ってもらいたいのですが、あなたのコメントには、優越性とスネークを伝える明らかな意図以外に、情報はまったく含まれていません。
Nathan Ridley

31

を使用している限りCREATE TABLE単一のフィールドで主キーを作成する場合は、以下を使用できます。

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

ではCREATE TABLE、常に次の方法を使用して、1つまたは複数のフィールドに主キーを作成することもできます

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

リファレンス:http : //www.sqlite.org/lang_createtable.html

この回答では、テーブルの変更については触れていません。


10

その後、sqlite_masterテーブルを直接変更して、主キーを追加しようとしました。このトリックはうまくいくようです。もちろん、これはハックソリューションです。

つまり、テーブルに通常の(一意の)インデックスを作成し、スキーマを書き込み可能にして、インデックスの名前をsqliteによって予約されている形式に変更して、主キーインデックスを識別します(例:sqlite_autoindex_XXX_1、XXXはテーブル名) sql文字列をNULLに設定します。最後に、テーブル定義自体を変更します。1つの注意点:データベースを再度開くまで、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    

2
これを間違えると、(私が知る限り)データベース全体にアクセスできなくなる可能性があるという単なる警告です。私は遊んでいて、2番目の更新クエリのWHERE句を誤って逃しました。SQLiteはそれが気に入らなかった:P
Andrew Magee 2015年

7

テーブルの作成に関するsqliteのドキュメントによると、create tableをselectとして使用すると、制約も主キーもない新しいテーブルが作成されます

ただし、ドキュメントには、主キーと一意のインデックスは論理的に同等であるとも記載されています(制約セクションを参照)。

ほとんどの場合、UNIQUEおよびPRIMARY KEY制約は、データベースに一意のインデックスを作成することによって実装されます。(例外は、WITHOUT ROWIDテーブルのINTEGER PRIMARY KEYおよびPRIMARY KEYです。)したがって、次のスキーマは論理的に同等です。

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」と呼ばれる内部整数列があります。ドキュメントによると、この内部列を使用してレコードテーブルを取得/変更できます。


EntityFrameworkを使用してデータベースに接続している場合、データベースは一意のインデックスを主キーとして認識しません。したがって、SQLite内では論理的および機能的に同等ですが、どこでもまったく同等ではありません。
Kristen Hammack


3

前書き

これはAndroidのJavaに基づいており、アプリケーションのファンや顧客に迷惑をかけずにデータベースを変更する良い例です。これは、SQLite FAQページhttp://sqlite.org/faq.html#q11のアイデアに基づいています。

問題

領収書の1つの購入アイテムを削除するには、row_numberまたはrecord_idを設定する必要があることに気付きませんでした。同時に、アイテムのバーコード番号は、それをそのアイテムを削除するためのキーにすることを考えさせられました。領収書の詳細をレシート_バーコードテーブルに保存しています。record_idなしでそのままにしておくと、アイテムのバーコードをキーとして使用した場合、レシート内の同じアイテムのすべてのレコードが削除されます。

通知

これは、この記事の執筆時に取り組んでいる私のコードのコピーと貼り付けであることを理解してください。例としてのみ使用してください。ランダムにコピーして貼り付けても役に立ちません。これを最初に必要に応じて変更してください

また、コード内のコメントを忘れずに読んでください。

コード

これをクラスのメソッドとして使用して、最初に、追加する列が欠落しているかどうかを確認します。これは、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;
}

次に、次のコードを使用して、アプリの初めてのユーザーがテーブルexit_barcodeをまだ終了していない場合に作成します。そして、コードの「存在しない場合」に注意してください。それは重要です。

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

同じ問題があり、私が見つけた最善の解決策は、最初に主キーを定義するテーブルを作成し、次にinsert intoステートメントを使用することです。

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

INSERT INTO mytable 
SELECT field1, field2 
FROM anothertable;

一括挿入の悪いアイデア
PirateApp 2018年

0

CREATE TABLE AS構文を使用して複数の列をマージしたところ、同じ問題が発生しました。これは、プロセスを高速化するために私が書いたAppleScriptです。

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"

0

その列にインデックスを追加しても、ほぼ同じ効果が得られると思います。


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>

0

SQLiteのDBブラウザーのようなツールを使用して、テーブルを右クリックするだけでPK、AIを追加できます->変更。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.