SQL Server'da INSERT INTO SELECT sorgusunda yinelemelerden kaçının


109

Aşağıdaki iki tablom var:

Table1
----------
ID   Name
1    A
2    B
3    C

Table2
----------
ID   Name
1    Z

İle arasında veri eklemem Table1gerekiyor Table2. Aşağıdaki sözdizimini kullanabilirim:

INSERT INTO Table2(Id, Name) SELECT Id, Name FROM Table1

Bununla birlikte, benim durumumda, içinde yinelenen kimlikler olabilir Table2(benim durumumda, sadece " 1") ve bir hata oluşturacağı için bunu tekrar kopyalamak istemiyorum.

Bunun gibi bir şey yazabilirim:

IF NOT EXISTS(SELECT 1 FROM Table2 WHERE Id=1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 
ELSE
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 WHERE Table1.Id<>1

Bunu kullanmadan yapmanın daha iyi bir yolu var mı IF - ELSE? Bir INSERT INTO-SELECTkoşula bağlı olarak iki ifadeden kaçınmak istiyorum .

Yanıtlar:


201

Kullanarak NOT EXISTS:

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE NOT EXISTS(SELECT id
                    FROM TABLE_2 t2
                   WHERE t2.id = t1.id)

Kullanarak NOT IN:

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE t1.id NOT IN (SELECT id
                       FROM TABLE_2)

Kullanarak LEFT JOIN/IS NULL:

INSERT INTO TABLE_2
  (id, name)
   SELECT t1.id,
          t1.name
     FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.id = t1.id
    WHERE t2.id IS NULL

Üç seçenekten LEFT JOIN/IS NULLdaha az verimlidir. Daha fazla ayrıntı için bu bağlantıya bakın .


9
NOT EXISTS sürümüyle ilgili bir açıklama, bir WITH (HOLDLOCK) ipucuna ihtiyacınız olacak, aksi takdirde kilit alınmayacaktır (çünkü kilitlenecek satır yoktur!), Böylece başka bir iş parçacığı altınızdaki satırı ekleyebilir.
IDisposable

3
İlginç, çünkü her zaman katılmanın alt seçimlerden daha hızlı olduğuna inandım. Belki de bu yalnızca düz birleşimler içindir ve sol birleşimler için geçerli değildir.
Duncan

1
Duncan, birleştirme genellikle daha hızlıdır ve ilişkili alt sorgular olduğunda alt seçim yapar. Seçim listesinde alt sorgunuz varsa, bir birleştirme genellikle daha hızlı olacaktır.
HLGEM

9
NOT EXISTSözellikle bileşik birincil anahtarla kullanışlıdır, NOT INo zaman çalışmaz
38'de

1
@OMGPonies - daha fazla ayrıntı için bağlantınız ölmüş gibi görünüyor. Yararlı olabilecek başka var mı?
FreeMan

36

MySQL'de bunu yapabilirsiniz:

INSERT IGNORE INTO Table2(Id, Name) SELECT Id, Name FROM Table1

SQL Server'da benzer bir şey var mı?


5
Beni bu konuda eğittiğiniz için +1. Çok güzel sözdizimi. Kullandığımdan kesinlikle daha kısa ve daha iyi. Maalesef Sql sunucusunda buna sahip değil.
Ashish Gupta

13
Tamamen doğru değil. Benzersiz bir dizin oluşturduğunuzda, bunu "kopyaları yoksay" olarak ayarlayabilirsiniz, bu durumda SQL Server, kopya ekleme girişimlerini yok sayar.
IamIC

2
Ve SQL Server hala acınacak durumda değil.
Smack Jack

1
Yani SQL Server hala yapamıyor mu?
Ingus

8

Az önce benzer bir sorun yaşadım, DISTINCT anahtar kelimesi sihirle çalışıyor:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1

21
Eğer ekliyorsanız sette çiftleri varsa ben tamamen yanlış anlamak sen, işe yarayacak sürece dan . Bununla birlikte, eklediğiniz küme insert intotabloda zaten bulunan verilerin kopyaları olabilirse yardımcı olmaz .
FreeMan

5

Son zamanlarda aynı problemle karşılaşıyordum ...
İşte MS SQL server 2017'de benim için işe yarayan şey ...
Birincil anahtar, tablo 2'deki ID'de ayarlanmalıdır ...
Sütunlar ve sütun özellikleri elbette ikisi arasında aynı olmalıdır tablolar. Bu, aşağıdaki komut dosyasını ilk çalıştırdığınızda çalışacaktır. Tablo 1'deki yinelenen kimlik eklenmeyecek ...

İkinci kez çalıştırırsanız, bir

PRIMARY KEY kısıtlama hatasının ihlali

Kod bu:

Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1


4

SQL Server'dan ( Benzersiz olması gereken sütunlar) için tabloda Benzersiz bir anahtar dizini ayarlayabilirsiniz.

Sql sunucusundan tablo tasarımına sağ tıklayın Indexes / Keys'i seçin

Yinelenmeyecek sütunları seçin, ardından Benzersiz Anahtar yazın


1

Biraz konu dışı, ancak verileri yeni bir tabloya taşımak istiyorsanız ve olası kopyalar orijinal tablodaysa ve muhtemelen çoğaltılan sütun bir kimlik değilse, GROUP BYşunları yapacaktır:

INSERT INTO TABLE_2
(name)
  SELECT t1.name
  FROM TABLE_1 t1
  GROUP BY t1.name

-1

Basit DELETEönce INSERTyeterli olacaktır:

DELETE FROM Table2 WHERE Id = (SELECT Id FROM Table1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1

Anahtarlama Table1için Table2hangi tablo en bağlı Idve namekorumak istediğiniz eşleştirilecek.


3
Lütfen bunu yapmayın. Temel olarak, "Sahip olduğum her veri değersizdir, hadi bu yeni veriyi ekleyelim!" Diyorsunuz.
Andir

@Andir Herhangi bir nedenden dolayı "Table2" "INSERT" den sonra düşmezse, diğer yöntemleri kullanın, ancak bu, OP'nin istediği şeyi elde etmenin mükemmel bir yoludur.
Sacro

1
Geçerli, ancak kesinlikle daha yavaş ve bir işlem olmadan potansiyel olarak bozucu. Bu rotaya giderseniz, bir İŞLEM'e sarın.
MC9000
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.