从MS Access VBA中的直通查询中检索输出参数?PT 查询执行程序。插入新记录的存储过程,我需要获取其 ID

Retrieving output parameter from passthrough query in MS Access VBA? PT query execs. a stored procedure inserting a new record, I need to get its id

提问人:mechanicalMuse 提问时间:8/5/2023 最后编辑:mechanicalMuse 更新时间:8/8/2023 访问量:89

问:

有没有办法使用具有输入和输出参数的直通查询来运行存储过程?如果是这样,如何使用VBA检索MS Access中的输出参数值?

有问题的存储过程插入了一条新记录,我需要获取新创建的记录 ID。

这是我的存储过程:

ALTER PROCEDURE [dbo].[sp_two_Insert]
    @woTitle nvarchar(255),
    @woWriter int,
    @woDate date,
    @woid int = 0 OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO dbo.two (woTitle, writtenby, wodate)
    VALUES (@woTitle, @woWriter, @woDate);
    
    SELECT @woid = SCOPE_IDENTITY();
END

先谢谢大家!

我尝试了这些代码片段:

尝试#1:
来自Gord Thompson的 https://stackoverflow.com/a/46086366/9604983

VBA每次在行上都会崩溃:,它会无限期地卡住创建新记录,所以我必须通过任务管理器退出程序。 Set rs = ptq.OpenRecordset

尝试#2:
https://answers.microsoft.com/en-us/msoffice/forum/all/pass-through-query-with-output-parameter/8d0c69dd-846d-4a02-8844-28d0e05dbf6a

但是,同样尝试 #1,MS Access 在 时崩溃,并产生相同的后果。Set rs = rs.NextRecordset

我不认为galla_在“Replies(1)”部分下给出的解决方案对我有用,因为我需要插入查询创建的记录的 ID,并且在插入之后立即运行第二个存储过程,肯定会获得创建的最后一条记录的 ID,但不一定是首先运行的插入存储过程创建的记录的 ID。

这是崩溃的VBA代码:

Option Compare Database
Option Explicit

Sub ptqTest()

Dim cdb As DAO.Database
Dim rs As DAO.Recordset

Set cdb = CurrentDb
Set rs=cdb.OpenRecordset("qPtMyPassThroughQ") ‘this is where Ms 
Access crashes

rs.Close
Set rs = Nothing
Set cdb = Nothing

End Sub
SQL Server VBA MS-Access 存储过程

评论

2赞 Thom A 8/5/2023
向我们展示您拥有的不起作用的VBA代码。
1赞 marc_s 8/5/2023
旁注不应将前缀用于存储过程。Microsoft 已保留该前缀供自己使用(请参阅命名存储过程),并且您确实会在将来的某个时候面临名称冲突的风险。这也不利于存储过程的性能。最好只是简单地避免并使用其他东西作为前缀 - 或者根本没有前缀!sp_sp_
0赞 mechanicalMuse 8/6/2023
@Thom A:你去吧,
0赞 Andre 8/6/2023
内容是什么?qPtMyPassThroughQ
0赞 mechanicalMuse 8/6/2023
@Andre:EXEC [dbo]。[sp_two_Insert]'big blend Wo', 5, '8/4/2023'

答:

2赞 Albert D. Kallal 8/8/2023 #1

好的,你这里有一些错误。

如果要使用或设置输出参数,则代码应按如下方式编写:

CREATE PROCEDURE [dbo].[AddHotel]
    @FirstName nvarchar(50),
    @LastName nvarchar(50),
    @HotelName nvarchar(50),
    @newPK int OUTPUT

AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    INSERT INTO tblHotelsA (FirstName, LastName, HotelName)
                          VALUES (@FirstName, @LastName, @HotelName)

    SET @newPK = SCOPE_IDENTITY()

END

请注意,输出值不是由 select 创建的。因此,上面的存储过程执行插入,然后设置 OUTPUT 参数。

但是,实际上,我认为没有必要或使用输出值的优势,因为使用 SELECT 返回值要容易得多。

那么,我能想到的唯一用例是将 OUTPUT 参数(并返回)到 MS-access?

这将是一个“用例”,其中存储过程由其他人编写,因此是一组现有 T-SQL 代码例程(以及那些其他例程需要并使用 OUTPUT 参数)的一部分。换句话说,当存储过程由您选择时,我不会以这种方式编写存储过程。

但是,我们仍然展示如何获取/使用/享受该返回值,因为您在软件旅行中很可能会遇到上述用例。

所以,说上面的存储过程。

因此,MS-Access VBA代码可以这样编写:

解决方法包括将存储过程调用“包装”在 2 行额外的代码中。(一个 T-SQL 变量声明和一个 SELECT 语句,用于将该输出值转换为 DAO 记录集返回)。

因此,这将适用于上述情况:

Dim newPK       As Long

Dim strSQL      As String
Dim sFirstName  As String
Dim sLastName   As String
Dim sHotelName  As String

sFirstName = "Albert"
sLastName = "Kallal"
sHotelName = "My Cool Hotel"

 strSQL = "declare @MyPK int;" & _
          "exec dbo.AddHotel " & _
          "@FirstName = '" & sFirstName & "'," & _
          "@LastName = '" & sLastName & "'," & _
          "@HotelName = '" & sHotelName & "'," & _
          "@newPK = @MyPK OUTPUT;" & _
          "SELECT @MyPK AS MyNewPKTest;"

With CurrentDb.QueryDefs("qryPassR")

    .ReturnsRecords = True
    .SQL = strSQL
    newPK = .OpenRecordset()(0)

End With

现在,请注意上面的 BEYOND CLOSE:

请注意,返回值的 T-SQL 语法是“向后”的。这不是 MS-Access 人脸植物,而是 T-SQL 所需的令人困惑的语法。返回的参数值保留在上述表达式的左侧!

正如我所指出的,以上是解决方法,只有在您遇到无法更改的现有存储过程时才需要。

但是,可以这样编写上述存储过程:

CREATE PROCEDURE [dbo].[AddHotel]
    @FirstName nvarchar(50),
    @LastName nvarchar(50),
    @HotelName nvarchar(50)

AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO tblHotelsA (FirstName, LastName, HotelName)
                          VALUES (@FirstName, @LastName, @HotelName)

    select SCOPE_IDENTITY()

END

因此,在上面,我们不使用输出参数(也不需要输出参数)。并代替 SET 命令来设置返回(输出)值?

我们使用一个简单的 SELECT 语句,这导致返回单个标准 DAO 数据行。

因此,我们的 VBA 代码现在如下所示:

 strSQL = "exec dbo.AddHotel " & _
          "@FirstName = '" & sFirstName & "'," & _
          "@LastName = '" & sLastName & "'," & _
          "@HotelName = '" & sHotelName & "'"
              
With CurrentDb.QueryDefs("qryPassR")

    .ReturnsRecords = True
    .SQL = strSQL
    newPK = .OpenRecordset()(0)

End With

或者,它甚至可以以这种格式编写,但由于参数顺序很重要,因此需要谨慎。

所以,这个语法:

 strSQL = "exec dbo.AddHotel '" & _
          sFirstName & "','" & sLastName & "','" & sHotelName & "'"
              
With CurrentDb.QueryDefs("qryPassR")

    .ReturnsRecords = True
    .SQL = strSQL
    newPK = .OpenRecordset()(0)
    

End With

请注意我用于返回记录集的简写。

这行代码:

    newPK = .OpenRecordset()(0)

可以写成这样的长格式:

 dim rstReturn    as DAO.RecordSet

 ... same above code here, and then

     set rstReturn = .OpenRecordSet
End With

然后,要获取该值,请使用:

 rstReturn(0)   ' first column.

或者,可以使用 T-SQL 语句中返回的列名。

rstReturn!newPK, or rstReturn("newPK")

同样,如果要将列名称用于返回值,请确保在 T-SQL 中设置该列

例如:

    select SCOPE_IDENTITY() as newPK

因此,VBA DAO 不直接支持 T-SQL 返回(输出)参数。

但是,它们很少得到保证,并且只有在无法更改 T-SQL 例程时才需要上述第一个解决方法。

另请注意我如何使用现有的创建的直通查询,因此假定已为该 Access 直通查询创建和设置了连接设置。