ADO recordset fails on "memo" datatype during import into Excel - sql

I am trying retrieve data from a SQL server, for use in some Excel 2003 macros. I would like to avoid the use of QueryTables, as I don't want this intermediate step of writing and reading from actual sheets. It seems time-consuming and pointless.
I have managed to get the recordset, but it contains empty data where the datatype is "memo", on the server.
Further, the program crashes where it tries to store the data into a Range. It appears to make it to the first "empty" field and then it gives me a 1004 Error Code.
Clearly the memo field is giving me grief. Can anyone make a suggestion as to how to get around this, or what I should be doing differently?
objMyConn.connectionString = "ODBC;" _
& "Provider=SQLOLEDB;DRIVER={SQL Server};SERVER=VANDB;" _
& "APP=Microsoft Office 2003;DATABASE=WPDB_BE;Trusted_Connection=Yes;"
objMyConn.Open
I've been searching online for ages, but this Access / ADO / Excel stuff is exceedingly painful. Please help.
Edit 1: I later modified the SQL query with "TOP 1" (SQL version of "LIMIT 1") and found that with that recordset, the memo fields were returned correctly. Similarly, I could SELECT a single problematic field, and get more rows, e.g. "SELECT TOP 52 bad_field FROM ..."
So I suspect that the issue is an ADO connection data size limit of some sort? It seems the Access "memo" type is simply like a "MEDIUMTEXT" MySQL type, so how would I get around such a limit? It's a separate question then, but what alternatives are there to the ADO connections?

You can use your ADO Connection object (objMyConn) to discover the data type (among other attributes) as ADO interprets it:
With objMyConn.OpenSchema(adSchemaColumns, Array(Empty, Empty, "your_table_name_here"))
.Filter = "COLUMN_NAME = 'your_column_name_here'"
MsgBox .Fields("DATA_TYPE").Value
End With
This will return the integer value of its respective SchemaEnum enum value, use the object browser to discover the enum value. Posting the results here could give a further clue to your problem.

Related

Applying Filter on a disconnected ADODB Recordset in VBA

I 've got a disconnected recordset which can be copied to an Excel worksheet by method "CopyFromRecordset".
But before that I need to set a Filter on the recordset by Filter property:
Rst.Filter = "UnitPrice >100"
Rst.MoveFirst
Sheet1.Range("A2").CopyFromRecordset Rst
The code raises a run-time error of some Automation Error at "Rst.Filter..." line.
Is there something that I did wrong?! or simply we cannot apply filter to a disconnected recordset??!
After a lot of testing and searching, I could find the source of problem.
It was due to definition of data-type of fields appeneded to the recordset at the recordset creation time, which were all of type adVariant = 12 for ease.
Seems that this data type is no longer fully supported, and for filtering recordset, all data types of fields must be precisely defined at the creation and used properly in Filter string. see the link below:
https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/datatypeenum?view=sql-server-ver15

MS Access Issue

Hello Stackoverflow community!
I have run into an issue and I'd love some advice.
I'm working with MS Access and I am trying to append two particular fields from one table to another; however this implementation is in a form and it gets a little complicated... So, I'll explain everything the best that I can
BACKGROUND INFORMATION:
First and fore most, I have two tables; one of which is a linked excel spread sheet from another directory (who is not willing to change any formatting what so ever, so I CANNOT make ANY changes to this file and it is being updated on a daily basis). This excel spreadsheet is very large and contains somewhere around 50 columns
The other table is not anywhere near as large but has around 20 columns and is meant to extract two columns from the excel spreadsheet (the first column and the third column). I'm trying to make a form for this database to be as user-friendly as possible and not many people in my office are familiar with the technicalities of Access queries and programming in VBA.
THE SITUATION:
On my form, the user will enter data into TextBoxA, from there they will click a button; this button will trigger a search through the linked excel spreadsheet for the data that was typed into TextBoxA. It will then copy the data from Field1 (which was the typed data) and Field3 and append these selected fields into the first two fields of the table in my Access Database. All of this is being done through a segment of VBA code
Private Sub CmdCloseForm_Click()
If IsNull(Me.TextBoxA) Or Me.TextBoxA = "" Then
MsgBox ("Field is empty, please try again!")
Else
Dim VendorNum As String
SearchingValue = Me.TextBoxA
Dim SQL As String
SQL = "INSERT INTO tbleRecord (Field1,Field2)" & _
"SELECT * " & _
"FROM tbleLinkedExcel " & _
"WHERE Field1 = '" & SearchingValue & "';"
DoCmd.RunSQL SQL
End If
End Sub
So the biggest issue here is that in Field1, and every time I try to run the code,
I receive an error; which I am assuming it is because of the space (unfortunately I cannot give the ACTUAL variable names out as it is confidential)
ERROR MESSAGE
The INSERT INTO statement contains the following unknown field name: 'FIELD 1'. Make sure you have typed the name correctly, and try the operation again.
The thing is, is that this 'FIELD 1' variable/name is not in my code, but in the linked excel spreadsheet (again, I am not able to change ANYTHING on this spreadsheet).
Thanks guys!

Identifying exact location of data entry issues when bulk copying or importing from excel to access

One of the requirements of a project that I have is to allow users to import or copy and paste in bulk a few hundred rows from excel to access. However, there is a reasonable chance due to human error that there will be some data validation issues between the imported data and the table structure/referential integrity rules. I would like to be able to identify exactly the field/s and record/s where these issues are occuring so that I can point them out to the user for correction.
As such the standard error essages like 'you cannot add or change a record because a related record is required in...' or 'data type mismatch in criteria or expression' are not descriptive enough to the exact location of the problem so even if I catch them I can't really give a better descriptor anyway
I am debating importing to a completely free text temporary table, then looping an insert to move one row at a time from the temp table to the properly validated table and using dbfailonerror to catch issues on individual records that need correction (the user needs to correct them I can't do this through code)
My question is whether this is a reasonable approach, is there a better/easier way, or a way to get a more specific error from access rather than using a loop?
Thanks
There are 2 ways to do this. I'm not sure what method you are using to do the import but if is as simple as copying rows from the excel sheet to the table Access will generate a Paste_errors table that will show the rows it couldn't import. The wizard will do the same thing but I think its prone to crashing.
The way I typically do it is actually have the end user use an excel template with a VBA backend that does the uploading. You can check each value conditionally and give a better descriptive alert and/or shuttle any defective rows to a temporary table for you to review.
You can do this the opposite way and do the import through Access VBA but that would be more coding since you would have to create an Excel object in code, open the sheet, etc.
I setup a Quickbooks export of a accounts receivable table by creating a User DSN on the local machine pointing to the Access file, opening an ADO recordset and looping through the rows one column at a time applying logic to each row.
Quickbooks would ask for an existing file to dump the data into so I made that a template file on the network. It sounds like your users may have to enter directly into the spreadsheet so you would have to distribute the template but the results are the same.
Example of Looping through the sheet and validating rows
Create a DSN file to the database and store it on a shared drive, this way you don't have to hardcode the string and if you need to change something, you only have to change the dsn file instead of redistributing templates.
Public Sub Upload
'Declare the main recordset that the records will be uploaded to.
Dim rstUpload as New Adodb.Recordset
'Declare a utility recordset object that you can reuse to check referential tables
Dim rstCheck as New Adodb.recordset
'Declare a utility command object that you can reuse to check referential tables
Dim SQLCommand as New Adodb.Command
'Declare the connection object to the database
Dim dataConn as New Adodb.Connection
'A tracking flag if you find something in a row that won't upload
Dim CannotUpload as Boolean
'Open the connection to the access database
dataConn.Open "\\Server\Share\Mydatabase.dsn" 'Your dsn file'
Set SQLCommand.ActiveConnection = DataConn
rst.Open "yourTable", dataConn, adOpenDynamic, adLockBatchOptimistic
For i = 1 to 100 ' Rows
*You may want to do a pass through the rows so you can get an accurate count, usually just loop through the rows until a column that must have data is blank. If your users are skipping rows that is going to be a problem.
rstUpload.AddNew
'Set the tracking Flag to False indicating you can upload this row, this will be changed if any field cannot be validated
CannotUpload = False
'First Column/Field: 'Non critical field, any value will do
rstUpload("Field1").Value = Range(i,1).Value '
'Second Column/Field has a referential integrity constraints
'Run a query against the table that has the values you are trying to validate.
SQLCommand.CommandText = "Select IDField From YourTable where ID = " & Range(i,2).Value
Set rstCheck = SQLCommand.Execute
'Check if any records were returned, if none than the value you are checking is invalid
If rstCheck.RecordCount > 0 Then 'we matched the value in the cell
rstUpload ("Field2").Value = Range(i,2).Value
else
'Design a flagging method, maybe highlight the cell in question
CannotUpload = True
End if
....continue through your columns in the same fashion, reusing the SQLCommand Object and the Utility recordset when you need to lookup something in another table
'After last column
If CannotUpload = False Then
'Nothing triggered the flag so this row is good to go
rstUpload.Update
Else
'Cannot Upload is true and this row won't be uploaded
rstUpload.Cancel
End If
Next i
dataconn.Close
set dataConn = Nothing
set rstUpload = Nothing
set rstCheck = Nothing
set SQLCommand = Nothing

Preserving Relationships on imported data

I'll start with the background story before explaining the problem with my code. I'm using MS Access 2010. I've been able to import a table of data with two columns. Then I was able to curate the data by adding fields with appropriate values to the imported table. Now, I need to take the curated table and integrate it into my data base. However, I cannot use any of Microsofts built in queries as none of these appear to be able to do what I need. The integration breaks the table apart, yes, but it needs to preserve the relationships of the data in each record.
To this end I've been writing some code in VBA:
Function IntegrateNIRData(curatedTable, queryRecords)
On Error GoTo Error_Handler
Dim db As DAO.Database
Dim rsCuratedTable, rsDBRecords As DAO.Recordset
Dim iCount As Integer
Set db = CurrentDb()
Set rsCuratedTable = db.OpenRecordset(curatedTable, dbOpenTable) 'open the recordset for use (table, Query, SQL Statement)
Set rsDBRecords = db.OpenRecordset("NIR_Samples_verify", dbOpenDynaset, dbExecDirect, dbOptimisticValue)
With rsCuratedTable
If Not (.BOF And .EOF) Then
Do While Not .EOF
' Rest of your code here.
rsDBRecords.AddNew
'Assign Fields here.
rsDBRecords![Product Name] = rsCuratedTable![productName]
rsDBRecords![Lot Number] = rsCuratedTable![lotNumber]
rsDBRecords!counts = rsCuratedTable![counts]
rsDBRecords![subsets] = rsCuratedTable![subsets]
rsDBRecords![Date Taken] = rsCuratedTable![dateTaken]
rsDBRecords.Update
rsDBRecords.Bookmark = rsDBRecords.LastModified
.MoveNext
Loop
End If
End With
rsCuratedTable.Close 'Close the recordset
rsDBRecords.Close 'Close the recordset
Error_Handler_Exit:
On Error Resume Next
'Cleanup after ourselves
Set rs = Nothing
Set db = Nothing
Exit Function
Error_Handler:
MsgBox "MS Access has generated the following error" & vbCrLf & vbCrLf & "Error Number: " & _
Err.Number & vbCrLf & "Error Source: IntegrateNIRData" & vbCrLf & "Error Description: " & _
Err.Description, vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
The Function hangs on this line, the second OpenRecordset:
Set rsDBRecords = db.OpenRecordset("NIR_Samples_verify", dbOpenDynaset, dbExecDirect, dbOptimisticValue)
To my understanding this may have something to do with Workspaces and the Jet engine not accepting a ms query that spans multiple tables. Of course, I could also be way off. Any advice at this point would be greatly appriciated.
Update:
Several of you have asked similar questions so I felt I should clarify the following:
1) NIR_Samples_verify is an MS access select query that generates a table of records from several of the tables in the database.
2) I keep getting two errors depending on what I set the RecordsetOptionEnum and LockTypeEnum to in the OpenRecordset method.
One is Error Number 3027 Database is Read-Only
Two is Error Number 3001 Invalid Arguement
3) To my understanding the rest of the code should be fine, it is just the OpenRecordset method that is causing the problem.
Update 2:
I am thinking that maybe access is not capable of doing what I would like. Let me illustrate. If I had two tables both with primary keys and these keys are referenced in a third table that links the two tables causing a many-to-many relationship, then the code would have to not only add the new data to the two tables, but also generate an appropriate record in the third table to maintain the relationship in the data. Hope that makes since. I do appriciate the help and experience.
Update 3:
Have been searching the net and found the following:
From this post it says the query is only updatable when:
1) It is based on a single table.
2) It is based on a query based on a single table.
3) It is based on a query based on tables with a one-to-one relationship.
Not knowing what the contents of NIR_Samples_verify are, I'd be highly suspicious of the dbExecDirect
From the help file
dbExecDirect
"Runs a query by skipping SQLPrepare and directly calling
SQLExecDirect (ODBCDirect workspaces only). Use this option only when
you’re not opening a Recordset based on a parameter query. For more
information, see the "Microsoft ODBC 3.0 Programmer’s Reference." "
I don't see you supplying any parameters.
-- Edit --
Typically I'll open a record set like this
Set rsDBRecords = db.OpenRecordset("select bar from foo where bar > 10", _
dbOpenDynaset, _
dbSeeChanges)
(Especially if I want to alter the data init)
Hopefully that'll move you further in your project.
-- Edit 2 --
It sounds like NIR_Samples_verify is to complicated to be edited. Actually, given that it is a join of multiple tables doing an Add on it doesn't make much sense, and Update MIGHT make sense in some cases.
Your ultimate solution is really going to be doing multiple Adds on multiple record sets (one for each table being referenced in NIR_Samples_verify); much like if you were entering the data into the DB by hand. You add the records that aren't dependant on anything else first (remembering to grab keys to use in the dependant tables).
As it turns out my hunch was correct. The problem had to do with MS Access having updatable and non-updatble queries (See my edits of the question). The main problem was not only does Microsoft not make this information apparent, but there is no master list on their site either. Thank you everyone for the help. Feel free to see this article for more details.

Problem with Excel SQL query code when it reads a closed workbook

Hi
I use the following code (part shown) which works great.
Basically I run a SQL query from excel which queries a closed workbook, extracts the info and puts it on the current opened sheet.
The problem is that one of the fields ("To") for some reason it always returns a value of nothing, even if every cell in that column has in fact information so I am not sure why it doesn't get the value.
All other columns are read perfectly.
Anyway knows why and whether there are some formatting (or other) specifics which affects SQL queries of this type?. There are no errors...simply the value is always nothing.
some code...
objRecordset.Open "SELECT * FROM [FILESAVED$] , objConnection, adOpenStatic, adLockOptimistic, adCmdText
for next_row=1 to objRecordset.count
Range("AF" & next_row) = objRecordset.Fields.Item("Validto")
Range("AG" & next_row) = objRecordset.Fields.Item("To")
Range("AO" & next_row) = objRecordset.Fields.Item("User Name")
objRecordset.movenext
next next_row
...more code
Thanks in advance
What kind of data is in the column? When you treat an Excel workbook as a database table, Excel infers the column type by matching it to whatever it infers about the majority of entries. (So if 60% look like numbers and 40% like text, it will treat them all as numbers.) This can cause errors, though it doesn't precisely match what you describe.