SQL UPSERT QUERY W/基于 3 个字段的唯一重复行 (C# VisStudio)

Posted

技术标签:

【中文标题】SQL UPSERT QUERY W/基于 3 个字段的唯一重复行 (C# VisStudio)【英文标题】:SQL UPSERT QUERY W/ Duplicate Rows Made Unique Based Upon 3 Fields (C# VisStudio) 【发布时间】:2020-09-23 20:02:48 【问题描述】:

背景是我正在建立一个 SQL 连接,该连接采用 .csv 文件并将其导入 SQL Server 数据库表。

我遇到的问题是,我遇到了查询语法问题,因为我正在导入的 .csv 文件中的一行没有唯一标识符。需要 3 个字段组合才能使一行唯一/不同。

.csv 文件数据的粗略示例,.csv 列的前三列可以一起考虑以构成唯一行:

Order_Id  Product_Id  Date    Other (etc...)
    1         1a       1/9      q
    1         2a       1/9      q
    1         2a       1/10     e
    2         1a       1/9      e
    2         2a       1/10     e

这是我在 Visual Studios 中简化的查询语法(我实际上从 .csv 文件中导入了 25 个左右的列,因此为了保持直截了当/简单,我在两个 .csv 文件中使列名称完全相同。 csv 文件和 SQL-Server 表),但基本语法如下所示:

private void SaveImportDataToDatabase(DataTable importData)

    using (SqlConnection conn = new SqlConnection("Server=localhost;Database=my_Database;Trusted_Connection=True;"))
    
        conn.Open();
        foreach (DataRow importRow in importData.Rows)
        
            
            SqlCommand cmd = new SqlCommand("IF EXISTS(SELECT DISTINCT Order_id, Product_Id, Date FROM Sales WHERE Order_id = @Order_id AND Product_Id = @Product_Id AND Date = @Date) UPDATE SQL_Sales SET Order_id = @Order_id WHERE Order_id = @Order_id ELSE INSERT INTO SQL_Sales (order_id, Product_Id, Date)" +
                                            "VALUES (@order_id, @Product_Id, @Date);", conn);
            
            cmd.Parameters.AddWithValue("@Order_id", importRow["Order_id"]);
            cmd.Parameters.AddWithValue("@Product_Id", importRow["Product_Id"]);
            cmd.Parameters.AddWithValue("@Date", importRow["Date"]);
            
            cmd.ExecuteNonQuery();

        
    

导入后,我在 SQL Server 表中发现了一些问题,

    order_id 字段将为空 它只导入了非常少量的数据,大约 2000 条记录中的 50 条 如果我重新导入数据并更改 .csv 文件,比如使用一个新行,我会得到 2000 条记录中的 100 条

我不确定我正在尝试做的事情是否可行或值得。我应该更多地分解它而不是在一个查询中完成所有操作吗?我不一定是编码新手,但我不经常编码/我很生疏,这是我的第一个 C# 项目,所以如果可能的话,请给我一些耐心。

只是想添加更多代码以响应@casey crookston,问题 2 和 3 可能与我的循环有关

private void btnImport_Click(object sender, EventArgs e)
    
       Cursor = Cursors.WaitCursor;            
       DataTable importData = GetDataFromFile();

        if (importData == null) return;
        SaveImportDataToDatabase(importData);

        MessageBox.Show("Import Successful");
        txtFileName.Text = string.Empty;

        Cursor = Cursors.Default;
    

    private DataTable GetDataFromFile()
    
        DataTable importedData = new DataTable();
       try
        
            using (StreamReader sr = new StreamReader(txtFileName.Text))
            
                string header = sr.ReadLine();
                if (string.IsNullOrEmpty(header))
                
                    MessageBox.Show("No File Data");
                    return null;
                

                string[] headerColumns = header.Split(',');
                foreach (string headerColumn in headerColumns)
                
                    importedData.Columns.Add(headerColumn);
                

                while (!sr.EndOfStream)
                
                    string line = sr.ReadLine();

                    if (string.IsNullOrEmpty(line)) continue;

                    string[] fields = line.Split(',');
                    DataRow importedRow = importedData.NewRow();

                    for(int i = 1; i < fields.Count(); i++)
                    
                        importedRow[i] = fields[i];
                    

                    importedData.Rows.Add(importedRow);
                
            
        
        catch (Exception e)
        
            Console.WriteLine("The file could not be read:");
            Console.WriteLine(e.Message);
        

        return importedData;
    

【问题讨论】:

我假设您在此方法的开头放置了一个断点,然后检查以确保 importData 中包含您期望的所有数据? 虽然我不确定我是否理解你的意思,但你的评论让我重新检查了我的 for 循环,修复了 "for(int i = 1; i 【参考方案1】:

这看起来是使用 SQL Server 的 MERGE 语法的好地方:

merge sales s
using (values(@product_id, @order_id, @date, @other_1, @other_2)) 
    as p(order_id, product_id, date, other_1, other_2)
on (s.product_id = p.product_id and s.order_id = p.order_id and s.date = p.date)
when matched then 
    update set s.other_1 = p.other_1, s.other_2 = p.other_2
when not matched by target then 
    insert(order_id, product_id, date, other_1, other_2)
    values(p.order_id, p.product_id, p.date, p.other_1, p.other_2)

这使用前 3 列作为主键;当一个元组已经存在时,other_1other_2 列会更新为本来应该插入的值。

【讨论】:

谢谢,我想我明白了,会试一试并回复你 让它与您提供的这个大纲一起工作,非常感谢!我在问题中列出的其他问题似乎与我的循环有关,尤其是这一行,“for(int i = 1; i

以上是关于SQL UPSERT QUERY W/基于 3 个字段的唯一重复行 (C# VisStudio)的主要内容,如果未能解决你的问题,请参考以下文章

Postgres UPSERT - 如果所有数据都相同,请不要更新?

SQL Server中Upsert的三种方式

SQL Server 2005 中的原子 UPSERT

sql Comando UPSERT(postgres)

sleep

如何使用基于 _id 的 Mongoose 进行 upsert?