SQL सर्वर सम्मिलित करें यदि मौजूद नहीं है


243

मैं अपनी तालिका में डेटा सम्मिलित करना चाहता हूं, लेकिन केवल वही डेटा सम्मिलित करता हूं जो मेरे डेटाबेस में पहले से मौजूद नहीं है।

यहाँ मेरा कोड है:

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

और त्रुटि यह है:

Msg 156, Level 15, State 1, प्रक्रिया EmailsRecebidosInsert, लाइन 11
'WHERE' कीवर्ड के पास गलत सिंटैक्स।


10
आपको डुप्लिकेट सुनिश्चित करने के लिए अकेले इस चेक पर भरोसा नहीं करना चाहिए, यह थ्रेड सुरक्षित नहीं है और रेस की शर्त पूरी होने पर आपको डुप्लिकेट मिल जाएगा। यदि आपको वास्तव में अद्वितीय डेटा की आवश्यकता है, तो तालिका में एक अद्वितीय बाधा जोड़ें, और फिर अद्वितीय बाधा उल्लंघन त्रुटि को पकड़ें। इसका उत्तर देखें
गैरेथ 12

1
आप MERGE क्वेरी का उपयोग कर सकते हैं या यदि मौजूद नहीं हैं (चुनिंदा स्टेटमेंट) इंसर्ट वैल्यू शुरू करें END
अब्दुल हन्नान एजाज़

यह परिदृश्य पर निर्भर करता है कि आपको इस चेक पर रिले करना चाहिए या नहीं। यदि आप एक ऐसी तैनाती स्क्रिप्ट विकसित कर रहे हैं जो उदाहरण के लिए "स्थिर" तालिका में डेटा लिखती है, तो यह कोई समस्या नहीं है।
एक्सलवॉस


2
@ गैरेथ: आपका क्या मतलब है "थ्रेड सेफ नहीं"? यह सुरुचिपूर्ण नहीं हो सकता है लेकिन यह मुझे सही लगता है। एक एकल insertकथन हमेशा एक ही लेनदेन होता है। यह ऐसा नहीं है कि SQL सर्वर पहले सबक्विरी का मूल्यांकन करता है और बाद में कुछ बिंदु पर, और बिना लॉक पकड़े, इंसर्ट करता है।
एड एविस

जवाबों:


322

कोड के बजाय

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

से बदलो

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

अपडेट किया गया (इंगित करने के लिए @Marc Durdin का धन्यवाद)

ध्यान दें कि उच्च भार के तहत, यह अभी भी कभी-कभी विफल हो जाएगा, क्योंकि दूसरा कनेक्शन INSERT निष्पादित करने से पहले IF रेसिस परीक्षा पास कर सकता है, अर्थात दौड़ की स्थिति। देखें stackoverflow.com/a/3791506/1836776 क्यों किसी लेनदेन में भी रैपिंग इस का समाधान नहीं करता पर एक अच्छा जवाब के लिए।


20
ध्यान दें कि उच्च भार के तहत, यह अभी भी कभी-कभी विफल हो जाएगा, क्योंकि दूसरा कनेक्शन INSERT निष्पादित करने से पहले IF रेसिस परीक्षा पास कर सकता है, अर्थात दौड़ की स्थिति। देखें कि stackoverflow.com/a/3791506/1836776 पर एक अच्छे उत्तर के लिए भी लेन-देन में लपेटने से इसका समाधान क्यों नहीं होता है।
मार्क डर्डिन

11
EmailsRecebidos से 1 का चयन करें जहां = @_DE और Assunto = @_ASSUNTO और डेटा = @_DATA * के बजाय 1 का उपयोग करने के लिए अधिक कुशल होगा
Reno

1
पूरी चीज़ के चारों ओर राइट लॉक लगाएं और फिर आपके पास डुप्लिकेट का कोई मौका नहीं होगा।
केविन फिन्केन्बिंडर

10
select *इस मामले में @jazzcat को कोई फर्क नहीं पड़ता क्योंकि इसका उपयोग एक EXISTSखंड में किया जा रहा है । SQL सर्वर हमेशा इसे ऑप्टिमाइज़ करेगा और सदियों से करता आ रहा है। चूंकि मैं बहुत बूढ़ा हो गया हूं, मैं आमतौर पर इन प्रश्नों को लिखता हूं EXISTS (SELECT 1 FROM...)लेकिन इसकी अब कोई आवश्यकता नहीं है
1

16
इस तरह के सरल प्रश्न निश्चितता से अधिक संदेह क्यों पैदा करते हैं?
ड्रूना

77

सबसे तेज़ रास्ता खोजने वालों के लिए , मैं हाल ही में इन बेंचमार्क पर आया था जहाँ जाहिरा तौर पर "INSERT Select ... EXCEPT SELECT ..." का उपयोग करके 50 मिलियन रिकॉर्ड या उससे अधिक के लिए सबसे तेज़ निकला।

यहां लेख से कुछ नमूना कोड (कोड का तीसरा ब्लॉक सबसे तेज था):

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

6
मुझे
ब्रायन

1
पहली बार मैंने EXCEPT का उपयोग किया है। सरल और सुरुचिपूर्ण।
jhowe

लेकिन EXCEPT थोक संचालन के लिए कुशल नहीं हो सकता है।
आसिश क्र। शर्मा

EXCEPT उतना कुशल नहीं है।
बिस्वा

1
@ बिस्वा: उन बेंचमार्क के अनुसार नहीं। कोड साइट से उपलब्ध है। परिणामों की तुलना कैसे करें, यह देखने के लिए इसे अपने सिस्टम पर चलाने के लिए स्वतंत्र महसूस करें।

25

मैं एक मर्ज का उपयोग करेगा:

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

im इसके साथ जा रहा है क्योंकि इसके
कट्टर

मुझे मर्ज का उपयोग करना अच्छा लगेगा ... लेकिन यह मेमोरी ऑप्टिमाइज़्ड टेबल्स के लिए काम नहीं करता है।
डॉन सैम

20

कोड के नीचे प्रयास करें

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

11

INSERTआदेश एक नहीं है WHEREखंड - आप इसे इस तरह से लिखने के लिए होगा:

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

1
आपको इस प्रक्रिया के लिए त्रुटियों को संभालने की आवश्यकता है क्योंकि ऐसे मामले होंगे जहां चेक और सम्मिलित के बीच एक इंसर्ट होगा।
फिलिप दे वोस

@FilipDeVos: सच - एक संभावना, शायद बहुत संभावना नहीं है, लेकिन अभी भी एक संभावना है। अच्छी बात।
marc_s

क्या होगा यदि आप एक लेनदेन के भीतर दोनों को लपेटते हैं? क्या यह संभावना को अवरुद्ध करेगा? (मैं लेन-देन का कोई विशेषज्ञ नहीं हूं, इसलिए यदि यह एक मूर्खतापूर्ण प्रश्न है तो कृपया क्षमा करें।)
डेविड

1
एक लेन-देन इसे हल नहीं करता है, इस बारे में अच्छे जवाब के लिए stackoverflow.com/a/3791506/1836776 देखें ।
मार्क डर्डिन

IF स्टेटमेंट में: BEGIN & END का उपयोग करने की कोई आवश्यकता नहीं है यदि आवश्यक कमांड लाइनों की संख्या सिर्फ एक है, भले ही आपने एक से अधिक लाइन का उपयोग किया हो, तो आप इसे यहां छोड़ सकते हैं।
वेसम एल माेदी

11

मैंने SQL सर्वर 2012 के साथ एक ही काम किया और यह काम किया

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
बेशक यह काम किया है, आप एक अस्थायी तालिका का उपयोग कर रहे हैं (यानी आपको अस्थायी तालिकाओं का उपयोग करते समय संगामिति के बारे में चिंता करने की आवश्यकता नहीं है)।
ड्रू

6

IF EXISTS से अलग SQL सर्वर के आपके संस्करण (2012;) के आधार पर आप MERGE का उपयोग भी कर सकते हैं :

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

2

अलग एसक्यूएल, एक ही सिद्धांत। केवल तभी सम्मिलित करें यदि जहाँ मौजूद नहीं है वह खंड विफल रहता है

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')

-1

जैसा कि नीचे दिए गए कोड में बताया गया है: प्रश्नों के नीचे निष्पादित करें और खुद को सत्यापित करें।

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;

एक रिकॉर्ड डालें:

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  |
+----+--------+-----------+------+

अब, उसी रिकॉर्ड को फिर से सम्मिलित करने का प्रयास करें:

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  |
+----+--------+-----------+------+

एक अलग रिकॉर्ड डालें:

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
यह MySQL के लिए नहीं है और प्रश्न SQL सर्वर के लिए है?
डगलस गास्केल

हाँ MySQL के लिए।
वदिराज जहगीरदार

-2

आप GOकमांड का उपयोग कर सकते हैं । यह एक त्रुटि के बाद SQL कथनों के निष्पादन को पुनः आरंभ करेगा। मेरे मामले में मेरे पास कुछ 1000 INSERT स्टेटमेंट हैं, जहां मुट्ठी भर उन रिकॉर्ड्स पहले से ही डेटाबेस में मौजूद हैं, मुझे अभी यह नहीं पता है कि कौन से हैं। मैंने पाया कि कुछ 100 प्रसंस्करण के बाद, निष्पादन सिर्फ एक त्रुटि संदेश के साथ बंद हो जाता है कि यह INSERTरिकॉर्ड के रूप में मौजूद नहीं हो सकता है। काफी कष्टप्रद है, लेकिन इसे GOहल करना। यह सबसे तेज़ समाधान नहीं हो सकता है, लेकिन गति मेरी समस्या नहीं थी।

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

GOएक बैच विभाजक है? यह डुप्लिकेट रिकॉर्ड को रोकने में सहायता नहीं करता है।
डेल के
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.