【问题标题】:How to properly update 2 tables, at the same time, in a database?如何在数据库中同时正确更新 2 个表?
【发布时间】:2013-07-19 19:22:43
【问题描述】:

场景:

我正在使用 Visual Studio 2010,并且正在制作数据库示例应用程序。我必须将这些光谱文件存储在数据库中。这些光谱文件被加载到一个光谱数据对象中。我打算将这些对象存储在数据库中。对象具有许多不同的属性(int、double、string 等)。此外,该对象还包含一个 y 分量数据值数组。

我将我的数据库设计为由两个表组成(一个用于光谱数据对象属性,另一个用于双数组点。我创建的第一个表具有 ID 自动增量。

这是我整理的一些代码,它们基本上采用这些对象值并将它们分配给表参数。

SPCFile spc = new SPCFile();
TSpectraData spectra = new TSpectraData();
spectra = spc.LoadSPC(openSPCFile.FileName);

using(SqlConnection mySqlConnect = new SqlConnection(ConfigurationManager.ConnectionStrings["DBLocal"].ConnectionString))
{
      SqlCommand command = mySqlConnect.CreateCommand();
      command.CommandText = "INSERT INTO Spectras VALUES"
                                     + "(@DateTime, "
                                     + "@Name, "
                                     + "@Version, "
                                     + "@SerialHighNumber, "
                                     + "@SerialLowNumber, "
                                     + "@Completed, "
                                     + "@SpectrometerID, "
                                     + "@GasCellID, "
                                     + "@Format, "
                                     + "@Apodization, "
                                     + "@PhaseApodization, "
                                     + "@Temperature, "
                                     + "@Pressure, "
                                     + "@NumScans, "
                                     + "@Resolution, "
                                     + "@Gain, "
                                     + "@PathLength, "
                                     + "@FirstPoint, "
                                     + "@LastPoint, "
                                     + "@MaxFrequency, "
                                     + "@MaxLocPoint, "
                                     + "@MinLocPoint, "
                                     + "@NumDataPoints, "
                                     + "@NumDataPhase, "
                                     + "@Step, "
                                     + "@IgramType)";

                //Adding parameters to command
                //Value for ID will be added automatically since it is set to auto increment

                command.Parameters.Add("@DateTime",         SqlDbType.DateTime, Int32.MaxValue);
                command.Parameters.Add("@Name",             SqlDbType.NVarChar, Int32.MaxValue);
                command.Parameters.Add("@Version",          SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@SerialHighNumber", SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@SerialLowNumber",  SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@Completed",        SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@SpectrometerID",   SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@GasCellID",        SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@Format",           SqlDbType.SmallInt, Int32.MaxValue);
                command.Parameters.Add("@Apodization",      SqlDbType.SmallInt, Int32.MaxValue);
                command.Parameters.Add("@PhaseApodization", SqlDbType.SmallInt, Int32.MaxValue);
                command.Parameters.Add("@Temperature",      SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@Pressure",         SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@NumScans",         SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@Resolution",       SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@Gain",             SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@PathLength",       SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@FirstPoint",       SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@LastPoint",        SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@MaxFrequency",     SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@MaxLocPoint",      SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@MinLocPoint",      SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@NumDataPoints",    SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@NumDataPhase",     SqlDbType.Int, Int32.MaxValue);
                command.Parameters.Add("@Step",             SqlDbType.Float, Int32.MaxValue);
                command.Parameters.Add("@IgramType",        SqlDbType.SmallInt, Int32.MaxValue);
                //Adding values to these parameters
                command.Parameters["@DateTime"].Value = DateTime.Now;
                command.Parameters["@Name"].Value = spectra.Info.Name;
                command.Parameters["@Version"].Value = spectra.Info.Version;
                command.Parameters["@SerialHighNumber"].Value = spectra.Info.SerialHighNumber;
                command.Parameters["@SerialLowNumber"].Value = spectra.Info.SerialLowNumber;
                command.Parameters["@Completed"].Value = spectra.Info.Completed;
                command.Parameters["@SpectrometerID"].Value = spectra.Info.SpectrometerID;
                command.Parameters["@GasCellID"].Value = spectra.Info.GasCellID;
                command.Parameters["@Format"].Value = (short)spectra.Info.Format;
                command.Parameters["@Apodization"].Value = (short)spectra.Info.Apodization;
                command.Parameters["@PhaseApodization"].Value = (short)spectra.Info.PhaseApodization;
                command.Parameters["@Temperature"].Value = spectra.Info.Temperature;
                command.Parameters["@Pressure"].Value = spectra.Info.Pressure;
                command.Parameters["@NumScans"].Value = spectra.Info.NumScans;
                command.Parameters["@Resolution"].Value = spectra.Info.Resolution;
                command.Parameters["@Gain"].Value = spectra.Info.Gain;
                command.Parameters["@PathLength"].Value = spectra.Info.PathLength;
                command.Parameters["@FirstPoint"].Value = spectra.Info.FirstPoint;
                command.Parameters["@LastPoint"].Value = spectra.Info.LastPoint;
                command.Parameters["@MaxFrequency"].Value = spectra.Info.MaxFrequency;
                command.Parameters["@MaxLocPoint"].Value = spectra.Info.MaxLocPoint;
                command.Parameters["@MinLocPoint"].Value = spectra.Info.MinLocPoint;
                command.Parameters["@NumDataPoints"].Value = spectra.Info.NumDataPoints;
                command.Parameters["@NumDataPhase"].Value = spectra.Info.NumDataPhase;
                command.Parameters["@Step"].Value = spectra.Info.Step;
                command.Parameters["@IgramType"].Value = (short)spectra.Info.IgramType;
                mySqlConnect.Open();
                command.ExecuteNonQuery();
                fileChosen = false;
                //Don't have to call connection close due tp using statment
                label1.Hide();
            }

由于 ID 是自动递增的,因此我不需要将其包含在 VALUES 语句中。我过程的下一步是添加到数据点表。我计划重用命令对象并重新分配命令文本。我的问题是如何获取这个数据点数组并将它们分配到另一个表中,同时保持两个表中的 ID 并发。我假设,关系基本上是“如果主键和外键匹配,则它们是相关的”。

【问题讨论】:

  • 为什么不编写一个存储过程来为您完成这项工作?在那里也添加一个事务。
  • 你在使用 SQL Server 吗?
  • 序列 NEXTVAL 和 CURRVAL 很有用。或者将值保留在变量中并在两个插入中使用它
  • 使用 SQL 服务器。什么是存储过程?交易是什么意思?这里是半菜鸟。大声笑
  • @Randy 由于我没有在代码中分配 ID 号,我应该如何获取该号码以重复使用它?我已将数据库设置为自动增加主键 ID。

标签: c# database visual-studio-2010 database-design ado.net


【解决方案1】:

我在这里假设了很多东西,但是,我想给出这个伪代码。

首先,您可以使用查询 SELECT SCOPE_IDENTITY() 检索连接上最后插入的自动增量编号 (IDENTITY)。

其次,知道这个数字,您可以轻松地准备一个循环来插入数据点,其中参数是用循环外的虚拟值创建的,并在数据点上用循环内的有效值替换。

事务应该是一个好处,因为如果在循环数据点时出现问题,您不需要清除已经插入的数据

// Start a TransactionScope to handle all/or/nothing scenario
using(TransactionScope scope = new TransactionScope())
{
    using(SqlConnection mySqlConnect = new SqlConnection(ConfigurationManager.ConnectionStrings["DBLocal"].ConnectionString))
    {
          SqlCommand command = mySqlConnect.CreateCommand();

          // Append the SELECT SCOPE_IDENTITY as a second command on this text.....
          command.CommandText = "INSERT INTO Spectras VALUES"
                                         + "(......)"
                                         + ";SELECT SCOPE_IDENTITY();"
          mySqlConnect.Open();

          // HERE YOU EXECUTE THE COMMAND AND GET BACK THE LAST IDENTITY USED 
          SqlDataReader reader = command.ExecuteReader();
          if (reader.HasRows)
          {
              reader.Read();
              int spectraID = Convert.ToInt32(reader[0]);
              // Ready to start the insert of points

              SqlCommand cmdPoints = new SqlCommand("INSERT INTO DataPoints " + 
                                      "VALUES (@ID, @IDX, @VAL)", mySqlConnect);
              cmdPoint.Parameters.AddWithValue("@ID",spectraID);
              cmdPoint.Parameters.AddWithValue("@IDX",0);
              cmdPoint.Parameters.AddWithValue("@VAL",0);

              foreach(DataPoint dp in DataPoints)
              {
                  cmdPoint.Parameters["@IDX"].Value = dp.Index);
                  cmdPoint.Parameters["@VAL"].Value = dp.Value);
                  cmdPoint.ExecuteNonQuery();
              }
        }      
    }
    scope.Complete(); // This will write everything on the database

}

如果您因异常退出,范围将不会完成,整个记录集将被丢弃

注意:您没有在 INSERT INTO 命令中指定列名。这意味着框架将尝试插入每一列,包括 ID (IDENTITY) 和第一个参数的值(日期时间?)。您应该按照传递参数的确切顺序插入字段名称。

command.CommandText = "INSERT INTO Spectras " 
                      + "(Name, Version, SerialHighNumber, .......)"
                      + "VALUES (@Name, @Version, @SerialHighNumber, ......)"
                      + ";SELECT SCOPE_IDENTITY();"

【讨论】:

  • 我认为这可能有效。现在,如果我有多个应用程序实例访问数据库怎么办? select_scope_identity() 函数是否只与当前上下文中发生的事情有关?
  • 更新了答案,将 SCOPE_IDENTITY 包含在传递给 sql server 的同一批处理命令中。这将有效地消除并发访问在检索 IDENTITY 值的各种方法上弄乱 SCOPE_IDENTITY (Here a link to MSDN) 的机会)
  • 社区拒绝了您的编辑,但您是对的,在此版本中不需要第二条命令。现已修复
  • 运行此程序时出现错误。我尝试发送命令,但也许我做错了什么。 “只有在使用列列表并且 IDENTITY_INSERT 为 ON 时,才能为表 'Spectras' 中的标识列指定显式值。”
  • 您没有在 INSERT INTO 中插入列名。这意味着查询将尝试插入包括 IDENTITY (ID) 在内的每一列,这将导致错误,因为您的第一个参数是将插入列 ID 的 DateTime。我会尝试在一分钟内更新答案
猜你喜欢
  • 1970-01-01
  • 2018-04-08
  • 2021-09-11
  • 2014-02-21
  • 1970-01-01
  • 2017-01-14
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
相关资源
最近更新 更多