Is there a better way to avoid error when using ADODB.Command with parameters? - vba

I had some existing VBA code that was building queries using string concatenation. I updated the code to use a command object with a parameter instead, but then started getting an error every once in a while that said String data, right truncation. After looking into it, I found out that if my collection of strings started with a string smaller than the rest, it would fail when it got to a longer string. To make it work, I just added some code to sort the collection from largest to smallest. Easy enough for this example since I know I won't have more than a few strings, but if I ever have a larger collection of strings, it might be an issue.
Is there any other way around this besides sorting the collection? Below is the section of code that causes the issue.
Dim lineNumbers As New Collection
Dim cnDb as new ADODB.Connection
Dim cmd as new ADODB.Command
Dim rs as new ADODB.RecordSet
lineNumbers.Add "001-3""-5116323-ABA"
lineNumbers.Add "001-1""-5116327-ABA-1 1/2""C" ' If not sorted, it fails when it tries this one
lineNumbers.Add "001-1""-5116327-ABA"
lineNumbers.Add "001-1""-5116327-ABA-1""C"
sQry = "SELECT COUNT(commondatalink) FROM commondata WHERE reportedlineno = ?"
cmd.ActiveConnection = cnDb
cmd.CommandText = sQry
cmd.CommandType = adCmdText
For i = 1 To lineNumbers.Count
Set rs = cmd.Execute(, Array(lineNumbers(i))) ' Error thrown here on unsorted list
If rs(0) > 1 Then
' do something...
End If
Next i

Seems like you can't re-use a command object like that: first time it runs it may infer the parameter type/length, and if a subsequent call uses a longer value that triggers the error.
You could try explicitly creating the parameter using cmd.CreateParameter() and setting the size to the size of the field being queried (or a little larger)

Related

VBA Unspecified Error When Querying PostgreSQL View

I am querying a PostgreSQL view in VBA:
Sub GetData()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
Dim wb As Workbook: Set wb = ThisWorkbook
With cn
.ConnectionString = "Driver={PostgreSQL ANSI(x64)};Database=a;Server=b;Uid=c;Pwd=d;Port=5432;sslmode=require;"
.Open
End With
SqlString = "SELECT * FROM myView;"
rs.Open SqlString, cn
...
End Sub
The query SELECT * FROM myview; executes as expected from within pgAdmin. Within VBA, it throws an Unspecified Error. I have used the same VBA code with other simple SQL queries against the same database and it's worked properly.
The view should return 8 columns. If I list those 8 columns in my query in VBA (instead of SELECT *...), the same Unspecified Error is returned.
However, if I leave off one specific column (accountcode, which is text), and instead just return the other 7, it executes properly.
What could be the issue with this one column that's causing it to work properly in pgAdmin but not in VBA?
Thank you.
Seems the problem is the (ODBC? see if it has configuration options that would be useful for this) driver making wrong assumptions about handling the TEXT data type.
Not familiar with postgresql in any way, but according to this post about casting from TEXT to VARCHAR, consider explicitly listing each column you're selecting from the view (SELECT * is bad practice anyway), and then you can do something like this:
Dim sql As String
sql = "SELECT Field1, Field2, ThatTextField::varchar FROM myView;"
Also consider moving the connection and query code over to a class module, where your ADODB connection can be declared as a module-level WithEvents variable; then you can handle ADODB connection events like WillConnect, WillExecute, and InfoMessage, which can give you more meaningful error messages.

How to use If .eof OR .bof condition in vb.net?

I am converting vb6 application into vb.net and in it .eof and .bof are used as condition. I am not understanding how to carry out these conditions in vb.net
sSql = "Select * From SPCsetup"
Set dbConn = New ADODB.Connection
Call openDBconn(dbConn)
Set rsConn = dbConn.Execute(sSql)
If rsConn.EOF Or rsConn.BOF Then
bFlag = False
Else
bFlag = True
End If
Well, first off, ADODB is a COM component so you could in theory just keep on using it. But it’s not really recommended as while it does have some beers and whistles they are not without their own problems. Ado.Net exist for a reason and it’s not just because they like writing new code to do the same thing.
Secondly if you use a datareader then bof (before first record) is somewhat relevant, but if you use a dataadapter to fill a table it’s not. It either has records or it doesn’t. If you want/need to continue using something like a record set, then that would be a datreader...there isn’t a BOF, but you don’t really need it. You find out nothing was returned, by the fact that nothing was returned.
sSql = "Select * From SPCsetup"
Using dbConn = New SqlConnection(connctionString)
dbConn.Open()
Using dbCmd = dbConn.CreateCommand()
dbCmd.CommandText = sSql
Using dbReader = dbCmd.ExecuteReader
bFlag = false
While dbReader.Read
bFlag = true
'Do something
End While
End Using
End Using
End Using
As noted from your other posts, you can (and should) do one of two things:
Create your own recordset class. It will take likely less then 30 minutes. You can then use your existing code with VERY little changes.
And you ALSO really (but really really) needed to share with everyone here WHAT KIND of data object you going to use, or are planning to use or are currently using in vb.net. (or ASK what kind of data object you should use).
Are you using a dataset?
Are you using a datatable?
Are you using a iList?
.net has a GAZZILLION choices, ranging from “iList”, and a bunch more I NOT listed out here. So we have little clue as to WHAT you currently using.
I am going to take a BIG FAT WILD guess and assume datatable.
Assuming we using a datatable, then you code would be something like:
Dim tblHotels As New DataTable
Dim strSQL As string = "select * from tblHotels"
Dim DataReader As New SqlDataAdapter(strSQL, My.Settings.SQLCon1string)
DataReader.Fill(tblHotels)
If tblHotels.Rows.Count = 0 Then
bFlag = False
Else
bFlag = True
End if
So, in place of
If tblHotels.EOF then
You would then have:
If tblHotels.Rows.Count = 0 Then

EOF value is always true even if there is record returned from VBA SQL

I am querying my access table using VBA and writing the query result in excel.
EOF is always true but BOF is False - even if the record count is 1 or 14 or 100. What possibly would be wrong? I can see the record count more than zero. get string value has data in it. Due to this there is no data written in the destination sheet except for Headers. The headers are coming in fine.
List of things tried but result was still same:
Added Move last and Move first command
Tried all possible combinations of Cursor location, cursor type, lock type
Tried with execute command
Tried with different MS access table
Tried early and late binding techniques
Below is my query and link below is my how my record set looks after SQL open statement.
Const MyConn = "Access DB location"
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
With con
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Open MyConn
End With
QuerySql = "SELECT * FROM Store_Location"
Set rs = New ADODB.Recordset
rs.CursorLocation = adUseClient
rs.Open QuerySql, con, adOpenStatic, adLockReadOnly, adCmdUnknown
rs.MoveLast
rs.MoveFirst
i = 0
For i = 0 To rs.Fields.Count - 1
Sheets("Search_Temp").Cells(1, i + 1) = rs.Fields(i).Name
Next i
Range("A2").CopyFromRecordset rs
rs.Close
Set rs = Nothing
con.Close
Set con = Nothing
While debugging this is what my record set looks like:
Building on this answer to a similar question, calling getString on a Recordset object has a side-effect of moving the recordset to EOF.
You don't call getString anywhere in your code but you've added a watch on rs.getString which is visible as the last entry in the Watches window. If you have a watch on rs.getString and you have a breakpoint in the code where rs is open then that breakpoint will cause the recordset to move to EOF.
Depending on where the breakpoint occurs, this might not cause any issues (e.g. if the recordset was already at EOF) but, in this case, it is moving the recordset to EOF before you have copied the data from the recordset.
To solve this issue, remove the watch on rs.getString. It's probably a bad idea in general to have items in the Watches window cause side effects. You could also avoid the issue by not having any breakpoints where the recordset is open but removing the watch entirely is more robust.
The issue of getString moving the recordset to EOF isn't mentioned in the ADO documentation but it's easy to reproduce this effect.
It's uncommon for someone to include the entire list of watches they had set in their question but I'm not sure this question was answerable without that information

Using SQL Parameters for ''

First time poster - medium length reader. I'm an entry level programmer, currently working on passing a SQL Stored Procedure some information that might contain a single quote (').
In the past, we've attempted to just use a .Replace("'","''") when passing this information, but recently, we've run into some issues with returning data and having the set changes and replaces in about 20 places (corporate, woo!).
I've been looking at using SQL Parameters to not have to worry about these buggers: ', but cannot see/understand the difference in my below code. The first block was the original working version. The second is my attempt at introducing #paras.
SQL is being passed through ByVal as a String
Previous Code:
Dim dbConnection As New SqlConnection(ConnectionString)
Dim dbCommand As New SqlCommand(SQL, dbConnection)
MsgBox(dbCommand.CommandText.ToString) //Returns proper procedure/paras
dbCommand.CommandTimeout = CommandTimeout
dbConnection.Open()
dbCommand.ExecuteNonQuery()
Code with SQL Parameters:
Dim dbConnection As New SqlConnection(ConnectionString)
Dim dbCommand As New SqlCommand("#SQL", dbConnection)
dbCommand.Parameters.Add("#SQL", SqlDbType.VarChar).Value = SQL
MsgBox(dbCommand.CommandText.ToString) //Returns "#SQL"
dbCommand.CommandTimeout = CommandTimeout
dbConnection.Open()
dbCommand.ExecuteNonQuery()
I feel the second block should be returning the same information. A MsgBox from the first block will return the proper SQL. The second however, just returns "#SQL", not the SQL value it seems to assign.
Is there a special way of refreshing the SQL Command?
Am I unable to only declare #SQL and replace it later?
Took a peek around MSDN as well as quite a few searches, leading me here already, with no luck.
Here is how you would make this a parameterized call. Kudos for taking the effort to protect against sql injection!!!
dbCommand.CommandText = "LoginPassword"
dbCommand.CommandType = CommandType.StoredProcedure
dbCommand.Parameters.Add("#userID", SqlDbType.VarChar, 30).Value = userID
dbCommand.Parameters.Add("#Password", SqlDbType.VarChar, 30).Value = password
dbCommand.ExecuteNonQuery()
One thing you need to make sure you do is when you use parameters you should always specify the precision or length. I don't know what yours should be in this case so you will need to adjust as required.
--please forgive me if there is a syntax error. I work with C# but I think I got this correct for vb.net

getting type error when inserting parameterized query with vbscript in classic asp

edit still recieving type mismatch error but updated code with help from comments
-updated code shown below
I am new to ASP and VBScript. I am trying to insert post data into a SQL database, however, I want to ensure the query is sterilized so I am trying to use a parameterized query. When I execute the query I get this error.
Microsoft VBScript runtime error '800a000d'
Type mismatch
my code looks like this
Set conn = Server.CreateObject("ADODB.Connection")
conn.Mode = 3
conn.open "Provider=SQLOLEDB;Data Source=xxx.xxx.xxx.xxx,xxxx;
database=db_example;uid=user;pwd=password;"
Dim oCmd
set oCmd= Server.CreateObject("ADODB.Command")
Dim sSQL
sSQL = "INSERT INTO tbl_Application (Expenses, Name, Why) VALUES (?, ?, ?);"
oCmd.CommandText = sSQL
oCmd.ActiveConnection= conn
Dim uPar
uPar = oCmd.CreateParameter("Expenses",200,1,255,session("Expenses"))
oCmd.Parameters.Append(uPar)
Dim vPar
vPar = oCmd.CreateParameter("Name",200,1,255,session("Name"))
oCmd.Parameters.Append(vPar)
Dim wPar
wPar = oCmd.CreateParameter("Why",200,1,255,session("Why"))
oCmd.Parameters.Append(wPar)
Dim oRS
oRS = oCmd.Execute()
I have tried typing the data as a varchar and a char. Please let me know if you have a fix for my problem. Also, I am intending to insert more than three pieces of data on the site - is there a better way than going through and making a parameter manually for each column?
The documentation for the CreateParameter method state that:
If you specify a variable-length data type in the Type argument, you
must either pass a Size argument or set the Size property of the
Parameter object before appending it to the Parameters collection;
otherwise, an error occurs.
129 corresponds to adChar, which is a variable-length data type and therefore requires you to pass a Size argument. Usually, you should use the defined length of the column in the database, but I have found that just using the length of the value I'm passing works also.