기존 SQLite 테이블에 외래 키를 어떻게 추가합니까?


128

다음 표가 있습니다.

CREATE TABLE child( 
  id INTEGER PRIMARY KEY, 
  parent_id INTEGER, 
  description TEXT);

에 외래 키 제약 조건을 parent_id어떻게 추가 합니까? 외래 키가 활성화되어 있다고 가정합니다.

대부분의 예에서는 테이블을 생성한다고 가정합니다. 기존 테이블에 제약 조건을 추가하고 싶습니다.


SQLite ALTER 명령은 "테이블 이름 변경"및 "열 추가"만 지원합니다. 그러나 간단한 작업 순서를 사용하여 테이블 형식을 임의로 변경할 수 있습니다. 내 대답을
situee

답변:


198

당신은 할 수 없습니다.

테이블에 외래 키를 추가하는 SQL-92 구문은 다음과 같습니다.

ALTER TABLE child ADD CONSTRAINT fk_child_parent
                  FOREIGN KEY (parent_id) 
                  REFERENCES parent(id);

SQLite는 명령 의 ADD CONSTRAINT변형을 지원하지 않습니다ALTER TABLE ( sqlite.org : SQLite가 구현하지 않는 SQL 기능 ).

따라서 sqlite 3.6.1에서 외래 키를 추가하는 유일한 방법 CREATE TABLE은 다음과 같습니다.

CREATE TABLE child ( 
    id           INTEGER PRIMARY KEY, 
    parent_id    INTEGER, 
    description  TEXT,
    FOREIGN KEY (parent_id) REFERENCES parent(id)
);

불행히도 기존 데이터를 임시 테이블에 저장하고 이전 테이블을 삭제하고 FK 제약 조건으로 새 테이블을 만든 다음 임시 테이블에서 데이터를 다시 복사해야합니다. ( sqlite.org-FAQ : Q11 )


28
이전 테이블의 이름을 바꾸고 새 테이블을 만들고 데이터를 다시 복사하는 것이 더 쉽다고 생각합니다. 그런 다음 이전 테이블을 삭제할 수 있습니다.
tuinstoel

예, 더 쉽습니다. sqlite FAQ : sqlite.org/faq.html#q11을 인용했습니다 . 실제로 RENAME TOALTER TABLEsqlite 3에서 현재 지원되는 몇 안되는 변형 중 하나입니다.
Daniel Vassallo

3
그렇지 않아야합니다 : FOREIGN KEY (parent_id) REFERENCES parent (id) 사실, Jonathan은 "부모 테이블"의 이름을 제공하지 않았습니다. 사실, 테이블은 ... 사람 이름이되어야하지만
igorludi

3
이것은 나에게 큰 문제인 것 같습니다. 일반적으로 데이터베이스를 덤프 할 때 먼저 CREATE TABLE 명령을 내 보냅니다. 그런 다음 INSERT INTO 명령, 마지막으로 ADD CONSTRAINT 명령. 데이터에 순환 (외래 키 값) 종속성이있는 경우 외래 키가 적용되는 동안 데이터를 삽입 할 수 없습니다. 그러나 나중에 외래 키 제약 조건을 추가 할 수 없으면 멈춘 것입니다. 물론 지연된 제약이 있지만 이것은 매우 서투른 것입니다.
nagylzs

9
다른 테이블에이 테이블에 대한 참조가있는 경우 첫 번째 주석에서 설명한대로 이전 테이블의 이름을 바꾸지 마십시오! 이 경우에도이 모든 테이블을 다시 만들어야합니다.
rocknow

57

테이블을 변경하고 제약 조건을 사용하는 열을 추가하면 제약 조건을 추가 할 수 있습니다.

먼저 parent_id없이 테이블을 만듭니다.

CREATE TABLE child( 
  id INTEGER PRIMARY KEY,  
  description TEXT);

그런 다음 테이블을 변경하십시오.

ALTER TABLE child ADD COLUMN parent_id INTEGER REFERENCES parent(id);

2
이 시퀀스에 익숙해지는 것은 좋지만 실제 질문에 대한 답은 아닙니다 . 기존
Wolf

9

https://www.sqlite.org/lang_altertable.html#otheralter를 확인 하십시오.

SQLite에서 직접 지원하는 유일한 스키마 변경 명령은 위에 표시된 "테이블 이름 변경"및 "열 추가"명령입니다. 그러나 응용 프로그램은 간단한 작업 순서를 사용하여 테이블 형식을 임의로 변경할 수 있습니다. 일부 테이블 X의 스키마 디자인을 임의로 변경하는 단계는 다음과 같습니다.

  1. 외래 키 제약 조건이 활성화 된 경우 PRAGMA foreign_keys = OFF를 사용하여 비활성화하십시오.
  2. 거래를 시작하십시오.
  3. 테이블 X와 연관된 모든 인덱스 및 트리거의 형식을 기억하십시오.이 정보는 아래 8 단계에서 필요합니다. 이를 수행하는 한 가지 방법은 SELECT type, sql FROM sqlite_master WHERE tbl_name = 'X'와 같은 쿼리를 실행하는 것입니다.
  4. CREATE TABLE을 사용하여 테이블 X의 원하는 수정 된 형식으로 된 새 테이블 "new_X"를 생성합니다. 물론 이름 "new_X"가 기존 테이블 이름과 충돌하지 않는지 확인하십시오.
  5. INSERT INTO new_X SELECT ... FROM X와 같은 문을 사용하여 X에서 new_X로 콘텐츠를 전송합니다.
  6. 이전 테이블 X를 삭제하십시오. DROP TABLE X.
  7. ALTER TABLE new_X RENAME TO X를 사용하여 new_X의 이름을 X로 변경합니다.
  8. CREATE INDEX 및 CREATE TRIGGER를 사용하여 테이블 X와 연관된 인덱스 및 트리거를 재구성하십시오. 위의 3 단계에서 저장 한 트리거 및 인덱스의 이전 형식을 지침으로 사용하여 변경에 맞게 변경하십시오.
  9. 뷰가 스키마 변경의 영향을받는 방식으로 테이블 X를 참조하는 경우 DROP VIEW를 사용하여 해당 뷰를 삭제하고 CREATE VIEW를 사용하여 스키마 변경을 수용하는 데 필요한 변경 사항으로 다시 만듭니다.
  10. 외래 키 제약 조건이 원래 활성화 된 경우 PRAGMA foreign_key_check를 실행하여 스키마 변경이 외래 키 제약 조건을 위반하지 않았는지 확인합니다.
  11. 2 단계에서 시작한 트랜잭션을 커밋합니다.
  12. 외래 키 제약 조건이 원래 활성화 된 경우 지금 다시 활성화합니다.

위의 절차는 완전히 일반적이며 스키마 변경으로 인해 테이블에 저장된 정보가 변경되는 경우에도 작동합니다. 따라서 위의 전체 절차는 예를 들어 열 삭제, 열 순서 변경, UNIQUE 제약 조건 또는 PRIMARY KEY 추가 또는 제거, CHECK 또는 FOREIGN KEY 또는 NOT NULL 제약 조건 추가 또는 열에 대한 데이터 유형 변경에 적합합니다.


4

예, 새 열을 추가하지 않고도 할 수 있습니다. 데이터베이스 손상을 방지하려면 올바르게 수행하도록주의해야하므로이를 시도하기 전에 데이터베이스를 완전히 백업해야합니다.

특정 예 :

CREATE TABLE child(
  id INTEGER PRIMARY KEY,
  parent_id INTEGER,
  description TEXT
);

--- create the table we want to reference
create table parent(id integer not null primary key);

--- now we add the foreign key
pragma writable_schema=1;
update SQLITE_MASTER set sql = replace(sql, 'description TEXT)',
    'description TEXT, foreign key (parent_id) references parent(id))'
) where name = 'child' and type = 'table';

--- test the foreign key
pragma foreign_keys=on;
insert into parent values(1);
insert into child values(1, 1, 'hi'); --- works
insert into child values(2, 2, 'bye'); --- fails, foreign key violation

또는 더 일반적으로 :

pragma writable_schema=1;

// replace the entire table's SQL definition, where new_sql_definition contains the foreign key clause you want to add
UPDATE SQLITE_MASTER SET SQL = new_sql_definition where name = 'child' and type = 'table';

// alternatively, you might find it easier to use replace, if you can match the exact end of the sql definition
// for example, if the last column was my_last_column integer not null:
UPDATE SQLITE_MASTER SET SQL = replace(sql, 'my_last_column integer not null', 'my_last_column integer not null, foreign key (col1, col2) references other_table(col1, col2)') where name = 'child' and type = 'table';

pragma writable_schema=0;

어느 쪽이든 변경하기 전에 먼저 SQL 정의가 무엇인지 확인하고 싶을 것입니다.

select sql from SQLITE_MASTER where name = 'child' and type = 'table';

replace () 접근 방식을 사용하는 경우 실행하기 전에 다음을 실행하여 replace () 명령을 먼저 테스트하는 것이 도움이 될 수 있습니다.

select replace(sql, ...) from SQLITE_MASTER where name = 'child' and type = 'table';

3

Firefox 애드온 sqlite-manager를 사용하는 경우 다음을 수행 할 수 있습니다.

테이블을 삭제하고 다시 생성하는 대신 다음과 같이 수정할 수 있습니다.

열 텍스트 상자에서 나열된 마지막 열 이름을 마우스 오른쪽 버튼으로 클릭하여 상황에 맞는 메뉴를 표시하고 열 편집을 선택합니다. TABLE 정의의 마지막 열이 PRIMARY KEY이면 먼저 새 열을 추가 한 다음 FOREIGN KEY 정의를 추가하기 위해 새 열의 열 유형을 편집해야합니다. 열 유형 상자 내에 쉼표를 추가하고

FOREIGN KEY (parent_id) REFERENCES parent(id)

데이터 유형 후 정의. 변경 버튼을 클릭 한 다음 위험한 작업 대화 상자에서 예 버튼을 클릭합니다.

참조 : Sqlite Manager


2

이것을 시도 할 수 있습니다.

ALTER TABLE [Child] ADD COLUMN column_name INTEGER REFERENCES parent_table_name(column_id);

-1

기본적으로 그럴 수는 없지만 상황을 우회 할 수 있습니다.

기존 테이블에 외래 키 제약 조건을 추가하는 올바른 방법은 다음 명령입니다.

db.execSQL("alter table child add column newCol integer REFERENCES parent(parent_Id)");

그런 다음 복사 PARENT_ID을 받는 데이터를 뉴콜 후 삭제 PARENT_ID의 열을. 따라서 임시 테이블이 필요하지 않습니다.


질문을주의 깊게 읽지 않은 것 같습니다. 문제는 제약 조건이있는 열을 추가하지 않고 외부 제약 조건 만 추가하는 것이 었습니다.
Wolf

아니. 질문에 대답하지 않습니다.
MK

-4

첫 번째 자식 테이블에 열을 추가 Cidint다음 alter table아래의 코드와 함께. 이 방법으로 외래 키 Cid를 부모 테이블의 기본 키로 추가하고 자식 테이블의 외래 키로 사용할 수 있습니다.

ALTER TABLE [child] 
  ADD CONSTRAINT [CId] 
  FOREIGN KEY ([CId]) 
  REFERENCES [Parent]([CId]) 
  ON DELETE CASCADE ON UPDATE NO ACTION;
GO

1
이것은 SQLite에서 유효하지 않습니다. 또한 이것은 MS SQL 구문입니다.
StilesCrisis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.