SQLite 기본 키 추가


100

문을 CREATE TABLE AS기반으로 테이블을 만드는 구문을 사용하여 Sqlite에서 테이블을 만들었습니다 SELECT. 이제이 테이블에는 기본 키가 없지만 추가하고 싶습니다.

실행 ALTER TABLE table_name ADD PRIMARY KEY(col1, col2,...)하면 "near PRIMARY"구문 오류가 발생합니다.

테이블 생성 중 또는 나중에 Sqlite에서 기본 키를 추가하는 방법이 있습니까?

"창조 중"이란 CREATE TABLE AS.


1
데이터베이스 편집을 위해 모든 db 브라우저를 사용할 수 있습니다. 또한 테이블을 삭제하고 생성합니다. 그러나 우리는 그것에 대해 귀찮게하고 싶지 않습니다. 당신은 여기에서 모든 OS에 대한 db 브라우저를 다운로드 할 수 있습니다 sqlitebrowser.org
vichu

답변:


122

SQLite 테이블이 생성 된 후에는 중요한 방식으로 수정할 수 없습니다. 허용되는 권장 솔루션은 올바른 요구 사항으로 새 테이블을 만들고 데이터를 여기에 복사 한 다음 이전 테이블을 삭제하는 것입니다.

이에 대한 공식 문서는 다음과 같습니다. http://sqlite.org/faq.html#q11


7
이 링크 ( sqlite.org/omitted.html )는 생략 된 내용을 자세히 설명합니다.
Martin Velez 2012-08-15

1
그러나 우리는 새로운 열을 추가 할 수 있습니다
umesh


1
테이블 생성 후 PK를 추가 할 수 없지만 CREATE UNIQUE INDEX pkName ON tableName(columnName)MS SQL의 SMO와 같은 DB 프레임 워크가 실제로 테이블 생성 후 PK를 추가 할 때 인덱스 ( )를 추가 할 수 있다는 것이 이상합니다!
SteveCinq

@deFreitas 우리에게 당신의 지혜를주십시오. 분명히 당신은 사람들이 당신이 답변이나 댓글 작성자 중 한 명이 말한 것을 반대한다는 것을 알기를 원하지만, 당신의 댓글에는 우월함과 비난을 전달하려는 명백한 의도 외에는 정보가 전혀 포함되어 있지 않습니다.
Nathan Ridley

31

를 사용 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

나중에 sqlite_master 테이블을 직접 변경하여 기본 키를 추가하려고했습니다. 이 트릭은 효과가있는 것 같습니다. 물론 해킹 솔루션입니다.

요컨대 : 테이블에 일반 (고유) 인덱스를 만든 다음 스키마를 쓰기 가능하게 만들고 인덱스 이름을 sqlite가 예약 한 형식으로 변경하여 기본 키 인덱스를 식별합니다 (예 : sqlite_autoindex_XXX_1, 여기서 XXX는 테이블 이름). SQL 문자열을 NULL로 설정하십시오. 마침내 테이블 정의 자체를 변경하십시오. 한 가지 핵심 : 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
잘못하면 전체 데이터베이스에 액세스 할 수 없게 만들 수 있다는 경고입니다. 나는 놀다가 실수로 두 번째 업데이트 쿼리에서 WHERE 절을 놓쳤습니다. SQLite는이 같은 그렇지 않은 : P
앤드류 매기

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 alter 구문을 통해 테이블 ​​정의를 변경할 수 없더라도 고유 인덱스를 사용하여 동일한 기본 키 효과를 얻을 수 있습니다.

또한 모든 테이블 (rowid 구문없이 생성 된 테이블 제외)에는 "rowid"라는 내부 정수 열이 있습니다. 문서에 따르면이 내부 열을 사용하여 레코드 테이블을 검색 / 수정할 수 있습니다.


EntityFramework를 사용하여 데이터베이스에 연결하는 경우 고유 인덱스를 기본 키로 인식하지 않습니다. 따라서 SQLite 내에서 논리적이고 기능적으로는 동일하지만 모든 곳에서 동일하지는 않습니다.
Kristen Hammack

5

다음과 같이 할 수 있습니다.

CREATE TABLE mytable (
field1 text,
field2 text,
field3 integer,
PRIMARY KEY (field1, field2)
);

3

소개

이것은 Android의 Java를 기반으로하며 애플리케이션 팬 / 고객을 괴롭히지 않고 데이터베이스를 변경하는 좋은 예입니다. 이것은 SQLite FAQ 페이지 http://sqlite.org/faq.html#q11 의 아이디어를 기반으로합니다 .

문제

영수증에서 구매 한 단일 항목을 삭제하려면 row_number 또는 record_id를 설정해야한다는 사실을 알지 못했으며, 동시에 항목 바코드 번호가 해당 항목을 삭제하는 열쇠로 만드는 생각에 속았습니다. 영수증 세부 정보를 영수증 바코드 테이블에 저장하고 있습니다. record_id없이 그대로두면 항목 바코드를 키로 사용하면 영수증에서 동일한 항목의 모든 레코드를 삭제할 수 있습니다.

주의

이것은이 글을 쓰는 시점에 제가 작업하고있는 제 코드의 복사-붙여 넣기라는 것을 이해하십시오. 예제로만 사용하십시오. 무작위로 복사하여 붙여 넣는 것은 도움이되지 않습니다. 필요에 따라 먼저 수정하십시오.

또한 코드의 주석을 읽는 것을 잊지 마십시오.

코드

이것을 클래스에서 방법으로 사용하여 추가하려는 열이 누락되었는지 먼저 확인하십시오. 우리는 영수증 바코드 테이블을 변경하는 과정을 반복하지 않기 위해 이렇게합니다. 수업의 일부로 언급하십시오. 다음 단계에서 우리가 어떻게 사용하는지 볼 것입니다.

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

그런 다음 앱을 처음 사용하는 사용자에게 이미 종료 되지 않은 경우 영수증 _ 바코드 테이블을 생성하는 데 다음 코드가 사용됩니다 . 그리고 코드에서 "IF NOT EXISTS"를 확인하십시오. 중요합니다.

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

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 Browser와 같은 도구를 사용하면 테이블을 마우스 오른쪽 버튼으로 클릭-> 수정하여 PK, AI를 추가 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.