从数据库中复制Datatable并忽略重复记录的SqlBulkCopy

3

我想知道在将一个数据表传递给SqlBulkCopy时,是否可以忽略已经存在于SQL数据库中的重复记录。如果可以,请告诉我如何操作;如果不行,还有其他什么选择。


你的记录中有主键吗?这个记录没有主键吗? - Arun CM
一个可能的解决方案是使表的列唯一。为此,我们必须在表上创建一个索引,其中包含要检查唯一性的列名。 - Arun CM
3个回答

2

正如之前的帖子所说,这不是内置的。我使用以下方法实现类似的功能:

SQL存储过程接受一个包含所需数据的TableValuedParameter。

在存储过程中,我将所有记录插入到临时表中。一旦数据到达那里,您可以在存储过程中使用SQL的MERGE语句来插入尚不存在的数据。

因此,让我们假设我们的数据仅是存储在名为“people”的表中的人名。我们只保留ID和名称。我还假设此表称为“people”。

下面是我创建的Table Valued Parameter类型(在SQL Server中创建):

CREATE TYPE udt_person AS TABLE(
[id] [INT] NOT NULL,
[name] [nvarchar(50)] NULL
)
GO

我现在创建存储过程:
CREATE PROCEDURE SaveNewPeople @pPeople udt_Person
AS
BEGIN
    -- Create Temp table
    CREATE TABLE #tmpPeople (id INT, name VARCHAR 50)

    -- We will stage all data passed in into temp table
    INSERT INTO #tmpPeople
    SELECT id, name FROM @pPeople

    -- NB: you will need to think about locking strategy a bit here
    MERGE people AS p
    USING #tmpPeople AS t
    ON p.id = t.id
    WHEN NOT MATCHED BY TARGET THEN
        -- We want to insert new person
        INSERT (id, name) VALUES (t.id, t.name)
    WHEN MATCHED THEN
        -- you may not need this, assume updating name for example
        UPDATE SET p.name = t.name

END

现在我们已经有了SQL。

让我们用C#创建大量数据:

DataTable ppl = new DataTable();
ppl.Columns.Add("id", typeof(int));
ppl.Columns.Add("name", typeof(string));

// table is created, let's add some people
var bob = ppl.NewRow();
bob["id"] = 1;
bob["name"] = "Bob";
ppl.Rows.Add(bob);

var jim = ppl.NewRow();
jim["id"] = 2;
jim["name"] = "Jim";
ppl.Rows.Add(jim);

// that's enough people for now, let's call the stored procedure
using(var conn = new SqlConnection("YouConnStringHere"))
{
    using(var cmd = new SqlCommand("SaveNewPeople", conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;

        var tvp = new SqlParameter
        {
          ParameterName = "@pPeople",
          SqlDbType = SqlDbType.Structured,
          Value = ppl,
          TypeName = "udt_person"
        }
        cmd.Parameters.Add(tvp);
        conn.Open();
        cmd.ExecuteNonQuery();
    }

}

希望这能给你一个想法。如果您修改了C#数据表,您应该看到插入、更新或忽略的行。
祝你好运。

是的,谢谢GinjaNinja。我明白你说的了。你能给我一个表值函数和SQL Merge语句的代码示例吗?我正在使用Sql Server 2000和.net 2.0。 - Rahul
我修改了上面的帖子,然后注意到你正在使用SQL 2000。很抱歉,这行不通! - GinjaNinja

2
不,这并不是内置的功能。您需要先在客户端上清理数据或将其插入临时表中。

0
另一种方法是创建一个数据库触发器来替换由SqlBulkCopy启动的插入。性能将受到影响,取决于批处理的大小等因素,但它仍然可以工作。
CREATE TABLE [dbo].[TempTable] (
    [Id] INT IDENTITY PRIMARY KEY,
    [Val] NVARCHAR(20)
)

GO

CREATE OR ALTER TRIGGER [IgnoreDuplicates] ON [dbo].[TempTable] 
INSTEAD OF INSERT 
AS 
BEGIN
    SET NOCOUNT ON
    INSERT INTO [dbo].[TempTable]([Val])
    SELECT [Val] FROM [INSERTED] WHERE [Val] NOT IN (
        SELECT [Val] FROM [dbo].[TempTable]
    )
END

GO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接