【问题标题】:csv import sqlserver without duplicates using c#csv导入sqlserver没有重复使用c#
【发布时间】:2016-09-27 10:04:58
【问题描述】:

我有一个将 csv 文件导入 sql server 数据库的小程序 但是当您按两次按钮或其他内容时,它会添加重复项。 我需要它,这样当有重复时它会跳过那个。 如果有人可以帮助我编写代码,那就太棒了。

编辑:我注意到有人说我需要在按钮工作时禁用该按钮,而这是解决一件事的方法,我还希望如果数据库中已有某些内容,它将在相同时跳过该部分在 csv 文件中与在数据库中一样。

代码:

private void button1_Click(object sender, EventArgs e)
    {
        SqlConnection con = new SqlConnection(@"server=localhost;Initial Catalog=klantbestand;Integrated Security=SSPI;");
            string filepath = @"C:\clients TEST.csv";

        StreamReader sr = new StreamReader(filepath);

        string line = sr.ReadLine();
        string[] value = line.Split(';');
        DataTable dt = new DataTable();
        DataRow row;

        foreach (string dc in value)
        {
            dt.Columns.Add(new DataColumn(dc));
        }

        while (!sr.EndOfStream)
        {
            value = sr.ReadLine().Split(';');
            if (value.Length == dt.Columns.Count)
            {
                row = dt.NewRow();
                row.ItemArray = value;
                dt.Rows.Add(row);
            }
        }

        SqlBulkCopy bc = new SqlBulkCopy(con.ConnectionString, SqlBulkCopyOptions.TableLock);
        bc.DestinationTableName = "GegevensCSV";
        bc.BatchSize = dt.Rows.Count;
        con.Open();
        bc.WriteToServer(dt);
        bc.Close();
        con.Close();
    }

【问题讨论】:

  • 在按钮工作时制作某种互斥锁或禁用按钮不是更安全的选择吗?
  • 这不仅仅是当你按下按钮两次的时候。当数据库中已经有一个值与 csv 文件中的值相同时。

标签: c# sql-server csv


【解决方案1】:

将数据批量加载到数据库表中时,我遵循的一般原则是创建一个新的临时表(仅在导入期间暂时存在),将所有数据批量加载到其中,然后运行查询将该数据迁移到最终目标表中。之后,只需删除临时表。

这有几个好处:

  1. 您可以increase initial bulk loading performance 进入 db/reduce 初始争用,而不会影响使用目标表的其他进程(例如 SqlBulkCopyOptions.TableLock)
  2. 然后您可以在第二个迁移步骤中实现一些东西,例如不复制最终目标表中已经存在的数据(如您所愿)

【讨论】:

  • 你能帮我写一些代码吗?我对编程还不是很了解。
【解决方案2】:

@AdaTheDev 的答案是正确的。但是还有另一种方法可以做到这一点。

如果在进行批量导入时出现重复插入的问题,您也可以使用存储过程而不是使用批量复制功能来处理它。

在 SQL Server 2005 之后的版本中,您可以使用“表值参数”,即您可以将整个表作为参数传递到存储过程并在服务器端对其进行操作。

如果您通过存储过程参数将表传递到服务器端,则可以使用“合并命令”,其中合并命令是一个 upsert 命令。即,您可以以最安全、最快捷的方式从同一命令中插入、更新或删除所需的记录。

以下是有关该过程的一些详细信息:

第一步: 在 SQL Server 中创建为表值参数;命令是:

CREATE TYPE [dbo].[TableTypeName] AS TABLE(
    [ColumnName1] [DataType],
    [ColumnName2] [DataType],
    [ColumnName3] [DataType]
)
GO

这里的“ColumnName1,2,3”是表列的名称,“DataType”是为这些列分配的sql server数据类型。

第 2 步:使用合并命令创建存储过程:

CREATE PROCEDURE [dbo].[ProcedureName]

    @TableTypeName [dbo].[TableTypeName] READONLY

AS 
BEGIN

    DECLARE @InsertedRowsId TABLE 
    (
        [InsertedRowId] [DataType] NOT NULL
    );
    DELETE FROM @InsertedRowsId;

    BEGIN TRY

        BEGIN TRANSACTION 

            -- Merge command 
            MERGE INTO [dbo].[TableName] AS [Target]
            USING (     
                    SELECT * FROM @TableTypeName                            
                ) AS [Source]
            -- Candidate Keys: All the column(s) combination that makes the record(s) unique.
            ON [Target].[ColumnName1] = [Source].[ColumnName1] -- Always false, ensures all rows copied
            AND [Target].[ColumnName2] = [Source].[ColumnName2] 
            AND [Target].[ColumnName2] = [Source].[ColumnName2] 

            WHEN NOT MATCHED THEN   
                INSERT 
                    (
                        [ColumnName1]
                        ,[ColumnName1]
                        ,[ColumnName1]
                    )
                VALUES
                    (
                        [Source].[ColumnName1]
                        ,[Source].[ColumnName1]
                        ,[Source].[ColumnName1]
                    );          

        COMMIT TRANSACTION

    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION
    END CATCH

END 

第 3 步:现在是调用存储过程的最后一步。

Private void button1_Click(object sender, EventArgs e)
{
    string connectionString = @"server=localhost;Initial Catalog=klantbestand;Integrated Security=SSPI;";
    string filepath = @"C:\clients TEST.csv";

    StreamReader sr = new StreamReader(filepath);

    string line = sr.ReadLine();
    string[] value = line.Split(';');
    DataTable dt = new DataTable();
    DataRow row;

    foreach (string dc in value)
    {
        dt.Columns.Add(new DataColumn(dc));
    }

    while (!sr.EndOfStream)
    {
        value = sr.ReadLine().Split(';');
        if (value.Length == dt.Columns.Count)
        {
            row = dt.NewRow();
            row.ItemArray = value;
            dt.Rows.Add(row);
        }
    }

    if (dt.Rows.Count>0) {
        using (SqlConnection connection = new SqlConnection(connectionString)) {
        connection.Open();
        using (SqlCommand command = connection.CreateCommand()) {
                command.CommandText = "dbo.ProcedureName";
                command.CommandType = CommandType.StoredProcedure;

                SqlParameter parameter;            
                parameter = command.Parameters.AddWithValue("@TableTypeName", dt);              
                parameter.SqlDbType = SqlDbType.Structured;
                parameter.TypeName = "dbo.TableTypeName";

                command.ExecuteNonQuery();
            }
        }
    }        
}

通过这种方式,您可以批量导入数据而不会出现任何重复记录。 甚至,如果需要,您可以记录插入或异常详细信息。

【讨论】:

  • 所以我尝试了这个,我得到了异常 System.Data.dll 中发生了“System.Data.SqlClient.SqlException”类型的未处理异常附加信息:字符串或二进制数据将被截断。表值参数“@TableTypeName”的数据不符合参数的表类型。 SQL Server 错误为:8152,状态:10 语句已终止。
  • 例外是字符串截断类型,即为 varchar 字段定义的大小不够。其次,FOR ""@TableTypeName"不符合参数的表类型"是指存储过程中定义的参数名与SqlParameter中设置的参数名不匹配
猜你喜欢
  • 1970-01-01
  • 2013-07-18
  • 2021-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-18
  • 2020-02-07
  • 2021-09-21
相关资源
最近更新 更多