Varsa SQL Server Ekle


255

Tabloma veri eklemek istiyorum, ancak yalnızca veritabanımda bulunmayan verileri eklemek istiyorum.

İşte kodum:

ALTER PROCEDURE [dbo].[EmailsRecebidosInsert]
  (@_DE nvarchar(50),
   @_ASSUNTO nvarchar(50),
   @_DATA nvarchar(30) )
AS
BEGIN
   INSERT INTO EmailsRecebidos (De, Assunto, Data)
   VALUES (@_DE, @_ASSUNTO, @_DATA)
   WHERE NOT EXISTS ( SELECT * FROM EmailsRecebidos 
                   WHERE De = @_DE
                   AND Assunto = @_ASSUNTO
                   AND Data = @_DATA);
END

Ve hata şudur:

Msg 156, Seviye 15, Durum 1, Prosedür E-postalarıRecebidosInsert, 11. Satır
'WHERE' anahtar kelimesinin yakınında yanlış sözdizimi.


11
Yineleme olmadığından emin olmak için bu denetime tek başına güvenmemelisiniz, iş parçacığı açısından güvenli değildir ve bir yarış koşulu karşılandığında kopyalar alacaksınız. Gerçekten benzersiz verilere ihtiyacınız varsa, tabloya benzersiz bir kısıt ekleyin ve ardından benzersiz kısıtlama ihlali hatasını yakalayın. Bu cevabı gör
GarethD

1
MERGE sorgusunu kullanabilir veya mevcut değilse (ifade seçin) değerleri girmeye başlayabilirsiniz END
Abdul Hannan Ijaz

Bu kontrole geçip geçmemeniz senaryoya bağlıdır. Örneğin, verileri "statik" bir tabloya yazan bir dağıtım betiği geliştiriyorsanız, bu bir sorun değildir.
AxelWass


2
@GarethD: "İş parçacığı güvenli değil" ne demek? Zarif olmayabilir ama bana doğru görünüyor. Tek bir insertifade her zaman tek bir işlemdir. SQL Server önce alt sorguyu değerlendiriyor ve daha sonra bir noktada ve bir kilit tutmadan eklemeyi yapmaya devam ediyor gibi değil.
Ed Avis

Yanıtlar:


339

Aşağıdaki Kod yerine

BEGIN
   INSERT INTO EmailsRecebidos (De, Assunto, Data)
   VALUES (@_DE, @_ASSUNTO, @_DATA)
   WHERE NOT EXISTS ( SELECT * FROM EmailsRecebidos 
                   WHERE De = @_DE
                   AND Assunto = @_ASSUNTO
                   AND Data = @_DATA);
END

ile değiştirin

BEGIN
   IF NOT EXISTS (SELECT * FROM EmailsRecebidos 
                   WHERE De = @_DE
                   AND Assunto = @_ASSUNTO
                   AND Data = @_DATA)
   BEGIN
       INSERT INTO EmailsRecebidos (De, Assunto, Data)
       VALUES (@_DE, @_ASSUNTO, @_DATA)
   END
END

Güncellendi: (işaret ettiği için @Marc Durdin'e teşekkürler)

Yüksek yük altında, bu yine de bazen başarısız olacağına dikkat edin, çünkü ikinci bir bağlantı, ilk bağlantı INSERT, yani bir yarış koşulunu yürütmeden önce IF NOT EXISTS testini geçebilir. Bir işlemi sarmalamanın bile bunu neden çözmediğine dair iyi bir yanıt için stackoverflow.com/a/3791506/1836776 adresine bakın .


24
Yüksek yük altında, bu yine de bazen başarısız olacağına dikkat edin, çünkü ikinci bir bağlantı, ilk bağlantı INSERT, yani bir yarış koşulunu yürütmeden önce IF NOT EXISTS testini geçebilir. Neden bir işlemi sarmalamanın bile bunu çözmediğine dair iyi bir yanıt için stackoverflow.com/a/3791506/1836776 adresine bakın .
Marc Durdin

11
E-postalardan 1 SEÇİNRecebidos NEREDE De = @_DE VE Assunto = @_ASSUNTO VE Veri = @_DATA * yerine 1 kullanmak daha verimli olur
Reno

1
Her şeyin etrafına bir yazma kilidi koyun ve böylece çoğaltma şansınız kalmaz.
Kevin Finkenbinder

11
@jazzcat select *bu durumda bir EXISTScümle içinde kullanıldığı için hiçbir fark yaratmaz . SQL Server onu her zaman optimize edecek ve bunu uzun zamandır yapıyor. Çok yaşlı olduğum için genellikle bu sorguları şöyle yazıyorum EXISTS (SELECT 1 FROM...)ama artık gerek yok.
Loudenvier

19
Bu tür basit bir soru neden kesinlikten daha fazla şüphe yaratır?
drowa

86

Arayanlar için en hızlı şekilde , son zamanlarda bu kriterler rastladım 50 milyon kayıtları veya daha uzun süre en hızlı olduğu ortaya çıktı görünüşte kullanarak "SELECT'in HARİÇ ... INSERT SELECT ...".

Makaleden bazı örnek kodlar (3. kod bloğu en hızlı olanıydı):

INSERT INTO #table1 (Id, guidd, TimeAdded, ExtraData)
SELECT Id, guidd, TimeAdded, ExtraData
FROM #table2
WHERE NOT EXISTS (Select Id, guidd From #table1 WHERE #table1.id = #table2.id)
-----------------------------------
MERGE #table1 as [Target]
USING  (select Id, guidd, TimeAdded, ExtraData from #table2) as [Source]
(id, guidd, TimeAdded, ExtraData)
    on [Target].id =[Source].id
WHEN NOT MATCHED THEN
    INSERT (id, guidd, TimeAdded, ExtraData)
    VALUES ([Source].id, [Source].guidd, [Source].TimeAdded, [Source].ExtraData);
------------------------------
INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData)
SELECT id, guidd, TimeAdded, ExtraData from #table2
EXCEPT
SELECT id, guidd, TimeAdded, ExtraData from #table1
------------------------------
INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData)
SELECT #table2.id, #table2.guidd, #table2.TimeAdded, #table2.ExtraData
FROM #table2
LEFT JOIN #table1 on #table1.id = #table2.id
WHERE #table1.id is null

7
SEÇİM DIŞINDA seviyorum
Bryan

1
İlk defa EXCEPT kullandım. Sade ve zarif.
jhowe

Ancak EXCEPT, toplu işlemler için verimli olmayabilir.
Aasish Kr. Sharma

1
@Biswa: Bu kriterlere göre değil. Kod siteden edinilebilir. Sonuçların nasıl karşılaştırıldığını görmek için sisteminizde çalıştırabilirsiniz.

1
Dikkat edin, bu ölçütler soruda istendiği gibi açık değerler olmayan "başka bir tablodan eklemeyi" test ediyor
Alex

28

Birleştirme kullanırdım:

create PROCEDURE [dbo].[EmailsRecebidosInsert]
  (@_DE nvarchar(50),
   @_ASSUNTO nvarchar(50),
   @_DATA nvarchar(30) )
AS
BEGIN
   with data as (select @_DE as de, @_ASSUNTO as assunto, @_DATA as data)
   merge EmailsRecebidos t
   using data s
      on s.de = t.de
     and s.assunte = t.assunto
     and s.data = t.data
    when not matched by target
    then insert (de, assunto, data) values (s.de, s.assunto, s.data);
END

meraklısı nedeniyle bununla gidiyorum
jokab

Birleştirmeyi kullanmayı çok isterim ... ancak Bellek İçin Optimize Edilmiş Tablolar için çalışmıyor.
Don Sam

20

Aşağıdaki kodu deneyin

ALTER PROCEDURE [dbo].[EmailsRecebidosInsert]
  (@_DE nvarchar(50),
   @_ASSUNTO nvarchar(50),
   @_DATA nvarchar(30) )
AS
BEGIN
   INSERT INTO EmailsRecebidos (De, Assunto, Data)
   select @_DE, @_ASSUNTO, @_DATA
   EXCEPT
   SELECT De, Assunto, Data from EmailsRecebidos
END

15

Aynı şeyi SQL Server 2012 ile yaptım ve işe yaradı

Insert into #table1 With (ROWLOCK) (Id, studentId, name)
SELECT '18769', '2', 'Alex'
WHERE not exists (select * from #table1 where Id = '18769' and studentId = '2')

4
Elbette işe yaradı, geçici bir tablo kullanıyorsunuz (yani geçici tabloları kullanırken eşzamanlılık konusunda endişelenmenize gerek yok).
drowa

Oracle üzerinde çalışmayın: (\
Yapı

11

INSERTKomutu bir yok WHEREmaddesini - Eğer bu gibi yazmak gerekir:

ALTER PROCEDURE [dbo].[EmailsRecebidosInsert]
  (@_DE nvarchar(50),
   @_ASSUNTO nvarchar(50),
   @_DATA nvarchar(30) )
AS
BEGIN
   IF NOT EXISTS (SELECT * FROM EmailsRecebidos 
                   WHERE De = @_DE
                   AND Assunto = @_ASSUNTO
                   AND Data = @_DATA)
   BEGIN
       INSERT INTO EmailsRecebidos (De, Assunto, Data)
       VALUES (@_DE, @_ASSUNTO, @_DATA)
   END
END

2
Bu prosedür için hataları işlemeniz gerekir çünkü kontrol ve ekleme arasında bir ekin gerçekleşeceği durumlar olacaktır.
Filip De Vos

@FilipDeVos: true - bir olasılık, belki çok olası değil, ama yine de bir olasılık. İyi bir nokta.
marc_s

Ya bir işlem içinde her ikisini de sararsanız? Bu olasılığı engeller mi? (İşlemler konusunda uzman değilim, bu yüzden lütfen bu aptalca bir soruysa affedin.)
David

1
Bkz stackoverflow.com/a/3791506/1836776 bir işlem, @ David bu çözmüyor neden iyi bir cevap için.
Marc Durdin

IF deyiminde: Birden fazla satır kullansanız bile, gerekli komut satırı sayısı sadece bir ise BEGIN & END'i kullanmanıza gerek yoktur, böylece burada atlayabilirsiniz.
Wessam El Mahdy

7

SQL Server sürümünüze (2012?) Bağlı olarak, IF EXISTS'in yanı sıra şunları da kullanabilirsiniz: MERGE şöyle:

ALTER PROCEDURE [dbo].[EmailsRecebidosInsert]
    ( @_DE nvarchar(50)
    , @_ASSUNTO nvarchar(50)
    , @_DATA nvarchar(30))
AS BEGIN
    MERGE [dbo].[EmailsRecebidos] [Target]
    USING (VALUES (@_DE, @_ASSUNTO, @_DATA)) [Source]([De], [Assunto], [Data])
         ON [Target].[De] = [Source].[De] AND [Target].[Assunto] = [Source].[Assunto] AND [Target].[Data] = [Source].[Data]
     WHEN NOT MATCHED THEN
        INSERT ([De], [Assunto], [Data])
        VALUES ([Source].[De], [Source].[Assunto], [Source].[Data]);
END

5

Farklı SQL, aynı prensip. Sadece var olmayan yerdeki cümle başarısız olursa ekleyin

INSERT INTO FX_USDJPY
            (PriceDate, 
            PriceOpen, 
            PriceLow, 
            PriceHigh, 
            PriceClose, 
            TradingVolume, 
            TimeFrame)
    SELECT '2014-12-26 22:00',
           120.369000000000,
           118.864000000000,
           120.742000000000,
           120.494000000000,
           86513,
           'W'
    WHERE NOT EXISTS
        (SELECT 1
         FROM FX_USDJPY
         WHERE PriceDate = '2014-12-26 22:00'
           AND TimeFrame = 'W')

0

Kümelenmiş dizininiz yalnızca bu alanlardan oluşuyorsa, basit, hızlı ve güvenilir seçenek kullanmaktır. IGNORE_DUP_KEY .

Kümelenmiş dizini IGNORE_DUP_KEY ON ile oluşturursanız

Sadece kullanabileceğinizden:

INSERT INTO EmailsRecebidos (De, Assunto, Data) VALUES (@_DE, @_ASSUNTO, @_DATA)

Bu her durumda güvenli olmalıdır!


-2

Aşağıdaki kodda açıklandığı gibi: Aşağıdaki sorguları yürütün ve kendinizi doğrulayın.

CREATE TABLE `table_name` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  `address` varchar(255) NOT NULL,
  `tele` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

Bir kayıt ekleyin:

INSERT INTO table_name (name, address, tele)
SELECT * FROM (SELECT 'Nazir', 'Kolkata', '033') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_name WHERE name = 'Nazir'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0

SELECT * FROM `table_name`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Nazir  | Kolkata   | 033  |
+----+--------+-----------+------+

Şimdi aynı kaydı tekrar eklemeyi deneyin:

INSERT INTO table_name (name, address, tele)
SELECT * FROM (SELECT 'Nazir', 'Kolkata', '033') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_name WHERE name = 'Nazir'
) LIMIT 1;

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Nazir  | Kolkata   | 033  |
+----+--------+-----------+------+

Farklı bir kayıt ekleyin:

INSERT INTO table_name (name, address, tele)
SELECT * FROM (SELECT 'Santosh', 'Kestopur', '044') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_name WHERE name = 'Santosh'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0

SELECT * FROM `table_name`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Nazir  | Kolkata   | 033  |
|  2 | Santosh| Kestopur  | 044  |
+----+--------+-----------+------+

1
Bu MySQL için değil ve soru SQL Server için mi?
Douglas Gaskell

Evet, MySQL için.
vadiraj jahagirdar

-3

GOKomutu kullanabilirsiniz . Bu, bir hatadan sonra SQL ifadelerinin yürütülmesini yeniden başlatır. Benim durumumda birkaç 1000 INSERT deyimim var, bu kayıtlardan birkaçı veritabanında zaten var, hangilerini bilmiyorum. Birkaç 100 işledikten sonra, yürütmenin INSERT, kayıt zaten mevcut olduğu için yapamayacağı bir hata mesajıyla durduğunu gördüm . Oldukça can sıkıcı, ancak bunu çözmek için bir GOçözüm koymak . En hızlı çözüm olmayabilir ama benim sorunum hız değildi.

GO
INSERT INTO mytable (C1,C2,C3) VALUES(1,2,3)
GO
INSERT INTO mytable (C1,C2,C3) VALUES(4,5,6)
 etc ...

GOtoplu ayırıcı mı? Yinelenen kayıtları önlemeye yardımcı olmaz.
Dale K
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.