C# 更新具有一对多关系的实体
我有一个供应商表,其架构如下:C# 更新具有一对多关系的实体,c#,sql,asp.net,sql-server,one-to-many,C#,Sql,Asp.net,Sql Server,One To Many,我有一个供应商表,其架构如下: | SupplierId | SupplierName | ----------------------------- | 1 | Good Company | | Id | SupplierId | Day (enum) | OpenHour | CloseHour | ---------------------------------------------------------------------- |
| SupplierId | SupplierName |
-----------------------------
| 1 | Good Company |
| Id | SupplierId | Day (enum) | OpenHour | CloseHour |
----------------------------------------------------------------------
| 1 | 1 | 0 | 05:00:00.0000000 | 15:00:00.0000000 |
| 2 | 1 | 1 | 05:00:00.0000000 | 15:00:00.0000000 |
| 3 | 1 | 2 | 05:00:00.0000000 | 16:00:00.0000000 |
我还有一个具有以下模式的一对多表供应商活动小时数:
| SupplierId | SupplierName |
-----------------------------
| 1 | Good Company |
| Id | SupplierId | Day (enum) | OpenHour | CloseHour |
----------------------------------------------------------------------
| 1 | 1 | 0 | 05:00:00.0000000 | 15:00:00.0000000 |
| 2 | 1 | 1 | 05:00:00.0000000 | 15:00:00.0000000 |
| 3 | 1 | 2 | 05:00:00.0000000 | 16:00:00.0000000 |
我创建了一个设置页面,每个供应商都可以在其中更新其数据
问题是,更新活动时数表的正确方法是什么?
供应商可以删除天数、添加天数、更新现有天数的小时数 我考虑了以下选择:
- 要删除的ID列表
- 要更新的项目列表(行id和更新的数据)
- 要添加的新项目列表
这种方法的问题是,我认为对于这样的普通操作来说有点太复杂了李>
更新 我目前正在阅读有关“合并”的文章,试图看看它是否能解决我的问题。尽管你说“擦除/更新”方法效率不高,但它是最容易实现的方法 当您有多个用户编辑同一数据时,您必须阻止其他用户编辑该表,或者告诉他们“最后一次编辑成功”;i、 e.编辑内容将被覆盖 这是否是用户的问题取决于您,取决于同时编辑数据的潜在用户数-您可能会发现这从来都不是问题 如果您担心丢失编辑,可以实施数据审核日志并记录所有更改。尽管您说“擦除/更新”方法效率不高,但它是最容易实现的方法 当您有多个用户编辑同一数据时,您必须阻止其他用户编辑该表,或者告诉他们“最后一次编辑成功”;i、 e.编辑内容将被覆盖 这是否是用户的问题取决于您,取决于同时编辑数据的潜在用户数-您可能会发现这从来都不是问题
如果您担心丢失编辑,可以实施数据审核日志并记录所有更改。对,您可以通过以下方式使用合并:
create type SupplierActivityHoursType as table
(
[Id] [int] NOT NULL,
[SupplierId] [int] NULL,
[Day] [int] NULL,
[OpenHour] [datetime] NULL,
[CloseHour] [datetime] NULL
)
go
CREATE PROCEDURE UpdateSupplierActivityHours
@SupplierActivityHours dbo.SupplierActivityHoursType readonly
AS
BEGIN
merge SupplierActivityHours as t
using (select Id, SupplierId, [Day], OpenHour, CloseHour
from @SupplierActivityHours) as s
on t.Id = s.Id, t.SupplierId = s.SupplierId
when matched then
update set t.[Day] = s.[Day], t.OpenHour = s.OpenHour, t.CloseHour = s.CloseHour
when not matched by target then
insert (SupplierId, [Day], OpenHour, CloseHour)
values (s.SupplierId, s.[Day], s.OpenHour, s.CloseHour)
when not matched by source then
delete;
END
因此,要更新所有需要传递到UpdateSupplierActivityHours
SP您的工时表的供应商数据
当在SupplierActivityHours
中找到记录时,该记录将被更新;当在@SupplierActivityHours中找到但在SupplierActivityHours
表中未找到该记录时,将插入该记录;如果在@SupplierActivityHours
中未找到该记录,则将删除该记录
MERGE语句允许在一个事务中高效地执行所有这些插入、更新和删除操作。对,您可以通过以下方式使用MERGE:
create type SupplierActivityHoursType as table
(
[Id] [int] NOT NULL,
[SupplierId] [int] NULL,
[Day] [int] NULL,
[OpenHour] [datetime] NULL,
[CloseHour] [datetime] NULL
)
go
CREATE PROCEDURE UpdateSupplierActivityHours
@SupplierActivityHours dbo.SupplierActivityHoursType readonly
AS
BEGIN
merge SupplierActivityHours as t
using (select Id, SupplierId, [Day], OpenHour, CloseHour
from @SupplierActivityHours) as s
on t.Id = s.Id, t.SupplierId = s.SupplierId
when matched then
update set t.[Day] = s.[Day], t.OpenHour = s.OpenHour, t.CloseHour = s.CloseHour
when not matched by target then
insert (SupplierId, [Day], OpenHour, CloseHour)
values (s.SupplierId, s.[Day], s.OpenHour, s.CloseHour)
when not matched by source then
delete;
END
因此,要更新所有需要传递到UpdateSupplierActivityHours
SP您的工时表的供应商数据
当在SupplierActivityHours
中找到记录时,该记录将被更新;当在@SupplierActivityHours中找到但在SupplierActivityHours
表中未找到该记录时,将插入该记录;如果在@SupplierActivityHours
中未找到该记录,则将删除该记录
合并语句允许在一个事务中有效地执行所有这些插入、更新和删除。
< P>此表模式和模式是我将考虑将代理项>代码> ID >代码>作为主键的几个实例之一,并考虑使用<代码> SupplierId、No.</C>作为主键。此版本的merge
过程无论如何都会忽略Id
。除非您还将通过除供应商Id,Day
之外的其他方式按行获取或设置查询,否则我认为不需要Id
最后,我建议你做任何你觉得舒服的关于Id
的事情
表格设置:
create table dbo.Suppliers (
SupplierId int primary key
, SupplierName nvarchar(64)
);
insert into dbo.Suppliers values
(1, 'Good Company');
create table dbo.SupplierActivityHours (
Id int not null
, SupplierId int not null
, [Day] tinyint not null
, OpenHour time(0) not null
, CloseHour time(0) not null
, constraint pk_SupplierActivityHours primary key clustered (Id)
, constraint fk_SupplierActivityHours_Suppliers foreign key (SupplierId)
references Suppliers(SupplierId)
);
create unique nonclustered index
uix_SupplierActivityHours_SupplierId_Day
on dbo.SupplierActivityHours (SupplierId, [Day])
include (OpenHour, CloseHour);
/* If the Supplier can have multiple open/close per day,
move OpenHour from the include() to the index key. */
insert into dbo.SupplierActivityHours values
(1,1,0,'05:00:00.0000000','15:00:00.0000000')
, (2,1,1,'05:00:00.0000000','15:00:00.0000000')
, (3,1,2,'05:00:00.0000000','16:00:00.0000000');
表格类型:
create type dbo.udt_SupplierActivityHours as table (
/* You will not have an ID for new rows, so no need for it here */
SupplierId int not null
, [Day] tinyint not null
, OpenHour time(0) not null
, CloseHour time(0) not null
, unique (SupplierId,[Day]) /* unnamed unique constraint */
--, primary key clustered (SupplierId, [Day]) /* instead of unique constraint */
/* If the Supplier can have multiple open/close per day,
add OpenHour to the unique constraint or primary key. */
);
合并程序:
go
create procedure dbo.SupplierActivityHours_Merge (
@SupplierActivityHours dbo.udt_SupplierActivityHours readonly
) as
begin;
set nocount, xact_abort on; /* you should always include this. */
begin try
begin tran
merge SupplierActivityHours with (holdlock) as t
/* with (holdlock) prevents race conditions in merge */
using (select SupplierId, [Day], OpenHour, CloseHour
from @SupplierActivityHours) as s
on t.SupplierId = s.SupplierId
and t.[Day] = s.[Day]
when matched and (
t.OpenHour != s.OpenHour
or t.CloseHour != s.CloseHour
)
then
update set
t.OpenHour = s.OpenHour
, t.CloseHour = s.CloseHour
when not matched by target
then insert (SupplierId, [Day], OpenHour, CloseHour)
values (s.SupplierId, s.[Day], s.OpenHour, s.CloseHour)
when not matched by source then
delete
/* check output for testing: */
--output $action, deleted.*, inserted.*, s.*
;
commit tran
end try
begin catch;
if @@trancount > 0
begin;
rollback transaction;
throw; /* or other error handling */
end;
end catch;
end;
go
在上面的表类型中,我包含了一个防止重复的唯一约束。唯一约束将产生一些开销。如果这种开销是一个问题,并且您有其他保护措施来防止传入数据出现问题,请将其删除
唯一约束的替代方法是使用聚集在表类型上的主键,这也会产生开销。有一些方法可以通过保证数据已经从应用程序中订购添加到数据中来减轻一些开销,但这涉及到很多问题
为什么你总是包括
使用merge
时应注意的事项:
下面是一个示例,说明了如何执行由三部分组成的操作,而不是执行
合并
。希望这看起来不像您预期的那么复杂:
create type dbo.udt_ActivityHours as table (
[Day] tinyint not null
, OpenHour time(0) not null
, CloseHour time(0) not null
, unique ([Day])
-- If the Supplier can have multiple open/close per day,
-- add OpenHour to the unique constraint or primary key
);
go
create procedure dbo.SupplierActivityHours_Set_BySupplierId (
@SupplierId int not null
, @ActivityHours dbo.udt_ActivityHours readonly
) as
begin;
set nocount, xact_abort on; -- you should always use these.
begin try
begin tran
/* delete */
delete sah
from dbo.SupplierActivityHours sah
where sah.SupplierId = @SupplierId
and not exists (
select 1
from @ActivityHours ah
where ah.[Day] = sah.[Day]
);
/* update */
update sah set
sah.OpenHour = ah.OpenHour
, sah.CloseHour = ah.CloseHour
from SupplierActivityHours sah
inner join @ActivityHours ah on sah.[Day] = ah.[Day]
and sah.SupplierId = @SupplierId
where sah.OpenHour != ah.OpenHour
or sah.CloseHour != ah.CloseHour;
/* insert */
insert into dbo.SupplierActivityHours
(SupplierId, [Day], OpenHour, CloseHour)
select @SupplierId, [Day], OpenHour, CloseHour
from @ActivityHours ah
where not exists (
select 1
from dbo.SupplierActivityHours sah
where sah.SupplierId = @SupplierId
and ah.[Day] = sah.[Day]
);
commit tran;
end try
begin catch;
if @@trancount > 0
begin;
rollback transaction;
throw;
end;
end catch;
end;
go
TVP似乎会导致rextester死锁,因此rextester使用临时表替代。()
< P> ReXTeals:< /P> < P>此表模式和模式是我考虑将代理项>代码> ID >代码>作为主键的几个实例之一,并考虑使用<代码> SupplierId、No.</C>作为主键。此版本的
merge
过程无论如何都会忽略Id
。除非您还将通过除供应商Id,Day
之外的其他方式按行获取或设置查询,否则我认为不需要Id
最后,我建议
INNER JOIN (SELECT Supplier_ID, Day_enum
FROM dbo.Suppliers
WHERE Region_ID = 15
AND Region_ID = 20) AS B ON B.Supplier_ID = A.Supplier_ID
AND B.Day_enum = A.Day_Enum
WHERE A.Day_enum IS NOT NULL
SELECT A.Supplier_ID, A.Day_enum, A.OpenHour, A.CloseHour
FROM MYINSERT A
LEFT OUTER JOIN dbo.ActivityHours_Suppliers B ON A.Supplier_ID = B.Supplier_ID
AND A.Day_enum = B.Day_enum
WHERE A.Day_enum IS NOT NULL