SQL Server插入(如果不存在)

Fra*_*lho 205 sql sql-server stored-procedures sql-server-2008

我想在我的表中插入数据,但只插入我的数据库中不存在的数据!

这是我的代码:

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
Run Code Online (Sandbox Code Playgroud)

错误是:

消息156,级别15,状态1,过程EmailsRecebidosInsert,第11行
关键字'WHERE'附近的语法不正确.

Imr*_*han 289

而不是代码

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
Run Code Online (Sandbox Code Playgroud)

用...来代替

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
Run Code Online (Sandbox Code Playgroud)

更新:(感谢@Marc Durdin指点)

请注意,在高负载下,这仍然有时会失败,因为第二个连接可以在第一个连接执行INSERT之前通过IF NOT EXISTS测试,即竞争条件.请参阅stackoverflow.com/a/3791506/1836776,以获得有关为什么即使在事务中包装也无法解决此问题的答案.

  • 请注意,在高负载下,这仍然有时会失败,因为第二个连接可以在第一个连接执行INSERT之前通过IF NOT EXISTS测试,即竞争条件.请参阅http://stackoverflow.com/a/3791506/1836776,以获得有关为什么即使在事务中包装也无法解决此问题的答案. (17认同)
  • 为什么这种简单的问题产生的疑问多于确定性? (10认同)
  • @jazzcat`select*`在这种情况下没有任何区别,因为它被用在`EXISTS`子句中.SQL Server将始终对其进行优化,并且已经使用了很长时间.因为我很老,所以我经常将这些查询写成`EXISTS(SELECT 1 FROM ...)`但不再需要了. (9认同)
  • SELECT 1 FROM EmailsRecebidos WHERE De = @_DE AND Assunto = @_ASSUNTO AND Data = @_DATA使用1而不是*会更高效 (8认同)

小智 63

对于那些寻找最快速方式的人来说,我最近遇到了这些基准测试,显然使用"INSERT SELECT ... EXCEPT SELECT ..."对于5000万条以上的记录来说是最快的.

这是文章中的一些示例代码(第3块代码是最快的):

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
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢EXCEPT SELECT (5认同)

Bre*_*der 23

我会使用合并:

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
Run Code Online (Sandbox Code Playgroud)

  • 我会选择这个,因为它更漂亮 (3认同)

小智 17

试试下面的代码

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
Run Code Online (Sandbox Code Playgroud)

  • 请提供一个很好的解释,说明您的代码为何有效! (2认同)

Leo*_*son 14

只需更改代码以使用 SELECT 而不是 VALUES

   INSERT INTO EmailsRecebidos (De, Assunto, Data)
   SELECT @_DE, @_ASSUNTO, @_DATA
   WHERE NOT EXISTS (SELECT * FROM EmailsRecebidos 
                   WHERE De = @_DE
                   AND Assunto = @_ASSUNTO
                   AND Data = @_DATA);
Run Code Online (Sandbox Code Playgroud)

  • 这里只有正确答案,难以置信 (3认同)
  • @BenderBoy 哇!我刚刚看到了。我知道我错过了一些东西!你能想象我已经做了 17 年左右的后端工作却从未实现这一目标吗?!我总是使用“values(...)”并放入根据之前的查询声明的变量。这是一种顿悟。该死...谢谢,伙计! (2认同)

Mal*_*ine 13

不同的SQL,相同的原理。仅当 where not exists 中的子句失败时才插入

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')
Run Code Online (Sandbox Code Playgroud)

  • 非常优雅的解决方案,这也应该解决竞争条件问题。 (3认同)
  • 因为它位于查询本身中,而不是位于过程中的前面。因此,在插入期间检查该值是否存在时,该值不可能重复。 (2认同)

Ale*_*osh 11

如果您的聚集索引仅包含这些字段,那么简单、快速且可靠的选项是使用IGNORE_DUP_KEY

如果您使用 IGNORE_DUP_KEY ON 创建聚集索引

然后你就可以使用:

INSERT INTO EmailsRecebidos (De, Assunto, Data) VALUES (@_DE, @_ASSUNTO, @_DATA)
Run Code Online (Sandbox Code Playgroud)

在所有情况下这应该都是安全的!


mar*_*c_s 10

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
Run Code Online (Sandbox Code Playgroud)

  • 您需要处理此过程的错误,因为有时会在检查和插入之间发生插入。 (2认同)
  • 在IF语句中:如果所需的命令行数量只有一行,即使您使用了多行,也不需要使用BEGIN和END,所以这里可以省略。 (2认同)

小智 9

我使用SQL SERVER 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')
Run Code Online (Sandbox Code Playgroud)

  • 当然它可以工作,你使用临时表(即使用临时表时不需要担心并发). (4认同)

Don*_*Don 6

除了IF EXISTS之外,您还可以使用MERGE,具体取决于您的SQL Server版本(2012?):

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
Run Code Online (Sandbox Code Playgroud)