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

huangapple go评论100阅读模式
英文:

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

问题

You can retrieve the output parameter value from a stored procedure using a passthrough query in MS Access VBA by modifying your code like this:

Option Compare Database
Option Explicit

Sub ptqTest()
    Dim cdb As DAO.Database
    Dim qdf As DAO.QueryDef
    Dim rs As DAO.Recordset
    Dim woid As Long ' Assuming @woid is of type int in your stored procedure

    Set cdb = CurrentDb
    Set qdf = cdb.CreateQueryDef("")
    qdf.Connect = "ODBC;YOUR_CONNECTION_STRING_HERE" ' Replace with your connection string
    qdf.SQL = "EXEC [dbo].[sp_two_Insert] @woTitle='YourTitle', @woWriter=1, @woDate='2023-09-06', @woid=0 OUTPUT;"
    
    ' Open the passthrough query
    Set rs = qdf.OpenRecordset(dbOpenSnapshot)

    ' Read the output parameter value
    woid = rs(0) ' Assuming the output parameter is the first field returned

    rs.Close
    Set rs = Nothing
    Set qdf = Nothing
    Set cdb = Nothing

    ' Now 'woid' contains the newly created record id
End Sub

Make sure to replace "YOUR_CONNECTION_STRING_HERE" with your actual connection string. This code executes your stored procedure with the provided parameters and retrieves the output parameter value into the 'woid' variable.

英文:

Is there any way to run a stored procedure, using a passthrough query, that has input and output parameters? If so, how can I retrieve the output parameter value in MS Access using VBA?

The stored procedure in question inserts a new record and I need to get the newly created record id.

This is my stored procedure:

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

Thank you all in advance!

I tried these snippets of code:

Attempt #1:
https://stackoverflow.com/a/46086366/9604983
from Gord Thompson

VBA crashes every time at the line: Set rs = ptq.OpenRecordset, it gets stuck creating new records indefinitely, so I have to exit the program through the task manager.

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

But, equally to attempt #1, MS Access crashes at the Set rs = rs.NextRecordset, with the same consequences.

I don't think the solution given by galla_ under "Replies(1)" section, will work for me , since I need the id of the record created by the insert query, and running a second stored procedure, right after the insert, will certainly get the id of the last record created, but not necessarily the id of the record created by the insert stored procedure that run in the first place.

Here is the VBA code that crashes:

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

答案1

得分: 2

下面是代码部分的中文翻译:

如果您想要使用或设置输出参数,那么您的代码应该这样编写:

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语句创建的。因此,上述存储过程执行插入操作,然后设置输出参数。

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

因此,我能想到的唯一使用(和返回)输出参数的MS-Access用例是什么呢?

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

然而,让我们仍然展示如何获取/使用/享受返回值,因为在软件开发过程中,您可能会遇到上述用例。

所以,假设上面的存储过程。

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

这个解决方法涉及在两行额外的代码中“包装”存储过程调用(一个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

现在,注意上述的T-SQL返回值的语法是“反向的”。这不是MS-Access的问题,而是T-SQL所需的令人困惑的语法。返回(输出)参数值仍然位于上述表达式的左侧!

正如我所指出的,上述解决方法仅在您无法更改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

因此,在上述情况下,我们不使用输出参数(也不需要)。而在设置返回(输出)值的地方呢?

我们使用一个简单的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

... 同样的上述代码,然后

set rstReturn = .OpenRecordSet
End With

然后要获取值,您可以使用:

rstReturn(0)   ' 第一列。

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

rstReturn!newPK, or rstReturn("newPK")

并且如果您要使用列名作为返回值,请确保在T-SQL中设置了该列。

例如:

select SCOPE_IDENTITY() as newPK

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

然而,这种情况很少出现,并且上面的第一个解决方法仅在无法更改T-SQL例程时才需要。

还要注意,我使用了现有创建的传递查询,因此假定连接设置已经创建并设置为那个Access传递查询。

英文:

Ok, you have some errors here.

If you want to use or set an output parameter, then your code should be written like this:

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

Note how the output value IS NOT created by a select. So, the above stored procedure does an insert and THEN sets the OUTPUT parameters.

However, really, I don't see the need, nor advantage of using output values, since it is far easier to return values using SELECT.

So, the only use case I can think of for using (and returning) OUTPUT parameters to MS-access?

That would be a "use case" in which the stored procedure was written by someone else and thus is part of some set of existing T-SQL code routines (and those other routines expect and use OUTPUT parameters). In other words, I would not write the stored procedure that way when it is YOUR choice.

However, let's still show how you can get/use/enjoy that return value, since you may well in your software travels encounter the above use case.

So, say the above stored procedure.

So, the MS-Access VBA code can be written this way:

The work around involves "wrapping" the stored procedure call in 2 extra lines of code. (a T-SQL variable declare, and a SELECT statement to turn that output value into a DAO recordset return).

So, this will work for above:

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

Now, NOTE BEYOND CLOSE in above:

Note how the T-SQL syntax for a return value is "backwards". This is not a MS-Access face plant, but is the confusing syntax required for T-SQL. The parameter value returned remains on the LEFT SIDE of the above expression!

As I noted, the above is a workaround ONLY required if you are stuck with an existing stored procedure that you can't change.

However, one would write the above stored procedure this way:

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

So, in above, we don't use an output parameter (and don't need one). And in place of a SET command to set the return (output) value?

We use a simple SELECT statement, and that results in a single standard DAO data row being returned.

So, our VBA code now looks like this:

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

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

End With

Or, it could even be written in this format, but then caution is required since parameter order matters.

So, this syntax:

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

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

End With

And note the short hand I am using for the return recordset.

This line of code:

    newPK = .OpenRecordset()(0)

could be written in long format as this:

 dim rstReturn    as DAO.RecordSet

 ... same above code here, and then

     set rstReturn = .OpenRecordSet
End With

Then to get the value, you use:

 rstReturn(0)   ' first column.

Or, you could use the returned column name from the T-SQL statement.

rstReturn!newPK, or rstReturn("newPK")

And again if you going to use a column name for the return value, then make sure you set that column in the T-SQL

eg this:

	select SCOPE_IDENTITY() as newPK

So, VBA DAO does not directly support T-SQL return (output) parameters.

However, it is rare they are warranted, and the above first work around only is required when you can't change the T-SQL routines.

Note also how I use an exiting created pass-though query, and thus the connection setup is assumed to have already been created and setup for that Access pass-though query.

huangapple
  • 本文由 发表于 2023年8月5日 04:15:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76838895.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定