【问题标题】:How do I set up a stored procedure that does these 2 transactions as an atomic operation?如何设置将这两个事务作为原子操作执行的存储过程?
【发布时间】:2016-05-26 19:50:40
【问题描述】:

我有一张这样的桌子

CREATE TABLE Partners 
( 
    id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID(),
    first_name NVARCHAR(50) NOT NULL,
    last_name NVARCHAR(50) NOT NULL,
    org_name NVARCHAR(50) NOT NULL,
    email NVARCHAR(254) NOT NULL, -- 254 is optimal length according to http://stackoverflow.com/questions/1199190/what-is-the-optimal-length-for-an-email-address-in-a-database
    region_id INT,
    PRIMARY KEY (id),
    FOREIGN KEY (region_id) REFERENCES Regions(id)
);

还有一个存储过程

CREATE PROCEDURE AddPartner (@first_name NVARCHAR(50),
                             @last_name NVARCHAR(50),
                             @org_name NVARCHAR(50),
                             @email NVARCHAR(254),
                             @region_id INT,
                             @new_guid UNIQUEIDENTIFIER OUTPUT)
AS
BEGIN
      DECLARE @T TABLE ( OutputGuid UNIQUEIDENTIFIER )

      INSERT INTO Partners  (first_name,last_name,org_name,email,region_id)
      OUTPUT INSERTED.id INTO @T
      VALUES (@first_name,@last_name,@org_name,@email,@region_id)

      SELECT @new_guid = OutputGUID FROM @T
  END 

用于插入伙伴并输出新生成的伙伴id。

现在我刚刚了解到我需要在Partners 和另一个我称为Regions 的基本表之间建立一对多关系。所以我做了一个交集表

-- Create intersection table that associates partners with regions
CREATE TABLE Destinations 
(
    id INT IDENTITY(1,1),
    partner_id UNIQUEIDENTIFIER, 
    region_id INT,
    FOREIGN KEY (partner_id) REFERENCES Partners(id),
    FOREIGN KEY (region_id) REFERENCES Regions(id)
);

在用户界面中添加合作伙伴时,将与合作伙伴关联的区域添加到Destinations 表中。我希望这是一个原子操作(如果添加 Destination 时出现问题,则回滚伙伴的插入)而且我从来没有做过这样的事情。

问题 1:如何将区域 ID 列表插入到存储过程中?我了解如何通过对存储过程的多次调用来做到这一点,但我认为有一些方法可以通过一次调用来做到这一点,但我不知道怎么做。

问题 2:您能告诉我如何编写此查询吗?

问题 3:你能告诉我如何修改我的 C# 代码

Guid? pid = null;

// Insert partner to database
using (SqlCommand cmd = new SqlCommand("AddPartner", this._Conn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.AddWithValue("@first_name", P.FirstName);
    cmd.Parameters.AddWithValue("@last_name", P.LastName);
    cmd.Parameters.AddWithValue("@org_name", P.OrgName);
    cmd.Parameters.AddWithValue("@email", P.Email);
    cmd.Parameters.AddWithValue("@region_id", P.RegionId);

    SqlParameter output = new SqlParameter("@new_guid", SqlDbType.UniqueIdentifier) {  Direction = ParameterDirection.Output };
    cmd.Parameters.Add(output);

    this._Conn.Open();
    cmd.ExecuteNonQuery();

    pid = (Guid?)output.Value;
    this._Conn.Close();
}

调用这个新查询? P

定义
public class PartnerInit
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string OrgName { get; set; }
    public string Email { get; set; }
    public int RegionId { get; set; }
    public List<int> Destinations { get; set; }
}

我不确定如何将 Destinations 字段放入存储过程。

【问题讨论】:

标签: sql asp.net sql-server tsql database-design


【解决方案1】:

这个问题展示了如何将表值参数传递给存储过程:How to pass table value parameters to stored procedure from .net code

一旦你有这个工作,你的存储过程将如下所示:

CREATE PROCEDURE dbo.AddPartner
    @first_name NVARCHAR(50),
    @last_name NVARCHAR(50),
    @org_name NVARCHAR(50),
    @email NVARCHAR(254),
    @region_ids dbo.DestinationsTableType,
    @new_guid UNIQUEIDENTIFIER OUTPUT
AS
BEGIN
    BEGIN TRY
        BEGIN TRANSACTION

        INSERT INTO dbo.Partners...

        INSERT INTO dbo.Destinations (partner_id, region_id)
        SELECT @new_guid, region_id
        FROM @region_ids

        COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        IF (@@TRANCOUNT > 0)
            ROLLBACK TRANSACTION
    END CATCH
END

当然,您需要添加适当的错误处理等。

【讨论】:

  • 请注意,您必须在 catch 块内检查 XACT_STATE 值而不是 TRANCOUNT
  • 不,你没有。如果您有特定要求,例如您可能需要在 CATCH 块内使用 COMMIT,那么您可能需要 XACT_STATE,但肯定不是必需的。
猜你喜欢
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
  • 2022-08-17
相关资源
最近更新 更多