Var olan bir SQLite tablosuna nasıl yabancı anahtar eklerim?


128

Aşağıdaki tabloya sahibim:

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

Bir yabancı anahtar kısıtlamasını nasıl eklerim parent_id? Yabancı anahtarların etkinleştirildiğini varsayın.

Örneklerin çoğu tabloyu sizin oluşturduğunuzu varsayar - Kısıtlamayı mevcut olana eklemek istiyorum.


SQLite ALTER komutu yalnızca "tabloyu yeniden adlandır" ve "sütun ekle" yi destekler. Bununla birlikte, basit bir işlem dizisi kullanarak bir tablonun biçiminde başka keyfi değişiklikler yapabiliriz. Cevabımı
situee

Yanıtlar:


198

Yapamazsın.

Tablonuza yabancı anahtar eklemek için SQL-92 sözdizimi aşağıdaki gibi olsa da:

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

SQLite desteklemediğiADD CONSTRAINT varyantını ALTER TABLEkomutu ( sqlite.org: SQL SQLite mu Uygulamak Not That Özellikleri ).

Bu nedenle, sqlite 3.6.1'e yabancı anahtar eklemenin tek yolu CREATE TABLEaşağıdaki gibidir:

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

Maalesef mevcut verileri geçici bir tabloya kaydetmeniz, eski tabloyu bırakmanız, FK kısıtlamasıyla yeni tabloyu oluşturmanız ve ardından verileri geçici tablodan geri kopyalamanız gerekecektir. ( sqlite.org - SSS: S11 )


28
Eski tabloyu yeniden adlandırmanın, yeni tabloyu oluşturmanın ve verileri geri kopyalamanın daha kolay olduğunu düşünüyorum. Daha sonra eski tabloyu bırakabilirsiniz.
tuinstoel

Evet, bu daha kolay. Sadece sqlite SSS'den alıntı yapıyordum : sqlite.org/faq.html#q11 . Aslında, şu anda sqlite 3'te desteklenen RENAME TObirkaç ALTER TABLEvaryanttan biri.
Daniel Vassallo

3
Olmaması gerekir: FOREIGN KEY (parent_id) REFERENCES parent (id) Doğru, Jonathan "üst tablonun" adını vermedi. Aslında, tabloya kişi adı verilmelidir, ancak ...
igorludi

3
Bu benim için büyük bir sorun gibi görünüyor. Genellikle bir veritabanını döktüğünüzde, önce CREATE TABLE komutlarını verirsiniz. Sonra INSERT INTO komutları ve son olarak ADD CONSTRAINT komutları. Verilerinizde döngüsel (yabancı anahtar değeri) bağımlılık varsa, yabancı anahtarlar uygulanırken verilerinizi ekleyemezsiniz. Ancak yabancı anahtar kısıtlamalarını daha sonra ekleyemezseniz, sıkışmışsınızdır. Elbette ertelenmiş kısıtlamalar var, ama bu çok sakar.
nagylzs

9
Diğer tabloların bu tabloya başvuruları varsa, eski tabloyu ilk yorumda belirtildiği gibi yeniden adlandırmayın! Bu durumda, tüm bu tabloları da yeniden oluşturmanız gerekecektir.
rocknow

57

Tabloyu değiştirirseniz ve kısıtlamayı kullanan sütunu eklerseniz kısıtlama ekleyebilirsiniz.

İlk olarak, parent_id olmadan tablo oluşturun:

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

Ardından tabloyu değiştirin:

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

2
Bu diziye alışmak güzel, ancak bu asıl soruyu yanıtlamıyor: Kısıtlamayı mevcut olana eklemek istiyorum.
Wolf

9

Lütfen https://www.sqlite.org/lang_altertable.html#otheralter adresini kontrol edin

SQLite tarafından doğrudan desteklenen tek şema değiştirme komutları, yukarıda gösterilen "tabloyu yeniden adlandır" ve "sütun ekle" komutlarıdır. Ancak uygulamalar, basit bir işlem dizisi kullanarak bir tablonun biçiminde başka rasgele değişiklikler yapabilir. Bazı X tablosunun şema tasarımında rastgele değişiklikler yapma adımları aşağıdaki gibidir:

  1. Yabancı anahtar kısıtlamaları etkinleştirilmişse, bunları PRAGMA foreign_keys = OFF kullanarak devre dışı bırakın.
  2. Bir işlem başlatın.
  3. Tablo X ile ilişkili tüm dizinlerin ve tetikleyicilerin formatını hatırlayın. Bu bilgiye aşağıdaki 8. adımda ihtiyaç duyulacaktır. Bunu yapmanın bir yolu, aşağıdaki gibi bir sorgu çalıştırmaktır: SELECT type, sql FROM sqlite_master WHERE tbl_name = 'X'.
  4. Tablo X'in istenen revize edilmiş biçiminde yeni bir "new_X" tablosu oluşturmak için CREATE TABLE kullanın. "New_X" adının, var olan herhangi bir tablo adıyla çakışmadığından emin olun.
  5. İçeriği X'ten new_X'e aşağıdaki gibi bir ifade kullanarak aktarın: INSERT INTO new_X SELECT ... FROM X.
  6. Eski tablo X'i bırakın: DROP TABLE X.
  7. Şunu kullanarak new_X'in adını X olarak değiştirin: TABLO DEĞİŞTİRİN new_X X'E DEĞİŞTİRİN.
  8. Tablo X ile ilişkili indeksleri ve tetikleyicileri yeniden yapılandırmak için CREATE INDEX ve CREATE TRIGGER'ı kullanın. Yukarıdaki 3. adımdan kaydedilen tetikleyicilerin ve dizinlerin eski formatını bir kılavuz olarak kullanın ve değişiklik için uygun değişiklikleri yapın.
  9. Herhangi bir görünüm, şema değişikliğinden etkilenen bir şekilde tablo X'e başvurursa, bu görünümleri DROP VIEW kullanarak bırakın ve CREATE VIEW kullanarak şema değişikliğine uyum sağlamak için gerekli olan değişiklikleri yeniden oluşturun.
  10. Yabancı anahtar kısıtlamaları başlangıçta etkinleştirildiyse, şema değişikliğinin herhangi bir yabancı anahtar kısıtlamasını bozmadığını doğrulamak için PRAGMA foreign_key_check'i çalıştırın.
  11. 2. adımda başlatılan işlemi tamamlayın.
  12. Yabancı anahtar kısıtlamaları başlangıçta etkinleştirildiyse, bunları şimdi yeniden etkinleştirin.

Yukarıdaki prosedür tamamen geneldir ve şema değişikliği tabloda depolanan bilgilerin değişmesine neden olsa bile çalışacaktır. Bu nedenle yukarıdaki prosedürün tamamı, bir sütunu düşürmek, sütunların sırasını değiştirmek, UNIQUE kısıtlaması veya PRIMARY KEY eklemek veya kaldırmak, CHECK veya FOREIGN KEY veya NOT NULL kısıtlamaları eklemek veya örneğin bir sütunun veri türünü değiştirmek için uygundur.


4

Evet, yeni bir sütun eklemeden yapabilirsiniz. Veritabanının bozulmasını önlemek için doğru şekilde yapmaya dikkat etmelisiniz, bu yüzden bunu denemeden önce veritabanınızı tamamen yedeklemelisiniz.

özel örneğiniz için:

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

veya daha genel olarak:

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;

Her iki durumda da, muhtemelen herhangi bir değişiklik yapmadan önce SQL tanımının ne olduğunu görmek istersiniz:

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

Replace () yaklaşımını kullanırsanız, çalıştırmadan önce, aşağıdaki komutu çalıştırarak replace () komutunuzu test etmeniz yararlı olabilir:

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

3

Firefox eklenti sqlite-yöneticisini kullanıyorsanız, aşağıdakileri yapabilirsiniz:

Tabloyu bırakıp yeniden oluşturmak yerine, bu şekilde değiştirilebilir.

Sütunlar metin kutusunda, bağlam menüsünü açmak için listelenen son sütun adına sağ tıklayın ve Sütunu Düzenle'yi seçin. TABLO tanımındaki son sütun PRIMARY KEY ise, FOREIGN ANAHTAR tanımını eklemek için önce yeni bir sütun eklemek ve sonra yeni sütunun sütun türünü düzenlemek gerekecektir. Sütun Türü kutusunda, bir virgül ekleyin ve

FOREIGN KEY (parent_id) REFERENCES parent(id)

veri türünden sonra tanım. Değiştir düğmesine tıklayın ve ardından Tehlikeli İşlem iletişim kutusunda Evet düğmesine tıklayın.

Referans: Sqlite Manager


2

Bunu deneyebilirsiniz:

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

-1

Temelde yapamazsınız ama durumu atlayabilirsiniz.

Yabancı anahtar kısıtlamasını mevcut bir tabloya eklemenin doğru yolu aşağıdaki komuttur.

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

Sonra kopyalamak PARENT_ID verileri newCol ve sonra silin PARENT_ID sütunu. Dolayısıyla geçici masaya gerek yoktur.


Görünüşe göre soruyu dikkatlice okumamışsın. Sorun, kısıtlı bir sütun eklemek değil, yalnızca yabancı bir kısıt eklemekti.
Wolf

Hayır! Sorulan soruya cevap vermiyor.
MK

-4

İlk çocuk tablosunda bir sütun ekleyin Cidolarak into alter tableaşağıda koduyla. Bu şekilde yabancı anahtarı Cidüst tablonun birincil anahtarı olarak ekleyebilir ve alt tablodaki yabancı anahtar olarak kullanabilirsiniz ... umarım bu benim için iyi olduğu için size yardımcı olur:

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

1
Bu SQLite'da geçerli değil. Ayrıca bu MS SQL sözdizimidir.
StilesCrisis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.