Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 更新具有一对多关系的实体_C#_Sql_Asp.net_Sql Server_One To Many - Fatal编程技术网

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 |
我创建了一个设置页面,每个供应商都可以在其中更新其数据

问题是,更新活动时数表的正确方法是什么?
供应商可以删除天数、添加天数、更新现有天数的小时数

我考虑了以下选择:

  • 当用户提交更新数据时,我将删除“SupplierActivityHours”表中的所有行(当然是特定供应商的所有行) 然后添加新值。 这种方法的问题是效率不高。如果用户只是在其中一天更新一个小时呢
  • 我将在客户端保存3个列表
    • 要删除的ID列表
    • 要更新的项目列表(行id和更新的数据)
    • 要添加的新项目列表

      这种方法的问题是,我认为对于这样的普通操作来说有点太复杂了
  • 顺便说一句,我正在使用ASP.NETMVC核心、MS SQL和Dapper.NET(如果有什么关系的话)

    我不知道还有更好的选择吗?谢谢大家!


    更新 我目前正在阅读有关“合并”的文章,试图看看它是否能解决我的问题。

    尽管你说“擦除/更新”方法效率不高,但它是最容易实现的方法

    当您有多个用户编辑同一数据时,您必须阻止其他用户编辑该表,或者告诉他们“最后一次编辑成功”;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