Access Crashes on Command Execute That Works [duplicate] - sql-server

until few days ago everything was working fine and then I run into this problem.
I wrote some code for Access vba in Microsoft 365 that run SQL query on some local and connected tables.
One of this connected table has a field set as data type Number - Decimal. As I mention, few days ago this field start to return empty string. This are few steps I try to investigate the problem.
I made a local copy of the connected table to make sure the problem was not coming from outside. No difference
I create a simple query access - SELECT * FROM [NameTable] and all the data were there
I run the same query in vba and the field in question return an empty string
I run the access query within vba
Set qdfNew = dbs.QueryDefs("Pippo")
Set RS = qdfNew.OpenRecordset
If Not (RS.EOF And RS.BOF) Then
RS.MoveLast
RS.MoveFirst
For iCurRec = 0 To RS.RecordCount - 1
Debug.Print RS.Fields("HSL_QUANTITA").Value
RS.MoveNext
Next iCurRec
End If
RS.Close
It returns an empty string
5. I change the data type of the table into Number - Integer, Long, Single and Double and in all these cases the query in vba return correct value
6. I modify the code in this way
Set qdfNew = dbs.QueryDefs("Pippo")
Set RS = qdfNew.OpenRecordset
If Not (RS.EOF And RS.BOF) Then
RS.MoveLast
RS.MoveFirst
For iCurRec = 0 To RS.RecordCount - 1
Debug.Print TypeName(RS.Fields("HSL_QUANTITA").Value)
RS.MoveNext
Next iCurRec
End If
RS.Close
While changing the data type the code return in the immediate windows:
String -> Number-Decimal
Single -> Number-Single precision
Double -> Number-Double precision
Integer -> Number-Integer
Long -> Number-Long
It looks like since few days ago vba cannot convert the decimal to a String anymore
I do not own the connected table hence I cannot change the data type.
I try to report the problem to the the Office help desk but they cannot solve the problem since is vba related.
Any suggestion?
Thanks

You have been hit by a recent bug:
Access VBA/DAO code may crash or report incorrect data for Decimal columns
Notice the included link for a temporary work-around.

Related

Access pass through query

I have a very easy Stored Procedure in SQL (this is my first one!) and I can run it after researching how to accomplish this. Essentially, it deletes all the current records in a table and appends a new set of records. (I have read that having two queries in the same Stored Procedure may cause the problem I am having.)
This works very well the first time I run it. I make a change to the open table to see if it works the second time. It does not. I can even close the database, reopen and rerun the code and it still doesn't give me what I expect.
What I have figured out is that if I try to open the "CurrentProc" query in the Access window, I get an error that says "Pass-through query with ReturnsRecords property set to True did not return any records." That's fine, because after I hit "OK", the code will work again and give me the expected return.
My question is what can I do with the code below so that I can make whatever change I want to in a table, but have it all reset whenever I call the function without having to open the CurrentProc query and getting the error message. (Yes, I have tried opening the query with OpenQuery, but that didn't work either.)
Please understand that, while I have been an Access developer for (yikes!) nearly 20 years, this is my first time trying to use SQL stored procedures. Running this same query in Access takes about 45 minutes. Using the Stored procedure, it literally takes seconds! I will be adapting this for use with much larger recordsets and rewriting some Access code as SQL stored procedures to leverage this power, so any ideas you can provide will be greatly appreciated.
Dim qdf As dao.QueryDef
Dim dbs As dao.Database
Dim rst As dao.Recordset
Dim sSQL As String
strConnect = "ODBC;DRIVER={SQL Server}" _
& ";SERVER=ourserver\equipment" _
& ";DATABASE=BDS"
Set dbs = CurrentDb
Set qdf = dbs.QueryDefs("CurrentProc")
qdf.Connect = strConnect
qdf.SQL = "exec AppendtoFRPbyModel1"
DoCmd.OpenTable "FRP by Model1"
Set rst = Nothing
Set qdf = Nothing
Set dbs = Nothing
Josetta
Here's what worked:
I changed ReturnsRecords to "no" and that didn't do anything, HOWEVER, when I added "DoCmd.OpenQuery "CurrentProc" I did not receive an error (as I did before when I tried to run it outside of code) and I did get the expected results every time. Thank you!!

Unable to query nVarChar(Max) field in Access 2010

have used Stack Overflow as a resource hundreds of times, but my first time posting a question for some help!
I've got a table in SQL Server 2005 which contains 4 nVarChar(Max) fields.
I'm trying to pull out the data from an Access (2010) VBA Module using ADO 2.8
I'm connecting using SQL driver SQLNCLI10
(I can't use a linked table, as the 'table' I will ultimately be querying is a Table-Valued Function)
When I then print / use the recordset, the data is getting jumbled and concatenated with other fields in the same record - with a bunch of obscure characters thrown in.
The VBA: (various other methods were tried with the same result)
Sub TestWithoutCasting()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim i As Integer
cn.Open "Data Source=ART;DataTypeCompatibility=80;MARS Connection=True;"
Set rs = cn.Execute("SELECT * FROM JobDetail WHERE JobID = 2558 ORDER BY SeqNo ASC")
Do While Not rs.EOF
For i = 1 To rs.Fields.Count
Debug.Print rs.Fields(i).Name & ": " & rs.Fields(i).Value
Next i
rs.MoveNext
Loop
End Sub
Example Output:
SeqNo: 1
CommandID: 2
Parameter1: 2 Daily Report é [& some other chars not showing on here]
Parameter2: [Null]
Parameter3: [Null]
Parameter4: [Null]
Description: Daily Report
Active: False
Expected Output:
SeqNo: 1
CommandID: 2
Parameter1: SELECT Day_Number ,Day_Text ,Channel_Group_ID [...etc]
Parameter2: [Null]
Parameter3: [Null]
Parameter4: [Null]
Description: Daily Report
Active: False
So, it's grabbing bits of data from other fields instead of the correct data (in this case, it's an SQL statement)
I then tried casting the nvarchar(max) fields as text at source
View Created:
CREATE VIEW TestWithCast
AS
SELECT jd.JobID, jd.SeqNo, jd.CommandID
,cast(jd.Parameter1 as text) as Parameter1
,cast(jd.Parameter2 as text) as Parameter2
,cast(jd.Parameter3 as text) as Parameter3
,cast(jd.Parameter4 as text) as Parameter4
,jd.[Description]
,jd.Active
FROM JobDetail jd
Now, I initially had some luck here - using the same code as above does bring back data - but when I use this code in my main code (which jumps in & out of other procedures); as soon as I've queried the first result of the recordset, it appears to wipe the rest of the records / fields, setting them to Null. I also tried setting the value of each field to a variable whilst the rest of the vba runs before getting the next record - but this doesn't help either.
It almost feels like I need to dump the recordset into a local Access table, and query from there - which is a bazaar workaround for what is already a workaround (by casting as text).
I there something I'm completely missing here, or do I indeed need to cast as text and load to a local table?
Thanks for any help - it's driving me mad!
ps. Hope I've given the right level of detail / info - please let me know if I missed anything key.
EDIT:
Yikes, I think I've done it / found the issue...
I changed the driver to SQLSRV32 (v6.01) - and seems to work fine directly against the text casted field.
So... why would it work with an older driver but not the newer 'recommended' (by various sources I read) as the one to use.
And... will there be a significant drawback in using this over the native client?
EDIT 2:
Ok, I've tried a few drivers on a few machines, in each case with both the TEXT CASTING and Directly to VARCHAR MAX..
[On my windows 7 machine w/ SQLSMS 2008]
SQL Native Client 10.0 - Neither method works reliably with this driver
SQL Server 6.01 - BOTH methods appear to work reliably - further testing needed though
[On our production server w/ SQLS 2005]
SQL Native Client (v2005.90) - Does not work at all with varchar(max), but DOES work with text casting
SQL Server (v2008.86) - BOTH methods appear to work reliably - further testing needed though
This should make deployment interesting!
It's not a real answer, because I did not test it, but ... You are using a "DataTypeCompatibility=80" parameter in your connection. As far as I know, DataTypeCompatibility=80 refers to SQL Server 2000, where the nvarchar(max) field type was still not implemented.
I had the same problem, solved it by converting the field to an nvarchar(1000). Would be an easy, compatible solution for your problem if 1000 chars is enough.

VB6 ADODB subsequent SELECT queries timeout after moving from SQL2000 to SQL2008

I'm facing timeout issues when performing multiple SELECTs after moving database to a different server, in a relic (VB6 application) I've been tasked to patch up. Things worked flawlessly in the old environemnt, the new one contains carbon copies of the old tables.
NOTE! The new database was built from scratch (that is, DBA ran many CREATE TABLE + INSERT scripts to create carbon copies of tables then fill them with the old data).
This is the error source:
// "conn" is being initialized outside the function
Public Function PerformOperation(ByRef conn as ADODB.Connection, query as string) as Boolean
Dim rs as ADODB.Recordset
//This below is the timeout source
rs.Open conn, query, adOpenStatic, adLockReadOnly
If Not (rs.EOF or rs.BOF) Then
rs.MoveFirst
//assign data to many variables
End If
ExitPoint:
If Not (rs Is Nothing) Then
If (rs.State = adStateOpen) Then rs.Close
Set rs = Nothing
End If
Exit Function
Error:
MsgBox "Blah blah"
Resume ExitPoint
Resume 0
End Function
Function is called like this
conn = New ADODB.Connection
conn.ConnectionString = "..."
conn.Open
For i = 1 To RowCount //reading data from a grid component (data is correct!)
//very long select here... kept short
query = "SELECT something FROM somewhere WHERE <manyFields> = <manyValues>"
If PerformOperation(conn, query) = True Then
//UPDATE another table based on the SELECT data
// NOTE: this occurs on a DIFFERENT, INDEPENDENT ADODB.Connection object
End If
Next i
The first time PerformOperation is called it goes through fine, second time through it times out no matter how long i set the CommandTimeout. It also works OK if the cycle "cycles" a single time.
Big problem here is, all I have to try things out is the production environment so I'll need to be extra careful. I also don't have a very deep DBA knowledge at hand... Just a very very old piece of software which will eventually be ported to .NET but needs to be dealt with in the meantime...
How can I check and/or fix this issue ? This has to work for any length of the For cycle
Many thanks for any suggestion (as always, if i missed any essential detail point it out and i'll provide it if i can).
EDIT #1
I've expanded the first and secondo code blocks to provide deeper details about what's going on. Comments changed in order to try and fix highlighting (single quotes mess up coloring).
EDIT #2
Enabling Multiple Active Result Sets (MARS) in the connection string didn't help either.
I'm a bit suprised that it has worked so well so far.
You have to close the recordset that you open, otherwise you will use up one database connection for each query you run. The database will reclaim the unused connections after a while, but if you use up too many connections too fast you will reach the limit for the number of connections per user, and the database will refuse any more connections.
The reason that it worked at all with the older database is probably because it created new connections as needed. This has been changed in later versions, and you would have to change the settings to allow the older, more resource wasteful behaviour. Changing that setting is however not a good solution to the problem.
Close the recordset and remove the reference when you don't need it any more. That will free up the connection so that you can use it for another query:
Public Function PerformOperation(ByRef conn as ADODB.Connection, query as string) as Boolean
Dim rs as ADODB.Recordset
'This below is the timeout source
rs.Open conn, query, adOpenStatic, adLockReadOnly
rs.Close()
Set rs = Nothing
' [cut]: PerformOperation returns true if SELECT returns something
End Function
Edit:
There are other open states than adStateOpen, you should probably check against the closed state instead:
If (rs.State <> adStateClosed) Then rs.Close
Issue was solved editing this line:
If PerformOperation(conn, query) = True Then
like this
If PerformOperation(connOther, query) = True Then
Basically, method call has been moved to its own connection, declared exactly like the other one. Now it works, though I don't really know why: this is the result of some wild editing in an attempt to patch things up.

Access query not using index

I have a table in Access with 1 field called HostName, it is a text field, with 100 char max. I use it to store DNS host names. The field is setup as the primary key. If I do the following query it returns the expected results, but takes about 8 seconds to complete on a table with 1 million records:
SELECT TOP 1 HostsRev.HostName
FROM HostsRev
WHERE (((HostsRev.HostName)>="test"))
ORDER BY HostsRev.HostName;
If I remove the "ORDER BY" part, it returns in less than 1 second, but doesn't always return what I would expect -- not the first record that is >= to "test".
I am doing the query via ADO from a C++ app, but I've tested in Access also, by creating a query, and get the same results.
What I need is to quickly find the first record, if any, that starts with a given string. I also tried using LIKE query but that had the same results. I need to do this because if I search on images.google.com, I need to know if the list contains google.com but not images.google.com (I actually store the host names in reverse string order to make this work correctly, and reverse the strings before doing the lookup).
The issue is that the TOP command on it's own does not apply sorting to the data, so without the ORDER BY it will return in a different order and thus give different results, you could try the following instead:
SELECT Min(HostName) FROM HostsRev WHERE HostName >= "test"
Not sure if this will give any better performance though but worth a go : )
I am not sure if you can do this from C++, not being a C++ programmer, but ADO supports a property .index to allow you to set the index you wish to use and a .seek method to search on that index. here is some code in VB for what it is worth.
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Set conn = New ADODB.Connection
conn.Open ConnectionString
rs.Open "mytable", conn
rs.Index = "primarykey"
rs.Seek "test", adSeekAfterEQ
If rs.EOF Then ' record not found

Combining two Microsoft Access Databases into one with a form to choose between them

So I've been assigned to work on two identical databases which are very poorly made. Both are split up into front/back ends, but that have different data.
Is it possible to combine both databases into one and create a form to allow the user to choose which "database" they will interact with?
To clarify, the school that utilizes these databases has a database for the Fall semester and Spring semester. Both contain the exact same forms/queries/tables, but the specific data differs. e.g. Maybe a teacher teaches 3 classes in the fall, but 4 classes in the Spring (the database is way more complicated that this though). The structure of the data remains the same, but the specific data changes depending on the database.
So, if they need to access data, they need to open the corresponding database. As a result, they are constantly open/closing different databases. What they want to do is be able to just use one database and switch between Fall/Spring.
Is this at all possible?
Without knowing the exact structure of your data it's hard to give specific answers, but my gut feeling is most definitely these two databases could be combined. Combine all the data in tables of the same name, but add a "Semester" column to any table that would result in duplicated data. Then simply create the application frontend so wherever necessary the user would select the Semester they want to view, and pass it to your queries retrieving the data. You could even carry the "Semester" value from the beginning as you point out, and pass it from screen to screen (w/ a global application-level variable).
It's been a while since I've had to code in Access in my professional life, but I'm sure there's a way to accomplish your goal. Maybe post more details of your specific data structure, and we can all jump in and give pointers!
Here is a table linking function that I've used many times over the years. In order to use this, you'll have to provide a couple of functions:
ThisTableShouldBeChanged is passed the name of a table and returns true if the link to the table should be re-linked as part of the database switch. This will allow you to selectively exclude tables from the switch.
GetConnectionString should return the name of the new connection string. Constructing a connection string to another Access database is simple, just put the full database path into this format: ";DATABASE=path"
In order to do what you want, you just have to run this function when the user chooses a new databse to open. One word of advice: I would put an unmistakable element in the UI, or example the background color of the screens, that will signal to the user which database they are working on so there is no confusion.
This function can also be used to link tables to an ODBC or SQL Server connection when deploying into a production environment. That's what I generally use it for.
Public Function LinkTables() As Boolean
On Error GoTo HandleError
Dim tdf As TableDef
Dim tdfNew As TableDef
Dim strName As String
Dim tdfs As TableDefs
Dim strSource As String
Dim strConnect As String
Dim blnThrowErr As Boolean
Dim arr() As Variant
Set tdfs = CurrentDb.TableDefs
Dim intCount As Integer
Dim i As Integer
intCount = tdfs.Count
strConnect = GetConnectionString()
i = 1
'Save a copy of the existing table names'
ReDim arr(tdfs.Count, 2)
For Each tdf In tdfs
strName = tdf.Name
If ThisTableShouldBeChanged(strName) Then
If tdf.Connect <> "" Then
strSource = tdf.SourceTableName
arr(i, 1) = strName
arr(i, 2) = strSource
i = i + 1
End If
End If
Next tdf
tdfs.Refresh
Dim strNameRep As String
'Create new linked tables'
For i = 1 To UBound(arr)
If arr(i, 1) <> "" Then
Set tdfNew = New TableDef
strName = arr(i, 1)
tdfNew.Name = arr(i, 1)
tdfNew.SourceTableName = arr(i, 2)
tdfNew.Connect = strConnect
strNameRep = strName & "_temp"
'rename the old table'
tdfs(strName).Name = strNameRep
tdfs.Refresh
tdfs.Append tdfNew
If Err.Number <> 0 Then
Debug.Print Err.Description
tdfs(strNameRep).Name = strName
Err.Clear
Else
tdfs.Delete strNameRep
End If
On Error GoTo HandleError
End If
Next i
tdfs.Refresh
LinkTables = True
ExitHere:
Exit Function
HandleError:
MsgBox Err.Description & " (" & Err.Number & ")"
LinkTables = False
Resume ExitHere
End Function
I think it should be possible to create queries for each of the tables with the original table name and name the new, combined tables with an extra letter or two. Let us say you have a table Students, the new table becomes JointStudents and you create a query Students:
SELECT F1, F2, F3 FROM JointStudents WHERE Semester=Forms!PickSem!txtSemester
This means that any form or query that refers to the table Students will now pick up the query students with the built in filter.
A least-initial-cost solution, setting up an interface to link to the appropriate semester, is still going to involve touching all the forms and adding another (for user-friendly control of the "active" db), and you'll still be incurring additional cost for any changes needed down the road (as they'll be made in two places rather than one).
You don't have to change the tables by hand: you can write SQL to do it. It's possible to write a procedure that steps through existing tables and adds a "Semester" column and populates it ... best to run it in both databases so that you can also populate the columns in the same procedure. If the tables are named the same in both databases, it would even be possible to run a procedure that would step through all of them, updating Fall, updating Spring, and appending Spring to Fall so that you have everything in one database.
A safer method would probably be to copy the Fall structure to a new table and modify that table, in case something happens and you need to "roll back" what you've done.
Given that this question was asked months ago and you're working with a school, you've probably either resolved the issue somehow or have had to put it aside, but if there's still a need for it, I can post VBA that should help.

Resources