【问题标题】:Inserting into multiple tables using more than one ID that just got created使用多个刚创建的 ID 插入多个表
【发布时间】:2011-09-29 19:52:25
【问题描述】:

我不得不在我的数据库中添加另一个表,现在我需要返回并更新一个允许插入多个表的页面。我没有写这个页面,所以我正在尝试清理所有内容,但有些部分我并不真正理解。现在我打破了页面,它只插入到一个表中。我插入的第一个。

ProductName:进入产品表

描述:作为数据进入 Picklist 表......它还应该根据作为标识列的 PicklistID 生成插入到营销表中。市场营销表告诉 Picklist 表它正在寻找描述。

价格:进入产品表

Category:进入 CategoryLink 表,该表还插入最近生成的 ProductID。

Company:进入 CompanyLink 表,该表还插入最近生成的 ProductID。

目标受众:进入 TargetLink 表,该表还插入最近生成的 ProductID。

状态:进入产品表

Protected Sub submitButton_Click(ByVal sender As Object, 
ByVal e As System.EventArgs) Handles submitButton.Click
    Dim CategoryID As String = txtCategoryID.Value
    Dim CompanyIDs As New ArrayList
    'Get selected companies-----
    Dim CompanyCount As Integer = CompanyCheckbox.Items.Count
    For i As Integer = 0 To CompanyCount - 1
        If CompanyCheckbox.Items(i).Selected Then
            CompanyIDs.Add(CompanyCheckbox.Items(i).Value)
        End If
    Next
    'Get selected targets---
    Dim TargetIDs As New ArrayList
    Dim TargetCount As Integer = TargetCheckbox.Items.Count
    For i As Integer = 0 To TargetCount - 1
        If TargetCheckbox.Items(i).Selected Then
            TargetIDs.Add(TargetCheckbox.Items(i).Value)
        End If
    Next
    'Get Status---
    Dim Status As String = Nothing
    If StatusCheckbox.Checked Then
        Status = "1"
    Else
        Status = "0"
    End If
    'SQL Insert: Product Table
    Dim sqlInsertProduct As String = "IF NOT EXISTS (SELECT * FROM Product 
    WHERE ProductName= @ProductName) 
    BEGIN INSERT INTO Product 
    (ProductName, Status, CreateDate, ModifyDate, CreateUser, ModifyUser, Price) 
    VALUES (@ProductName, @Status ,getdate(),getdate(), @CreateUser, @ModifyUser,
    @Price) END;
    INSERT INTO Picklist (Title, Data) VALUES ('About this product', @Data);"

    'Create SQL Connection
    Using cn As New 
    SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings
    ("LocalSqlServer").ConnectionString)

        Using cmd As New SqlCommand(sqlInsertProduct, cn)

            cmd.Parameters.Add(New SqlParameter("@ProductName", 
            txtNewProductName.Text))
            cmd.Parameters.Add(New SqlParameter("@Status", StatusCheckbox.Checked))
            cmd.Parameters.Add(New SqlParameter("@Price", txtPrice.Text))
            cmd.Parameters.Add(New SqlParameter("@Data", txtProductDesc.Text))
            cmd.Parameters.Add(New SqlParameter("@CreateUser",
            System.Web.HttpContext.Current.User.Identity.Name))
            cmd.Parameters.Add(New SqlParameter("@ModifyUser",
            System.Web.HttpContext.Current.User.Identity.Name))
            cn.Open()
            cmd.ExecuteNonQuery()
        End Using
        'Get the productID of the newly inserted product
        Dim sqlGetID As String = "SELECT @@IDENTITY"
        Dim ProductID As String = Nothing
        Dim cmdGetID As New SqlCommand(sqlGetID, cn)
        Dim myReader As SqlDataReader = cmdGetID.ExecuteReader()
        While myReader.Read
            If IsDBNull(myReader(0)) Then
                ProductID = ""
            Else
                ProductID = myReader(0)
            End If
        End While
        myReader.Close()
        cn.Close()

        'SQL Insert: Marketing Table
        Dim sqlInsertMarketing As String = "INSERT INTO Marketing (ProductID, 
        MarketingTypeID, MarketingTitle, MarketingData) VALUES ('" & ProductID & "', 2,
        'Description', scope_identity())"
        'SQL Insert: Category Table
        If CategoryID <> Nothing Then
            Dim sqlInsertCategory As String = "INSERT INTO CategoryLink (CategoryID,
            ProductID) VALUES (@CategoryID,'" & ProductID & "')"
            Using cmdInsertCategory As New SqlCommand(sqlInsertCategory, cn)
                cmdInsertCategory.Parameters.Add(New SqlParameter("@CategoryID",
                txtCategoryID.Value))
                cn.Open()
                cmdInsertCategory.ExecuteNonQuery()
            End Using
            cn.Close()
        End If
        If CompanyIDs.Count > 0 Then
            For i = 0 To CompanyIDs.Count - 1
                Dim sqlInsertCompany = "INSERT INTO CompanyLink (CompanyID, ProductID)
                VALUES ('" & CompanyIDs(i) & "','" & ProductID & "')"
                Using cmdInsertCompany As New SqlCommand(sqlInsertCompany, cn)
                    cn.Open()
                    cmdInsertCompany.ExecuteNonQuery()
                End Using
                cn.Close()
            Next

        End If
        If TargetIDs.Count > 0 Then
            For i = 0 To TargetIDs.Count - 1
                Dim sqlInsertTarget = "INSERT INTO TargetLink (TargetID, ProductID) 
                VALUES ('" & TargetIDs(i) & "','" & ProductID & "')"
                Using cmdInsertTarget As New SqlCommand(sqlInsertTarget, cn)
                    cn.Open()
                    cmdInsertTarget.ExecuteNonQuery()
                End Using
                cn.Close()
            Next
        End If

    End Using

Response.Write("<script type='text/javascript'>{ alert('Product added successfully'); 
document.location.href = 'AddProduct.aspx'; }</script>")
End Sub
End Class

就像我之前说的,只有进入 Product 表的插入才有效。在我添加 Picklist 表并尝试重新连接所有内容之前,整个页面都正常工作。代码很草率而且没有参数,所以这也可能是我搞砸的地方,因为我仍在尝试学习如何使用它们。如果我需要包含其他信息,我可以这样做。不知道这篇文章写的有多详细。谢谢

更新:

我已经得到了 INSERT 的所有内容,除了 Marketing 表的 INSERT。我非常感谢能帮助我将该 PicklistID 插入到 Marketing 表的 MarketingData 列中的人

【问题讨论】:

  • 现在这不是您的问题,但请注意使用 @@IDENTITY 获取最后插入的值是个坏主意。我认为 IDENT_CURRENT 真的是最好的。阅读:msdn.microsoft.com/en-us/library/ms175098.aspx
  • @DavidEG 是的,当创建 2 个不同的 ID 需要插入数据库时​​,我不确定如何使用 IDENTITY。我的印象是 IDENTITY 仅用于创建的最后一个 ID?我还是新手……
  • 是的,是最后一个。但我认为永远不应该插入。假设您需要审核some_table,因此您创建audit_table 并在对some_table 进行任何插入、更新或删除操作后填充audit_table。如果您使用@@IDENTITY 获取some_table 的最后一个值,那么一切都会崩溃,因为您将获得audit_table 的最后一个值。为避免这种情况,您应该使用IDENT_CURRENT('some_table')
  • 这样行吗? Dim sqlGetID As String = "SELECT IDENT_CURRENT('Product')"括号里就是表名吧?
  • @HLGEM scope_identity() 仅适用于一个身份值,不是吗?有人告诉我,获得 2 个身份值的唯一方法是使用 OUTPUT 子句。以前从未使用过,我对此一无所知。

标签: asp.net sql vb.net insert


【解决方案1】:

如果您只有一个身份值可以使用SCOPE_IDENTITY(),请不要使用@@IDENTITY。 @@IDENTITY 的问题在于它总是返回最后一个标识值,即使它来自另一个“范围”。例如,您插入到表中并生成一个标识值,然后触发一个触发器,该触发器将插入到具有标识的日志表中。然后你调用@@IDENTITY,猜猜你得到的日志表标识值是什么,它是最后一个。 SCOPE_IDENTITY() 为您提供本地范围内的最后一个。

但是,如果由于在一个语句中插入多行而需要捕获多个标识值,则必须使用OUTPUT clause。它适用于 INSERT、UPDATE 和 DELETE,但这里有一个关于 UPDATE 的示例:view the changed values after an update statement

【讨论】:

  • 我有 2 个标识列可以获取并插入到 2 个不同的表中。创建的 ProductID 是一个标识列,创建的 PicklistID 也是一个标识列。
  • 当我说If you only have one identity value to get 时,我谈论的是单个操作中的行数。如果您使用单个插入命令将 2 行插入到表中,则会生成两个标识值,因此请使用 OUTPUT。无论您发出多少个插入命令,如果您插入一行,您都可以使用SCOPE_IDENTITY()(紧随其后)来获取标识。
【解决方案2】:

这是代理的缺点之一,即难以执行实体的批量创建。我自己避免使用代理,但经常遇到这个问题。我的一般方法是使用临时表将系统生成的代理映射到它们的自然键。

假设这个有点简化的结构:

CREATE TABLE Products 
(
 ProductName VARCHAR(20) NOT NULL UNIQUE,  -- natural key
 ProductID INTEGER IDENTITY(1, 1) NOT NULL UNIQUE  -- surrogate
);

CREATE TABLE PickLists
(
 PicklistDescription VARCHAR(30) NOT NULL UNIQUE,  -- natural key
 PicklistID INTEGER IDENTITY(50, 50) NOT NULL UNIQUE  -- surrogate
);

CREATE TABLE Marketing
(
 ProductID INTEGER NOT NULL UNIQUE REFERENCES Products (ProductID), 
 PicklistID INTEGER NOT NULL UNIQUE REFERENCES PickLists (PicklistID), 
 MarketingComment VARCHAR(40) NOT NULL
);

以下是使用 vanilla 标准 SQL-92 的基本大纲(IDENTITY 关键字除外!):

(注意:自然键是在 DBMS 之外生成的那些,代理是 DBMS 生成的值)

CREATE TABLE StagingMarketing
(
 ProductName VARCHAR(20) NOT NULL UNIQUE, 
 PicklistDescription VARCHAR(30) NOT NULL UNIQUE, 
 MarketingComment VARCHAR(40) NOT NULL
);

-- Bulk insert staging table using natural key values
INSERT INTO StagingMarketing (ProductName, PicklistDescription, MarketingComment)
   VALUES ('Widget55', 'Stuff22', 'Prototype');
INSERT INTO StagingMarketing (ProductName, PicklistDescription, MarketingComment)
   VALUES ('Widget99', 'Stuff152', 'Research');

-- Update referenced tables
INSERT INTO Products (ProductName)
   SELECT ProductName FROM StagingMarketing;
INSERT INTO PickLists (PicklistDescription)
   SELECT PicklistDescription FROM StagingMarketing;

-- Finally, update referencing table
INSERT INTO Marketing (ProductID, PicklistID, MarketingComment)
   SELECT P.ProductID, L.PicklistID, S.MarketingComment
     FROM StagingMarketing S
          INNER JOIN Products P
             ON P.ProductName = S.ProductName
          INNER JOIN PickLists L
             ON L.PicklistDescription = S.PicklistDescription;

-- Cleanup
DELETE FROM StagingMarketing;

SELECT * FROM Marketing;

附言如果设计师在没有自然密钥的情况下选择使用IDENTITY 作为人工密钥,那么您就上岸了。

【讨论】:

  • 我在原帖中添加了一张数据库结构的图片。我不知道这是否会帮助您了解我正在使用的内容。我不确定我是否理解您在帖子中所说的内容。我以前从未见过这样的sql。
  • @jlg:出于说明目的,我特意根据您的架构松散地选择了一个简化的三表结构。我不认为扩展它以适应您的完整和精确的架构将有助于您理解我的简单 SQL ;)
  • Okie dokie。还没有完全清醒:)
猜你喜欢
  • 2022-01-17
  • 2014-08-06
  • 1970-01-01
  • 2011-07-07
  • 2011-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多