【问题标题】:SELECT SCOPE_IDENTITY() to return the last inserted IDSELECT SCOPE_IDENTITY() 返回最后插入的 ID
【发布时间】:2019-12-27 00:26:59
【问题描述】:

关于这个问题有几个答案,但我的问题是关于我拥有的特定代码。

我正在尝试获取在 VBA 代码上执行的此查询的最后插入 ID。

Public Function Execute(cQry As excfw_dbQuery) As ADODB.Recordset    

    If pConn.State = 0 Then
        OpenConnection
    End If

    qry = "INSERT INTO [some really long query, which actually works]; SELECT SCOPE_IDENTITY()"

    On Error Resume Next
    Dim rs As ADODB.Recordset
    Set rs = New ADODB.Recordset
    rs.Open qry, pConn 'also tried with adOpenKeyset, adLockOptimistic

    'some error handling code which is not related to the issue 

    Set rs = rs.NextRecordset() 'also tried without moving onto the next recordset
    pInsertedId = rs.Fields(0).Value

    Set Execute = rs 'this is just to pass the query result for SELECT queries 

End Function

这应该将最后插入的 ID 保存在 pInsertedId 变量中,但每次插入一行时我都会得到 0。奇怪的是,当我将相同的代码复制并粘贴到 SSMS 中时,它可以工作。

我可能只是将一些独特的数据插入到数据库的一些未使用的列中并通过它进行查询。

--更新--

我刚刚注意到,在运行 SELECT 查询时,rs 对象保持打开状态,直到超出范围。以下是观看部分的截图:

在插入语句上,它会在查询执行后立即关闭:

【问题讨论】:

  • 调试提示:尝试将 SELECT SCOPE_IDENTITY() 替换为 select 42; 并确保其有效。
  • 刚刚尝试过,它返回 0。事实上,在尝试这样做时,我注意到在运行 SELECT 查询时,rs 对象保持打开状态,直到超出范围。当我运行INSERTquery 时,它在执行第一个查询后立即关闭。
  • 尝试直接在SSMS中运行sql语句。如果你得到正确的值,你肯定知道问题出在 VBA 方面(这是最有可能的赌注,甚至在测试之前)。
  • @ZoharPeled 我已经这样做了。事实上,这是我尝试的第一件事。问题说明了这个细节。
  • @SercioSoydanov 抱歉,不知何故错过了。

标签: excel vba tsql adodb scope-identity


【解决方案1】:

您可以使用output 子句显式保存insert 语句的结果,并使用select 返回结果:

qry =
  "declare @Ids as ( Id Int );" +
  "insert into MyTable ( Name ) " + ' Assuming   Id   is an identity column.
     "output Inserted.Id into @Ids " + 
     "values ( @Name );" +
  "select Id from @Ids;"

来自output的文档:

INSERTED 是一个列前缀,指定由 插入或更新操作。以 INSERTED 为前缀的列反映了 UPDATE、INSERT 或 MERGE 语句完成后的值,但 在执行触发器之前。

您可以使用output 子句从行中获取任何数据(注意复数。),例如新插入行的标识列值。 Output 可以与 insertupdatedeletemerge 一起使用,并且在 @ 的情况下提供对 beforeafter 值的访问987654333@。一个非常值得放在口袋里的工具。

【讨论】:

  • 太好了,我马上试试。为了简洁起见,您能否使用完整的插入语句的完整工作示例进行更新,以获取由两列(例如 id 和 name)组成的非常简单的示例表?
  • @SercioSoydanov 根据要求。我还假设它是一个带有 @Name 传入的参数化查询。您的查询可能会有所不同。
  • 我试过了。如果一个表附加了触发器,您仍然必须通过记录集前进以达到所需查询的结果,如我的回答中所述。但是这个信息对我来说很有价值,你的回答也很有用,所以我接受了你的回答。
  • 如果您这么友善,唯一的好处就是您可以在回答中强调这一点,以便其他人也可以从中受益,以防他们跳过阅读我的回答。非常感谢。
  • @SercioSoydanov 好的,我会咬一口的。触发器通常不会将记录集作为结果返回,它们只是在后台悄悄地验证、更新或记录数据。你的触发器在做什么来产生输出?应该使用什么数据,或者他们只是“泄漏”了便于调试的数据?
【解决方案2】:

事实证明,我尝试插入的表附有多个触发器。因此,包含 SELECT SCOPE_IDENTITY(); 的查询实际上是第四个查询。为了获得正确的范围,我不得不向前移动 4 个查询。我如何以编程方式实现这一点如下。不是很干净(也可能不是最好的方法),但为我完成了这项工作。

我基本上会继续下一个记录集,直到没有剩余,我通过检查错误号 91(未设置对象变量或块变量)来检测它

Public Function Execute(cQry As excfw_dbQuery) As ADODB.Recordset    

    If pConn.State = 0 Then
        OpenConnection
    End If

    qry = "INSERT INTO [some really long query, which actually works]; SELECT SCOPE_IDENTITY()"

    On Error Resume Next
    Dim rs As ADODB.Recordset
    Set rs = New ADODB.Recordset
    rs.Open cQry.Query, pConn, adOpenKeyset, adLockOptimistic

    'some error handling code which is not related to the issue 

    On Error Resume Next
    'begin loop
    Do
        'go to next recordset
        Set rs = rs.NextRecordset()

        'if we're getting error n. 91, it means 
        'recordsets are exhausted, hence we're getting
        'out of the loop 
        If Err.Number = 91 Then
            Err.Clear
            Exit Do
        End If

        'if we are not out of recordsets, check the 
        'result of the query. If it is bigger then zero, 
        'it means we hit the jackpot. 
        If rs.Fields(0).Value > 0 Then
            pInsertedId = rs.Fields(0).Value
        End If
    Loop
    On Error GoTo 0

End Function

再一次,不是最干净,也不是最正确的方法,但成功了。我愿意接受任何改进或建议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 2014-09-06
    相关资源
    最近更新 更多