I am querying SQL Server from VBA using ADODB. All of my tables have RowVersion (timestamp/varbinary) columns. Since VBA doesn't support the varbinary type, I convert the RowVersion to a string from a SQL Server function. A simple query will look like:
sql = "SELECT NameValueListID, dbo.RowVersionToString(RowVersion) AS RowVersion FROM NameValueLists WHERE NameValueListID=1
To get the data onto a sheet, I first tried range.CopyFromRecordset. This worked until I included the above function in the sql call. Adding the RowVersionToString function caused the recordset to return what seems like a random set of data - less rows than excepted and fewer column than I asked for. Never could figure that one out. I then used rs.GetRows which returned the expected data and I could use range.value = rsData successfully. Below is a sample call that works.
Private Sub Test_Scratch()
Dim rs As ADODB.Recordset
Dim sqlCmd As New ADODB.Command
Dim sqlConnection As ADODB.Connection
Dim sql As String
Dim rsData() As Variant
Set sqlConnection = New ADODB.Connection
sqlConnection.Open ModSQL.GetConnectionStringByStage(wsSetup.Range("suAppStage"))
sql = "SELECT NameValueListID, dbo.RowVersionToString(RowVersion) AS RowVersion FROM NameValueLists WHERE NameValueListID=1"
Set sqlCmd.ActiveConnection = sqlConnection
sqlCmd.CommandText = sql
Set rs = sqlCmd.Execute
rs.MoveFirst
rsData = rs.GetRows
End Sub
I then added a second varbinary column to the query (it's not a RowVersion but is of the same type). So the sql will look like:
sql = "SELECT NameValueListID, dbo.RowVersionToString(RowVersion) AS RowVersion, dbo.RowVersionToString(LastItemRowVersion) AS LastItemRowVersion FROM NameValueLists WHERE NameValueListID=1
After the call, the recordset rs has all the data, but rsData has "Empty" for the first varbinary column. The order doesn't matter. Only the last varbinary column has a value, the other is always "Empty" in the rsData array.
I guess,more could anser your question if you showed your code of user function RowVersionToString.What is the definition of argument of RowVersionToString?It might be that the argument "RowVersion" was interpreted to a data type not but a colmun name.
sql = "SELECT NameValueListID, dbo.RowVersionToString(NameValueLists.RowVersion) AS RowVersion, dbo.RowVersionToString(LastItemRowVersion) AS LastItemRowVersion FROM NameValueLists WHERE NameValueListID=1
Related
I have data with a date that I want to exclude from my current data. I am trying to pull the required date from a sql table and compare it to the dates in the excel sheet.
The sql code is
Dim con As ADODB.Connection
Dim rst As ADODB.Recordset
DIM code_string as STRING
code_string = "select date from table WHERE foo foo foo"
and the result is a date that I want to save in a variable.
I have tried Set rst = CurrentDb.OpenRecordset(strSQL) that I found here: How to save the result of a SQL query into a variable in VBA?
and vba gives me an object required error.
Is there a way to save the results of a sql query in a vba variable?
Iam running a legacy VB6 application. I'm trying to execute a stored procedure that would go through a bunch of tables in SQL-SERVER, grab Data and put it into a table in SQL - SERVER. Do I need to declare and set a new recordset?
dim strSQL as string
strSQL = "Exec FillEmptyTable #blah = "& blah
It would seem that I don't need a recordset, but this doesn't execute
Now when i SET a new recordset, then it works
dim rs as adodb.recordset
set rs = new adodb.recordset
dim strSQL as string
strSQL = "Exec FillEmptyTable #blah = "&blah
rs.open strSQL, Connection
Is this right? I don't know why I need a recordset if I'm only creating one on SQL-SERVER side?
If you don't need a recordset because the SP returns no rows or you don't care about any rows it does return you can simply pass the SQL string to the connection object:
Connection.Execute strSQL, 0, adCmdText
See here for a more formal way using a Command object that removes potential the SQL injection vulnerabilities implicit in manually building SQL in a string.
I'm trying to get data from a SQL Server table into an ADO Recordset using the below code. Everything works fine with no errors, but I always get a record count of -1. I've confirmed that the database name and table are correct. If I use SSMS I can see that there is data in the table.
What am I missing here?
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim rsCount As Integer
cn.ConnectionString = "Provider=SQLNCLI11;Server=Server1;DataBase=Database1;Trusted_Connection=yes;"
cn.Open
Call rs.Open("Select * from table1", cn, adOpenDynamic, adLockOptimistic)
rsCount = rs.RecordCount
As i mentioned in a comment to the question, replace adOpenDynamic with adOpenStatic and you should get proper recordcount.
The use of the ADO Recordset's .RecordCount property requires either
the use of:
1. Static or Keyset server-side cursors or
2. A client-side cursor (which returns a Static cursor)
The RecordCount property will return -1 for a forward-only cursor; the
actual count for a static or keyset cursor; and either -1 or the
actual count for a dynamic cursor, depending on the data source.
More details here and here!
I have a project where I need to query a Teradata database and then copy the records returned to a SQL Server database. I can hit the Teradata db no problem and I can get the results into a DataTable. The SQL server db is already setup and has the same columns as the Teradata results (except for the auto id column). I am having trouble figuring out how to take the records in the DataTable and insert them into the SQL server db.
Here is what i have with some pseudo code where I didn't think the details were relevant:
Using cn As New TdConnection("User Id=XYZ12345;Password=XYZ12345;Data Source=teradataserver.company.com;Persist Security Info=False")
cn.Open()
Dim cmd As TdCommand = cn.CreateCommand()
'build the SELECT part of the command we will issue
cmd.CommandText = GetTeradataSqlString()
'setup the DataAdapter
Dim da As New TdDataAdapter(cmd)
' Provider specific types will be used in the data table
da.ReturnProviderSpecificTypes = False 'True=Use Teradata types, False=Use .NET types
' Adapter will determine how many statements will be batched
da.UpdateBatchSize = 0
Dim cb As New TdCommandBuilder(da)
'create a DataTable to hold our returned data
Dim dtCheck As New DataTable("TableCheck")
' Filling the data table with data retrieved from the select statement
da.Fill(dtCheck)
'create a DataSet to hold all of our tables
Dim dsMain As New DataSet("MainDataset")
'now we add the DataTable to our DataSet
dsMain.Tables.Add(dtCheck)
'at this point a cycle through the DataTable to the debug window shows we have the data we need from the Teradata db.
'now we will pump it into our SQL server database
Dim connSqlSvr As New System.Data.SqlClient.SqlConnection
connSqlSvr.ConnectionString = "Data Source=DestSqlServer;Initial Catalog=DestDb;Connect Timeout=15"
connSqlSvr.Open()
'now we create a SQL command to take the data in the Teradata DataTable and insert it into the SQL server table
Dim sqlCmd As New SqlCommand
With sqlCmd
.CommandType = CommandType.Text
Dim sbSqlCmd As New StringBuilder
sbSqlCmd.AppendLine("INSERT INTO [DestDb].[dbo].[Events] ([CityCode],[CarNum],[VIN],[Fleet],[EventItm])")
sbSqlCmd.AppendLine("SELECT City,CarNo,VIN,Fleet,EventDesc FROM #MyTable;")
.CommandText = sbSqlCmd.ToString
Dim sqlParam As New SqlParameter
sqlParam.ParameterName = "#MyTable"
sqlParam.SqlDbType = SqlDbType.Structured
sqlParam.Value = dtCheck
sqlParam.TypeName = "TableCheck"
.Parameters.Add(sqlParam)
.Connection = connSqlSvr
Dim rowsAffectedLoad As Integer = .ExecuteNonQuery()
debug.print(rowsAffectedLoad & " rows were loaded into the SQL server table.")
End With
'close and dispose the SQL server database connection
connSqlSvr.Close()
connSqlSvr.Dispose()
End Using
Running the code I get an exception:
"Column, parameter, or variable #MyTable. : Cannot find data type TableCheck."
I've looked for a method to insert a DataTable into a database and noticed many samples were using the INSERT INTO. I just dont think I am using the SqlParameter properly.
Your example appears to be using a Table Valued Parameter of type TableCheck but you have not defined that type within SQL Server. See http://msdn.microsoft.com/en-us/library/bb510489.aspx
CREATE TYPE LocationTableType AS TABLE
( LocationName VARCHAR(50)
, CostRate INT );
Although I can't guarantee that you can pass a TVP directly into a raw SQL statement.
I would actually suggest you use a different approach using SqlBulkCopy, http://msdn.microsoft.com/en-us/library/ex21zs8x(v=vs.110).aspx .
I got an SQL 2005 table with many (84 to be specific) fields (actually it is a query returned by a procedure)
It looks like when I access recordset fields placed later then some field placed earlier becomes empty while server had actually returned a value for it
Had anyone such problem?
My solution is to put such disappeared field at the end of a table so when it is accessed later by a code (here VBA) its value is still accessible BUT I see it as a big problem in ADODB.Recordset 2.8 as I should not care about field order
I know that question is not very specific but maybe someone had a similar issue?
One way to to make sure the field values are there is to pass on the recordset to a array like (You will have to build your own connection function):
Function getStoredProcedure() As Variant
Dim rs As ADODB.Recordset
Dim cmd As ADODB.Command
Dim conn As ADODB.Connection
Dim values As Variant
Set conn = getConn("Server", "Database")
Set cmd = New ADODB.Command
cmd.ActiveConnection = conn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "StoredProcedureName"
cmd.Parameters.Item("#TODAY") = today
Set rs = cmd.Execute
If Not rs.EOF Then
values = rs.GetRows
Else
Exit Function
End If
Set cmd = Nothing
getStoredProcedure= transposeArray(values)
End Function
From there you can always retrieve the values from the array. Otherwise, without seeing your code or understand what you are trying to do, I cannot tell if this is really an issue with ADODB because I cannot recreate this issue when pulling field items in any order I want such as: rs.Fields.Item(i).Value for i = any number in any order.
I met this problem twice. There are two exception fields in my query string. Running the query string in sql server, they can return the values but these two exception fields are empty when run the query string in VBA used recordset. I put these exception fields behind other normal fields in the query string, and then they can return value in the recordset instead of blank. So it's really a big problem.