SQL Server插入(如果不存在)

SQL Server Insert if not exist

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

这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
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, Procedure EmailsRecebidosInsert, Line 11
Incorrect syntax near the keyword 'WHERE'.


而不是低于代码

1
2
3
4
5
6
7
8
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

替换为

1
2
3
4
5
6
7
8
9
10
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指点)

请注意,在高负载下,这有时仍然会失败,因为在第一个连接执行插入之前,第二个连接可以通过if not exists测试,即竞争条件。请参阅stackoverflow.com/a/3791506/1836776,了解为什么即使在事务中包装也不能解决这个问题。


对于那些寻找最快方法的人,我最近遇到了这些基准,其中显然使用了"insert select…"除了选择…"结果是5000万条或更多记录中最快的。

以下是本文中的一些示例代码(第三块代码最快):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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


我将使用合并:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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


尝试以下代码

1
2
3
4
5
6
7
8
9
10
11
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


INSERT命令没有WHERE子句-您必须这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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


我对SQL Server 2012也做了同样的事情,它起到了作用。

1
2
3
INSERT INTO #table1 WITH (ROWLOCK) (Id, studentId, name)
SELECT '18769', '2', 'Alex'
WHERE NOT EXISTS (SELECT * FROM #table1 WHERE Id = '18769' AND studentId = '2')


取决于您的版本(2012年?)对于SQL Server,除了if exists之外,还可以使用merge,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
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

如下面的代码所述:执行下面的查询并验证您自己(它们是自解释的)

1
2
3
4
5
6
7
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;

插入记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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  |
+----+--------+-----------+------+

现在,再次尝试插入相同的记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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  |
+----+--------+-----------+------+

插入其他记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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  |
+----+--------+-----------+------+


你可以使用go命令。这将在出错后重新启动SQL语句的执行。在我的例子中,我有一些1000个insert语句,其中一些记录已经存在于数据库中,我只是不知道哪些记录。我发现,在处理了几百个之后,执行只会停止,并显示一条错误消息,因为记录已经存在,所以无法插入。挺烦人的,不过一走了之就解决了这个问题。这可能不是最快的解决方案,但速度不是我的问题。

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