C# SqlCommandBuilder,CommandUpdate - 如何使用外部连接表编写基于选择的正确更新

Posted

技术标签:

【中文标题】C# SqlCommandBuilder,CommandUpdate - 如何使用外部连接表编写基于选择的正确更新【英文标题】:C# SqlCommandBuilder , CommandUpdate - how to write correct update based on select with outer join tables 【发布时间】:2021-12-17 15:06:44 【问题描述】:

我想要更新 2 个字段:p.FlagaWaznosci 和 p.Notatka

我的选择看起来像:

Select  DISTINCT p.id,p.Model_Number,p.Product_Name,p.Website_Link,p.Entry_date,p.LastUpdate_date,p.PrzydzialRozmiarow_ID,p.FlagaWaznosci,p.Notatka,pr.NazwaRozmiarowki,wd.LINK_StockX 
from Products p with(nolock)
left outer  join Widok_Model_Sklep_Stockx_Linki wd with(nolock) on wd.Product_ID = p.id 
left outer join PrzydzialRozmiarow pr with(nolock) on pr.id = p.PrzydzialRozmiarow_ID 
inner join Shops s with(nolock) on s.ID = p.Shop_ID 

只有外连接来获取我需要在 gridview 中显示的正确数据。现在,当值 p.FlagaWaznosci 或 p.Notatka 更改时,我想将更新保存在我的数据库中。

我尝试使用

//loads dataand fill to gridview
 DataTable WszystkieProduktyDlaDanegoSklepu;
 SqlDataAdapter sda555123 = new SqlDataAdapter("here is my select", conn123);
 sda555123.Fill(WszystkieProduktyDlaDanegoSklepu);
 
 //later update table Prooducts and save changed on p.Notatka and p.FlagaWaznosci
 cmdbl = new SqlCommandBuilder(sda555123);
 cmdbl.ConflictOption = ConflictOption.OverwriteChanges;
 sda555123.Update(WszystkieProduktyDlaDanegoSklepu);

但是这样我有错误

于是我搜索了很多,发现:我要自己写CommandUpdate。

所以 ...sda555123.UpdateCommand 我不知道如何在更新命令中为它编写自己的更新。

SQL Server 中的更新应如下所示:

Update Products
set FlagaWaznosci = @Flagawaznosci from my sda555123,
Notatka = @Notatka from my sda555123 
where id = @ p.ID from my sda555123 

我的命令更新应该是什么样子?

编辑 1:

我尝试添加:WszystkieProduktyDlaDanegoSklepu.PrimaryKey = new DataColumn[] WszystkieProduktyDlaDanegoSklepu.Columns["id"] 但什么都没有。还是这个错误。

【问题讨论】:

命令生成器不会理解如何编写 update from ;您必须手动编写它并自己将其连接到您的数据表。可能是时候将您的数据访问层切换到更新的东西(实体框架),它可以维护相关对象的图表、跟踪所有对象的更改并单独保存它们 如果您在第一个查询中向NOLOCK 提示发送垃圾邮件是有原因的?如果您真的需要对每个表使用NOLOCK(值得怀疑),您应该更改事务的隔离级别。 NOLOCK 不是一个神奇的“更快”关键字。 【参考方案1】:

我会通过改变方法而不是改变 SqlDataAdapter 的更新命令来解决问题。

鉴于您查询中的 Products.id 在结果集中是唯一的:

1- 创建一个临时表(本地或全局),其列与以id 作为主键的查询结果相同。

2- 使用您的 select 语句将数据插入到临时表中。

3- DataAdatper.selectQuery.commandText 设置为“从 TempTable 中选择 *”

4- 更新命令现在基于一个简单的选择语句,因此 datagridview/datatable 中的任何更改都可以使用 dataadapter.update(datatable) 更新到临时表

5- 至于最后的数据库更新,你可以使用下面的语句

Update Prd
set Prd.FlagaWaznosci = TempTable.FlagaWaznosci ,Prd.Notatka = TempTable.Notatka  etc.. all the fields that need to be updated  
from my Products as Prd 
Inner Join TempTable on TempTable.id = Prd.id

请注意,(5) 中的更新将影响所有行,即使是未更改的行。 要解决此问题,您可以按照以下方式进行操作

1- 将更改的 ID 保存在列表中。

List<string> lst = new List<string>();
foreach(DataRow dr in datatable.GetChanges(DataRowState.Modified))

 lst.add(dr["id"].ToString());

2- 将您的列表转换为要与 (5) 中的查询连接的字符串值

 String strchange = String.Join(",",lst); //will give you id1,id2,...
 //The update query becomes
 Update Prd
 set Prd.FlagaWaznosci = TempTable.FlagaWaznosci ,Prd.Notatka = 
 TempTable.Notatka  etc.. all the fields that need to be updated  
 from my Products as Prd 
 Inner Join TempTable on TempTable.id = Prd.id
 Where Prd.id In ( strchange ) 

【讨论】:

这是个好主意。但在这种情况下的主要问题是:您更新所有内容。 - 不仅改变了数据。当您只更新 1000 行时 - 更新将放在 1000 行上。当第二个用户并行使用它时 - 将会更新并保存他的工作。 正确,我会修改我的答案来解决这个问题 难道没有更好更简单的想法如何进行这样的更新吗?像这样的解决方法很烦人 如果 SelectCommand 返回 OUTER JOIN 的结果,DataAdapter 将不会为生成的 DataTable 设置 PrimaryKey 值。您必须自己定义 PrimaryKey 以确保正确解析重复行。 (docs.microsoft.com/en-us/dotnet/framework/data/adonet/…)。尝试向数据表添加主键,然后执行更新。它可能工作 不,它不起作用。我补充说: WszystkieProduktyDlaDanegoSklepu.PrimaryKey = new DataColumn[] WszystkieProduktyDlaDanegoSklepu.Columns["id"] ,仍然是同样的错误。这太愚蠢了,因为它从未修改过最后的 2 个值,我只想在表格中显示它们。我想不通【参考方案2】:

请分别更新您的表格,因为在加入时您刚刚看到两个或两个以上的表格合并为一个表格形式。但是你不能对

做任何粗鲁的操作

【讨论】:

这不是真的.. 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于C# SqlCommandBuilder,CommandUpdate - 如何使用外部连接表编写基于选择的正确更新的主要内容,如果未能解决你的问题,请参考以下文章

C# DataSet:更新数据库

使用没有主键的 SQLAdapter 和 SQLCommandBuilder

为啥我们要使用 sqlcommandbuilder?

SQLCommandBuilder - 仅从 DataTable 更新“追加”数据库表

SqlCommandBuilder类是如何构建T-Sql语句

SqlCommandBuilder的讨论