Why does using @Table variables cause Adodb issues in the application?
-
13-06-2021 - |
Question
I have [MySp], A stored procedure.
So check this out.. when I run this it works:
SELECT ID,Name FROM Table
But, When I do this, I get an error on the application side (Adodb)
Declare @Table TABLE(ID int,Name varchar(10))
--- Inserts into table variable ---
INSERT INTO @Table
SELECT ID,Name FROM Table
--- Returns data from table variable ---
SELECT ID,Name FROM @Table
Keep in mind, in SQL Console i get the same results for [MySp], but in the application/adodb code i get errors.
ASP Code:
Set oRS = Server.CreateObject("Adodb.Recordset")
oRS.Open "[MySp]", Conn
If oRS.EOF Then... <--Gives an error
ADODB.Recordset error '800a0e78'
Operation is not allowed when the object is closed.
Does anyone know why I'm getting this error when I use Table variables in SQL?
Solution
Try adding SET NOCOUNT ON
at the top of the stored procedure.
I have a vague recollection that ADO used to get confused by the result counts coming back from inserts/updates/deletes.
E.g.:
CREATE PROCEDURE MySP
AS
SET NOCOUNT ON
Declare @Table TABLE(ID int,Name varchar(10))
--- Inserts into table variable ---
INSERT INTO @Table
SELECT ID,Name FROM Table
--- Returns data from table variable ---
SELECT ID,Name FROM @Table
OTHER TIPS
I had a slightly more complicated (but similar) issue NOT involving a stored proc.
Thought I would share in case anyone found themselves in a similar position.
PROBLEM
SQL:
DECLARE @a (
Alpha VARCHAR(10),
Beta INT
);
INSERT @a VALUES
('A', 1)
DECLARE @b (
Gamma DATE,
Delta INT
);
INSERT @b VALUES
('2014-01-01', 1)
SELECT *
FROM @a a
JOIN @b b ON a.Beta = b.Delta
VBA:
Sub GetData()
Dim sSql
Dim oConnection As New ADODB.Connection
Dim oRecordSet As New ADODB.Recordset
Call oRecordSet.Open(sSqlQuery, oConnection, adOpenStatic, adLockReadOnly)
Call oConnection.Open("Provider=SQLOLEDB.1;Server=MyServer;Database=MyDB;Trusted_Connection=Yes;")
sSql = <as Above>
Call oRecordSet.Open(sSql, oConnection, adOpenStatic, adLockReadOnly)
If oRecordSet.RecordCount > 0 Then
' DO STUFF WITH DATA
End If
' Close objects
oConnection.Close
oRecordSet.Close
End Sub
SOLUTION
SQL_1
CREATE TABLE #a (
Alpha VARCHAR(10),
Beta INT
);
INSERT #a VALUES
('A', 1)
SQL_2
CREATE TABLE #b (
Gamma DATE,
Delta INT
);
INSERT #b VALUES
('2014-01-01', 1)
SQL_3
SELECT *
FROM #a a
JOIN #b b ON a.Beta = b.Delta
SQL_4
DROP TABLE #a;
SQL_5
DROP TABLE #b;
VBA
Sub GetData()
Dim sSql
Dim oConnection As New ADODB.Connection
Dim oRecordSet As New ADODB.Recordset
Call oConnection.Open("Provider=SQLOLEDB.1;Server=MyServer;Database=MyDB;Trusted_Connection=Yes;")
sSql = <SQL_1>
Call oConnection.Execute(sSql)
sSql = <SQL_2>
Call oConnection.Execute(sSql)
sSql = <SQL_3>
Call oRecordSet.Open(sSql, oConnection, adOpenStatic, adLockReadOnly)
If oRecordSet.RecordCount > 0 Then
' DO STUFF WITH DATA
End If
sSql = <SQL_4>
Call oConnection.Execute(sSql)
sSql = <SQL_5>
Call oConnection.Execute(sSql)
oConnection.Close
oRecordSet.Close
End Sub
Explanation
Basically my understanding is that VBA tries to get the results back after the initial INSERT, so separating it into the individual commands (Executes) and only opening the recordset when we are querying the data we want seems to do the trick.
A side issue of this is that I needed to use temporary tables (which I didn't actually realise I had permissions to do, as it is a live DB and I had never used these before) as I believe variable tables would have vanished by the time I made the second call.
Other things to be aware of
- Ensure you only close the connection after you have done everything (initially I had each command called from separate functions and was opening/closing the connection each time without thinking about it)
- Make sure you delete the temporary tables at the end
Hope this helps someone else!