MSACCESS: How to update a memo field with large text - ms-access

I've got a memo field in MS Access 2010 and I'm trying to paste a large piece of text in it (about 160k chars).
When I paste directly in the table I get an error that "the text is too long to be edited".
The same thing happens when trying to do it via a form.
I've also tried to save the text to a file and the to read the file contents with VBA and then update the table by running a UPDATE table sql statement. In this case I get a runtime error 3035 that "System resource exceeded".
According to this postI should be able to store 1GB of data
How can I update a memo field with my text otherwise?

You can use recordsets to update the field. That way, you won't exceed the maximum length of an update query
(incomplete code, need more details to write more exact code)
Dim str As String
'Read text file into str
Dim rs As DAO.RecordSet
Set rs = CurrentDb.OpenRecordset("MyTable")
rs.AddNew
rs.Fields("MyMemoField").Value = str
rs.Update
rs.Close

Related

How to create Memo with property as Rich Text using vba [duplicate]

My users have a number of backend .accdb databases (which I can't access directly). I need to code some vba to modify the structure of some of the tables in these databases to convert Text Fields to Rich Text memos. (The fields already contain text including Access "rich-text" i.e. the relevant html coding).
I need to:
Modify the field to be a rich text memo.
Modify the existing contents (if applicable) to display correctly as Access rich text in forms, datasheets and reports.
I can write a SQl statement that will modify a field from TEXT (255) to MEMO:
ALTER TABLE tblSource ALTER COLUMN Detail1 MEMO
However, this leaves the resultant memo field as a plain text memo.
I have considered creating a new Rich Text Field and then copying the contents of the old one (using a SQL CREATE TABLE statement followed by an UPDATE statement that applies the Plaintext function to the contents of the old field and then copies the result to the new field, and then further SQl to delete the old field and rename the new) but can't find out how to create a rich-text memo (default seems to be plain text).
Extensive web searches haven't shown up any additional techniques I can deploy. This is a process that will be run once for each file, so it doesn't need to be elegant or quick but it does need to be bomb-proof!
Since Rich Text is not a datatype and is not a field property which can be defined or modified with a SQL statement, you will need VBA to set the field's TextFormat property.
You can adapt techniques from this code sample.
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Set db = CurrentDb
Set tdf = db.TableDefs("Table1")
Set fld = tdf.Fields("memo_fld")
Debug.Print "acTextFormatPlain: " & acTextFormatPlain & _
"; acTextFormatHTMLRichText: " & acTextFormatHTMLRichText
With fld.Properties("TextFormat")
Debug.Print "TextFormat: " & .Value
If .Value = acTextFormatPlain Then
.Value = acTextFormatHTMLRichText
Debug.Print "TextFormat changed to: " & .Value
End If
End With
Note that code is run from the database which contains the target table. If Table1 was actually a link to a table in another Access db file, the code would fail.
Note also that only applies to a memo field. The TextFormat property is not created for regular text datatype fields, so this will throw error #3270, "Property not found."
Debug.Print tdf.Fields("some_text").Properties("TextFormat").Value
Since you will be converting regular text fields to memo fields, that point is probably not a concern. I mentioned it only in case you stumble into it.
ColeValleyGirl discovered the TextFormat property is not always created for a new memo field.

Copy Access database query into Excel spreadsheet

I have an Access database and an Excel workbook.
What I need to do is query the database and paste the query into a worksheet.
The issue is Runtime. I have stepped throught the program and everything works, but it works extremely slow, we're talking up to 30 second run times per query, although most of this run time is coming with the CopyFromRecordset call.
The database has over 800k rows in the table I'm querying.
Currently at my company there are people every morning who manually query the tables and copy and paste them into excel. I'm trying to remove this process.
Here is what I have:
Sub new1()
Dim objAdoCon As Object
Dim objRcdSet As Object
' gets query information '
Dim DataArr()
Sheets("Data2").Activate
DataArr = Range("A1:B40")
For i = 1 To UBound(DataArr)
job = DataArr(i, 1)
dest = DataArr(i, 2)
If InStr(dest, "HT") > 0 Then
OpCode = "3863"
ElseIf InStr(dest, "HIP") > 0 Then
OpCode = "35DM"
End If
strQry = "SELECT * from [BATCHNO] WHERE ([BATCHNO].[Job]='" & job & "') AND ([BATCHNO].[OperationCode] = " & "'" & OpCode & "')"
Set objAdoCon = CreateObject("ADODB.Connection")
Set objRcdSet = CreateObject("ADODB.Recordset")
objAdoCon.Open "Provider = Microsoft.Jet.oledb.4.0;Data Source = C:\Users\v-adamsje\Desktop\HTmaster.mdb"
'long run time
objRcdSet.Open strQry, objAdoCon
'very long run time
ThisWorkbook.Worksheets(dest).Range("A2").CopyFromRecordset objRcdSet
Set objAdoCon = Nothing
Set objRcdSet = Nothing
Next i
End Sub
Any help is appreciated. I am new to VBA and Access so this could be an easy fix. Thanks
Excel is very good at getting data for itself, without using VBA.
On the DATA ribbon
create a connection to a table or view of data somewhere (eg mdb or SServer)
then use the "existing connections" button to add data from your connected table to a worksheet table (ListObject).
You can even set the workbook (ie connection) to refresh the data every 12 hours.
Repeat for all the tables /view you need to grab data for. You can even specify SQL as part of the connection.
Let excel look after itself.
I just grabbed a 250,000 row table from a "nearby" disk in 2 secs.
It will look after itself and has no code to maintain!
I don't see how the CopyFromRecordset can be improved. You could copy the recods programmatically (in VB) record-by-record but that will probably be slower than the CopyFromRecordset.
You can move the CreateObject statements out of the loop, With the connection and RecordSet already created, this could be faster:
Set objAdoCon = CreateObject("ADODB.Connection")
Set objRcdSet = CreateObject("ADODB.Recordset")
For i = 1 To UBound(DataArr)
...
next i
Set objRcdSet = Nothing
Set objAdoCon = Nothing
You could also try ADO instead of DAO. ADO seems to perform faster on large record sets.
But also the server could be an issue, for example, are there indexes on Job and OperationCode? If not, then the slowness could be the server selecting the records rather than Excel placing them in the worksheet.
Whelp, never found out why the CopyFromRecordset runtime was obsurd, but solved my problem by pulling the whole table into excel then into an array, looping through that and putting them in respective sheets. From 30min runtime to <1min

Using additional tables as lookup values in Access Query in IIF Statements?

I'm currently working with an Access(2010) Query that is taking multiple order items and GROUPING BY the order number. For example, a customer may have ordered 5 items under order number 123 but my query groups all of these individual items into one line with each item in it's own column. The objective is each individual order number is contained on one line because the query will be exported in a .csv format to 3rd party software.
Since I'm combining multiple records/items I'm running into issues when the description field is getting truncated based on 255 characters. Since working in a query I don't see the option of changing the text field to a memo field, so that won't resolve the issue and the GROUP BY clause is capping all the text fields at 255 chars anyway.
So, my question is instead of using the description fields from the current query is there a way that I can use an additional table to lookup an items description based on the part number ordered? Proposed new table would be something very simple like:
PART | DESC
123 Widget Z_Langs_AUS_INT<br>
567 Widget K_Langs_DOM_CAN<br>
890 Widget B_Langs_SM
So the ideal statement in the query would be something like IIF TABLE1.PART#ORDERED(from current query) = NEWTABLE.PART(from new table), then obtain NEWTABLE.DESC(from new table) otherwise ""
This would return the description of a specific part number if the part number fields matched otherwise leave the field blank because it likely doesn't contain a part number.
I'd appreciate any suggestions at this point. I may be going about this all wrong when trying to resolve this issue so fresh input is welcome.
Don't write that as a query. It's too complex to bother with, and is extremely simple to do with VBA and direct text access. Add a reference to the Microsoft Scripting Runtime (scrun.dll), use internal query objects for your SQL data, and use code similar to what's below.
Sub makeText()
Dim rs1 As Recordset, rs2 As Recordset
Dim oFSO As Scripting.FileSystemObject, oFile As Scripting.TextStream
Dim txtLine As String
Set oFSO = New Scripting.FileSystemObject
oFile = oFSO.CreateTextFile("FileName.csv")
Set rs1 = CurrentDb.OpenRecordset("baseQuery")
Do Until rs1.EOF
txtLine = rs1!OrderNumber.Value
Set rs2 = CurrentDb.OpenRecordset("innerQuery")
Do Until rs2.EOF
txtLine = txtLine & "," & rs2!itemDesc.Value
rs2.MoveNext
Loop
oFile.WriteLine txtLine
rs1.MoveNext
Loop
oFile.Close
End Sub
Below is a picture from an example database we used to use. It has our parts with all the info you could need, and then the part that matches up with our vendor. The query used will display each and every row where our part matches the vendor part.
You can also just put a [AskMeForMyPart] or whatever in square brackets like that to make the query pull based on that specific part.

Access exports only 1st column of query

All -
I'm embarrassed to ask something that appears to be so rudimentary, but I'm stuck.
Using Access 2007, I ran a query against a single 84K row table to produce a result set of ~80K row. I can't copy/paste the result set into Excel (Access fails copy/pasting > 64K rows). When I right-click on the query and export, no matter what format I try, it only exports the first row (ID).
How can I get Access to export the entire result set? (I've tried highlighting everything, etc. I also tried using the 'External Data' ribbon, but that just exports the original table, not the result set from the query I ran.)
Thanks!
I ran a query, highlighted everything by clicking on the little arrow in the upper left, CTRL-C, opened Excel, CTRL-V. Exported the whole thing. (Granted I didn't have ~100k rows like you, but I don't understand why it wouldn't handle that too.)
Or is that not what you want?
What if you copy 40,000 rows at a time to different tabs in your Excel file?
I have had a similar problem with Access 2013, so decided to share how to resolve it. The only way I could solve this issue was by using VBA.
Only update testSQL (easy to see when you go to SQL view of your query) and CSV_file_path (the file path of your CSV export)
Sub Export_ToCSV()
Dim testSQL As String
Dim UserInput As String
Dim db As Database, qd As DAO.QueryDef
Set db = CurrentDb
testSQL = "SELECT Table1.Column1, Table1.Column2, Table1.Column3 FROM Table1;"
CSV_file_path = "C:\temp\filename.csv"
Set qd = db.CreateQueryDef("tmpExport", testSQL)
DoCmd.TransferText acExportDelim, , "tmpExport", CSV_file_path, True
db.QueryDefs.Delete "tmpExport"
MsgBox ("Finished")
End Sub

How to populate a ComboBox with a Recordset using VBA

There is some literature available at expert's exchange and at teck republic about using the combobox.recordset property to populate a combobox in an Access form.
These controls are usually populated with a "SELECT *" string in the 'rowsource' properties of the control, referencing a table or query available on the client's side of the app. When I need to display server's side data in a combobox, I create a temporary local table and import requested records. This is time consuming, specially with large tables.
Being able to use a recordset to populate a combobox control would allow the user to directly display data from the server's side.
Inspired by the 2 previous examples, I wrote some code as follow:
Dim rsPersonne as ADODB.recordset
Set rsPersonne = New ADODB.Recordset
Set rsPersonne.ActiveConnection = connexionActive
rsPersonne.CursorType = adOpenDynamic
rsPersonne.LockType = adLockPessimistic
rsPersonne.CursorLocation = adUseClient
rsPersonne.Open "SELECT id_Personne, nomPersonne FROM Tbl_Personne"
fc().Controls("id_Personne").Recordset = rsPersonne
Where:
connexionActive: is my permanent ADO connection to my database server
fc(): is my current/active form
controls("id_Personne"): is the
combobox control to populate with
company's staff list
Access version in 2003
Unfortunately, it doesn't work!
In debug mode, I am able to check that the recordset is properly created, with requested columns and data, and properly associated to the combobox control. Unfortunately, when I display the form, I keep getting an empty combobox, with no records in it! Any help is highly appreciated.
EDIT:
This recordset property is indeed available for the specific combobox object, not for the standard control object, and I was very surprised to discover it a few days ago.
I have already tried to use combobox's callback function, or to populate a list with the "addItem" method of the combobox,. All of these are time consuming.
To set a control that accepts a rowsource to a recordset you do the following:
Set recordset = currentDb.OpenRecordset("SELECT * FROM TABLE", dbOpenSnapshot)
Set control.recordset = recordset
Works with DAO Recordsets for sure, I haven't tried ADO recordsets because I don't have any real reason to use them.
When done this way, a simple requery will not work to refresh the data, you must do a repeat of the set statement.
As was said, you have to get the RowSourceType to "Table/Query" (or "Table/RequĂȘte" if in french) in order to show query results in the combobox.
Your memory problems arise from opening the recordset (rsPersonne) without closing it. You should close them when closing/unloading the form (but then again you would have scope problems since the recordset is declared in the function and not in the form).
You could also try to create and save a query with Access's built-in query creator and plug that same query in the RowSource of your combobox. That way the query is validated and compiled within Access.
I found the trick ... the "rowSourceType" property of the combobox control has to be set to "Table/Query". Display is now ok, but I have now another issue with memory. Since I use these ADO recordsets on my forms, memory usage of Access is increasing each time I browse a form. Memory is not freed either by stopping the browsing or closing the form, making MS Access unstable and regularly freezing. I will open a question if I cannot solve this issue
good method with using the Recordset property, thanks for that hint!
Patrick, the method you shown on your page has a big disadvantage (I tried that too on my own): The value list can only be 32 KB, if you exceed this limit the function will throw an error.
The callback method has the big disadvantage that it is very slow and it is called once for every entry which makes it unuseable for a longer list.
Using the recordset method works very well. I needed this because my SQL string was longer than 32 KB (lot of index values for WHERE ID IN(x,x,x,x,x...)).
Here's a simple function which uses this idea to set a recordset to the combobox:
' Fills a combobox with the result of a recordset.
'
' Works with any length of recordset results (up to 10000 in ADP)
' Useful if strSQL is longer than 32767 characters
'
' Author: Christian Coppes
' Date: 16.09.2009
'
Public Sub fnADOComboboxSetRS(cmb As ComboBox, strSQL As String)
Dim rs As ADODB.Recordset
Dim lngCount As Long
On Error GoTo fnADOComboboxSetRS_Error
Set rs = fnADOSelectCommon(strSQL, adLockReadOnly, adOpenForwardOnly)
If Not rs Is Nothing Then
If Not (rs.EOF And rs.BOF) Then
Set cmb.Recordset = rs
' enforces the combobox to load completely
lngCount = cmb.ListCount
End If
End If
fnADOComboboxSetRS_Exit:
If Not rs Is Nothing Then
If rs.State = adStateOpen Then rs.Close
Set rs = Nothing
End If
Exit Sub
fnADOComboboxSetRS_Error:
Select Case Err
Case Else
fnErr "modODBC->fnADOComboboxSetRS", True
Resume fnADOComboboxSetRS_Exit
End Select
End Sub
(The function fnADOSelectCommon opens an ADO recordset and gives it back. The function fnErr shows a message box with the error, if there was one.)
As this function closes the opened recordset there should be no problem with the memory. I tested it and didn't saw any increasing of memory which wasn't released after closing the form with the comboboxes.
In the Unload Event of the form you can additionaly use a "Set rs=Me.Comboboxname.Recordset" and then close it. This should not be necessary regarding memory, but it may be better to free up open connections (if used with a backend database server).
Cheers,
Christian
A combo box control does not have a recordset property. It does have a RowSource property but Access is expecting a SQL string in there.
You can change the RowSourceType to the name of a user defined "callback" function. Access help will give you more information including sample code by positioning yourself on the RowSourceType and pressing F1. I use this type of function when I want to give the users a list of available reports, drive letters, or other data that is not available via a SQL query.
I don't understand what you mean by your third paragraph with respect to using data directly from the server side. Or rather I don't understand what the problem is with using standard queries.
In MS Access, it's ok, but in VB, you may use something like this using adodc (Jet 4.0):
Private sub Form1_Load()
with Adodc1
.commandtype = adcmdtext
.recordsource = "Select * from courses"
.refresh
while not .recordset.eof
combo1.additem = .recordset.coursecode
.recordset.movenext
wend
end with
End Sub