SQL 基于 3 个字段使重复行唯一化的 UPSERT 查询 (C# VisStudio)
SQL UPSERT QUERY W/ Duplicate Rows Made Unique Based Upon 3 Fields (C# VisStudio)
背景是我正在建立一个 SQL 连接,该连接接受一个 .csv 文件并将其导入 SQL 服务器数据库 table。
我 运行 遇到的问题是,我在查询语法方面遇到了问题,因为我正在导入的 .csv 文件中的行没有唯一标识符。需要 3 个字段组合成一行 unique/distinct.
.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 个左右的列,因此为了保持内容 straight/simple 我使列名完全相同在 .csv 文件和 SQL-Server table) 中,但基本语法如下所示:
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 服务器 table、
中发现一些问题
- order_id 字段将为空
- 它只导入了非常少量的数据,大约 2000 条记录中的 50 条
- 如果我重新导入数据并更改 .csv 文件,比如说一个新行,我会得到 2000 条记录中的 100 条
我不确定我正在尝试做的事情是否可行或值得。我是否应该将其分解得更多,而不是在一个查询中全部完成?我不一定是编码新手,但我的编码并不十分 often/I 很生疏,这是我的第一个 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;
}
这看起来是使用 SQL 服务器的 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_1
和 other_2
列将更新为本来会插入的值。
背景是我正在建立一个 SQL 连接,该连接接受一个 .csv 文件并将其导入 SQL 服务器数据库 table。
我 运行 遇到的问题是,我在查询语法方面遇到了问题,因为我正在导入的 .csv 文件中的行没有唯一标识符。需要 3 个字段组合成一行 unique/distinct.
.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 个左右的列,因此为了保持内容 straight/simple 我使列名完全相同在 .csv 文件和 SQL-Server table) 中,但基本语法如下所示:
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 服务器 table、
中发现一些问题- order_id 字段将为空
- 它只导入了非常少量的数据,大约 2000 条记录中的 50 条
- 如果我重新导入数据并更改 .csv 文件,比如说一个新行,我会得到 2000 条记录中的 100 条
我不确定我正在尝试做的事情是否可行或值得。我是否应该将其分解得更多,而不是在一个查询中全部完成?我不一定是编码新手,但我的编码并不十分 often/I 很生疏,这是我的第一个 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;
}
这看起来是使用 SQL 服务器的 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_1
和 other_2
列将更新为本来会插入的值。