Як додати зовнішній ключ до існуючої таблиці SQLite?


128

У мене є така таблиця:

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

Як додати обмеження на зовнішній ключ parent_id? Припустимо, іноземні ключі ввімкнено

Більшість прикладів передбачає, що ви створюєте таблицю - я хотів би додати обмеження до існуючої.


Команда ALQL 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: SQL функцій , які SQLite не реалізує ).

Тому єдиний спосіб додати зовнішній ключ у 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

Так, простіше. Я тільки цитував FAQ на sqlite: sqlite.org/faq.html#q11 . Насправді RENAME TOце один з небагатьох ALTER TABLEваріантів, який наразі підтримується у sqlite 3.
Даніель Вассалло

3
Чи не повинно бути це: ІНТЕРНЕТ КЛЮЧ (parent_id) ДОВІДКИ батько (id) Щоправда, Джонатан не дав назву "батьківської таблиці". Насправді таблицю слід назвати людиною, але ...
igorludi

3
Це здається для мене великою проблемою. Зазвичай, коли ви скидаєте базу даних, спочатку ви експортуєте команди CREATE TABLE. Потім введіть команди INTO і, нарешті, додайте команди 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
Добре звикнути до цієї послідовності, але це не відповідає актуальному питанню: я хотів би додати обмеження до існуючого.
Вовк

9

Перевірте https://www.sqlite.org/lang_altertable.html#otheralter

Єдиними командами, що змінюють схему, безпосередньо підтримуваних SQLite, є команди "перейменувати таблицю" та "додати стовпчик", показані вище. Однак програми можуть вносити інші довільні зміни у формат таблиці, використовуючи просту послідовність операцій. Етапи внесення довільних змін у схему дизайну таблиці X є наступними:

  1. Якщо обмеження для іноземних ключів увімкнено, відключіть їх за допомогою PRAGMA Foreign_keys = OFF.
  2. Почніть операцію.
  3. Запам’ятайте формат усіх індексів і тригерів, пов’язаних з таблицею X. Ця інформація буде потрібна на кроці 8 нижче. Один із способів зробити це - запустити запит на зразок наступного: тип SELECT, sql від sqlite_master WHERE tbl_name = 'X'.
  4. Використовуйте CREATE TABLE для побудови нової таблиці "new_X", яка знаходиться у бажаному переглянутому форматі таблиці X. Переконайтеся, що ім'я "new_X", звичайно, не збігається з жодним наявним іменем таблиці.
  5. Перенесіть вміст з X у new_X за допомогою оператора типу: ВСТАВИТИ В НОВИЙ_X SELECT ... ВІД X.
  6. Відкиньте стару таблицю X: КОРОТКА ТАБЛИКА X.
  7. Змініть ім'я new_X на X, використовуючи: ALTER TABLE new_X RENAME TO X.
  8. Використовуйте CREATE INDEX та CREATE TRIGGER для реконструкції індексів та тригерів, пов’язаних із таблицею X. Можливо, використовуйте як керівництво старий формат тригерів та індексів, збережених із кроку 3 вище, вносячи зміни, що відповідають змінам.
  9. Якщо будь-які представлення посилаються на таблицю X таким чином, на який впливає зміна схеми, то скиньте ці представлення за допомогою DROP VIEW та відтворіть їх із будь-якими змінами, необхідними для пристосування змін схеми за допомогою CREATE VIEW.
  10. Якщо обмеження зовнішнього ключа були спочатку включені, тоді запустіть PRAGMA Foreign_key_check, щоб переконатися, що зміна схеми не порушила жодних обмежень зовнішнього ключа.
  11. Введіть транзакцію, розпочату на кроці 2.
  12. Якщо обмеження на сторонні ключі спочатку було увімкнено, відновте їх зараз.

Наведена вище процедура є повністю загальною і буде працювати, навіть якщо зміна схеми призведе до зміни інформації, що зберігається в таблиці. Отже, повна процедура, описана вище, підходить для пропускання стовпця, зміни порядку стовпців, додавання або видалення єдиного обмеження або ПЕРВИЧНОГО КЛЮЧА, додавання обмежень 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';

Якщо ви використовуєте підхід zamjenjuje (), вам може бути корисним спочатку перевірити команду заміни (), виконавши:

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

3

Якщо ви використовуєте додаток Firefox sqlite-manager, ви можете зробити наступне:

Замість того, щоб скидати та створювати таблицю ще раз, можна просто змінити її так.

У текстовому полі стовпців клацніть правою кнопкою миші на вказаному прізвищі останнього стовпця, щоб відкрити контекстне меню та виберіть пункт Змінити стовпчик. Зауважте, що якщо останній стовпець у визначенні ТАБЛИЦІ є ПЕРВИЧНИЙ КЛЮЧ, то спочатку потрібно буде додати новий стовпець, а потім відредагувати тип стовпця нового стовпця, щоб додати визначення FOREIGN KEY. У полі Тип стовпця додайте кому та

FOREIGN KEY (parent_id) REFERENCES parent(id)

визначення за типом даних. Натисніть кнопку Змінити, а потім натисніть кнопку Так у діалоговому вікні «Небезпечні дії».

Довідка: менеджер Sqlite


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 у newCol та видаліть колонку Parent_Id . Отже, немає необхідності у тимчасовому столі.


Здається, ви не уважно прочитали питання. Проблема полягала в тому, щоб додати лише зовнішнє обмеження, а не додати стовпчик із обмеженням.
Вовк

Ні. Це не відповідає на поставлене запитання.
МК

-4

Спочатку додайте стовпець у дочірню таблицю Cid а intпотім 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.