Comma separating text in field in MS Access 2016 Query - ms-access

I have a csv file (updated 3 times a week. I have no control over its format, so cannot normalise it) that I have created an OBDC-link to a MS Access 2016 database.(I have chosen MS Access to refresh my skills with it, otherwise would complete in SQL). I have tried various permutations in setting up the OBDC link for the linked table however none give the optimum structure for the other fields.
The CSV file looks like this:
Fecha de Sorteo,Numero de sorteo,Numero de Juego,Nombre,Valores Principales,Comodines,DRAWNAME,Ganadores de Premio Mayor,Premio Mayor Garantizado
"8/25/2002 12:00:00 AM","1714","1","main","31,34,26,1,2,28","16","Loto","0",
I am trying to create a query to comma separate Field 3 into its 6 component parts. I have seen many examples that separate either 2 or 3 components (but never more than that) using InStr and the Mid functions such as seen here.
Do I have to create multiple expressions to separate this field into its components or is their an alternate solution?

I would suggest writing the data to a local table using the Split function. It will allow you to split that field into an array. Then use VBA to write the whole record into Access.
So, something like:
Dim db as Database
Dim rec as Recordset
Dim rec2 as Recordset
Set db = CurrentDB
Set rec = db.OpenRecordset("SELECT * FROM MyLinkedTable")
Set rec2 = db.OpenRecordset("SELECT * FROM MyLocalTable")
Do while rec.EOF = False
rec2.AddNew
rec2("Field1") = rec("Field1")
rec2("Field2" = rec("Field2")
strArray = Split(rec("Field3"), ",")
rec2("Part1") = strArray(0)
rec2("Part2") = strArray(1)
etc...
rec2.Update
rec.MoveNext
The above is "aircode" and completely untested, but it's probably pretty accurate and should get you started.

So your file seems to follow CSV specs, where values can be put between brackets ...
You should then be able to open it directly as an ADODB recordset(*). The column corresponding to the "Valores Principales" data will contain a plain text string, being your 6 values, separated by comas.
(*) don't know if it works with DAO recordsets ... For an example, check this link (it's excel but it's the same logic): return csv file as recordset

Related

Updating an Access Table with a CSV File Automatically

Problem Background:
I have a Powershell script that I can execute from my Microsoft Access Form that scans through file folders that contain information on different facilities, and produces a CSV that looks something like:
SiteCode FacilityNumber DocumentType HyperlinkPath
DKFZ 10 DD1400 C:\FACILITIES DATABASE\path
DKFZ 10 FLRPLN C:\FACILITIES DATABASE\path
SMQL 17 P1 C:\FACILITIES DATABASE\path
SMQL 17 P2 C:\FACILITIES DATABASE\path
So that way every time new files are added to those folders, I can just run this script and produce an updated list of everything I have:
C:\...\Output\scanResults.csv
All I need now is to take that CSV file and update (or even overwrite) a Table that I have in an Access database, which has relationships to other tables and is used by various Queries and Forms in the database. The CSV columns are already named and formatted in the same way as the Access Table.
I've looked at and tried to replicate the following threads:
VBA procedure to import csv file into access
Access Data Project Importing CSV File In VBA
VBA Import CSV file
The closest answer I found is:
Sub Import()
Dim conn as new ADODB.Connection
Dim rs as new ADODB.Recordset
Dim f as ADODB.field
conn.Open "DRIVER={Microsoft Text Driver (*.txt; *.csv)};DBQ=c:\temp;"
rs.Open "SELECT * FROM [test.txt]", conn, adOpenStatic, adLockReadOnly, adCmdText
While Not rs.EOF
For Each f In rs.Fields
Debug.Print f.name & "=" & f.Value
Next
Wend
End Sub
But this obviously won't write the data into the table, and I could not understand what the author was trying to say with respect to changing Select to Insert.
I've also found:
DoCmd.TransferText acImportDelim, "YourCustomSpecificationName", _
"tblImport", "C:\SomeFolder\DataFile.csv", False
Since both of these are from 2010, I wonder if there isn't a better way to accomplish this in Access 2013. And while I can do this all manually, I would like to incorporate it into the VBA code I use to tell Powershell to produce the CSV, that way I can make it and then upload it immediately.
Any help or suggestions are greatly appreciated. I'm still very green to Access, VBA, and SQL statements in general, so this has been very much a "learning as I go" process.
I prefer to use SQL clauses and queries to import such data. The details depend on your exact configuration, but it tends to look something like this:
SELECT *
INTO MyTable
FROM [Text;FMT=CSVDelimited;HDR=No;DATABASE=C:\...\Output].[scanResults#csv]
Or append the information to the table instead:
INSERT INTO MyTable
(SiteCode, FacilityNumber, DocumentType, HyperlinkPath)
SELECT *
FROM [Text;FMT=CSVDelimited;HDR=No;DATABASE=C:\...\Output].[scanResults#csv]
This allows you to do checks before importing (using a WHERE clause), import only specific values, and allows you to customize a lot without using external files.
DATABASE= is followed by your folder name (use {} if there are characters that need escaping in there), and then followed by your file name with . replaced with #.
You can execute it by either saving it as a query, or using it as a string in either VBA or a macro. Note that I rarely recommend macro's, but you can execute them using a scheduled task and close Access after importing.
To backup and restore a relation before and after updating, you can use the following functions:
Public Function DeleteRelationsGiveBackup(strTablename As String) As Collection
Dim ReturnCollection As Collection
Set ReturnCollection = New Collection
Dim i As Integer
Dim o As Integer
Do While i <= (CurrentDb.Relations.Count - 1)
Select Case strTablename
Case Is = CurrentDb.Relations(i).Table
ReturnCollection.Add DuplicateRelation(CurrentDb.Relations(i))
o = o + 1
CurrentDb.Relations.Delete CurrentDb.Relations(i).NAME
Case Is = CurrentDb.Relations(i).ForeignTable
ReturnCollection.Add DuplicateRelation(CurrentDb.Relations(i))
o = o + 1
CurrentDb.Relations.Delete CurrentDb.Relations(i).NAME
Case Else
i = i + 1
End Select
Loop
Set DeleteRelationsGiveBackup = ReturnCollection
End Function
Public Sub RestoreRelationBackup(collRelationBackup As Collection)
Dim relBackup As Variant
If collRelationBackup.Count = 0 Then Exit Sub
For Each relBackup In collRelationBackup
CurrentDb.Relations.Append relBackup
Next relBackup
End Sub
Public Function DuplicateRelation(SourceRelation As Relation) As Relation
Set DuplicateRelation = CurrentDb.CreateRelation(SourceRelation.NAME, SourceRelation.Table, SourceRelation.ForeignTable)
DuplicateRelation.Attributes = SourceRelation.Attributes
Dim i As Integer
Dim fldLoop As Field
Do While i < SourceRelation.Fields.Count
Set fldLoop = DuplicateRelation.CreateField(SourceRelation.Fields(i).NAME)
fldLoop.ForeignName = SourceRelation.Fields(i).ForeignName
DuplicateRelation.Fields.Append fldLoop
i = i + 1
Loop
End Function
And then, when importing:
Dim colRelBackup As Collection
Set colRelBackup = DeleteRelationsGiveBackup("MyTable")
'Delete MyTable
'Import new version
RestoreRelationBackup colRelBackup
(Note that the code is quite long, developed for a project several years ago, and not extensively tested. If a field name/type is not exactly like how it was before the import, the restore of the backup might fail and the relations will be permanently lost).
So some high level architect advice: replacing data versus replacing table
It is easier replacing data - - the new incoming data must be the exact same structure as the existing table (i.e. same field names and no new fields).
just fire a Delete Query to the existing table that clears out all records
then fire an Append Query to the linked CSV file that writes all those records into the existing table
very simple really.
You can replace the tables if you must - and you are already down this path. You can delete those table relationships entirely. That table relationship feature is useful - but not mandatory. You can create relationships at the query level as an alternative. Essentially the table relationships just auto create the query level relationships. If you delete the table relationships then one must simply create the table relationships at the query level manually - they don't automatically appear. Note however that if one is relying on cascade deletes or referential integrity, then removing table relationships will undo that - so you should check these points.
Deleting Table Relationships will not break any existing queries. Their table relationship join lines will remain intact.

Automating query selection based on combo box on a form

Okay, friends, I'm leaving my job in a week and a half, and I'm trying to make what I've done easier for my boss to do. He has no access knowledge, so I'm trying to create a form that will automate the reports I've been generating. Rather than create a different form for all the different reports, I'm trying to automate it from a table of parameters. Here's what I'm going for:
I have a table, which I have created, which is comprised of 5 fields. I'd like to use these fields to fill parameter fields in a standard form template. The five fields in my table are as follows:
The type of query being run (the result spit out)
The queries that generate this report, separated by a comma and no space. "QRYNAMEA,QRYNAMEB"
The Table which these queries generate, which will be used by transferspreadsheet
The destination excel file, which already has a pivot table set up to feed of the data.
The input sheet of this excel file. Currently, all of these sheets are called "Input". (that isn't important)
My issue comes with having no idea where to go after I've made my combo box. I know enough visual basic to automate my queries, but not enough to populate the form with the information in 3,4 and 5 (so far, I've been manually changing these for different queries). I have no idea how to look up the record in the table from the choice in the 'choosebox', and then select individual fields from that in my automation.
I'm pretty confident in my ability to parse #2 and automate the queries, and to put the values into the fields I'm looking at, but I don't know how to actually pull those values from the table, before I can do these things. I also can't seem to describe this well enough for google to help me.
Has anyone done something like this before? I'm assuming I just lack knowledge of one of the VBA libraries, but I've not had any luck finding out which.
edit:
my inclination at this point is to create a query for this table, which will return a single field depending on the input I give. I can imagine doing this in SQL, but I still don't know how to populate the forms, nor extract the field object from the table once I get it.
I have to head out for the day, but I'll be back on Friday to keep working on this, and I'll post my solution, once I find it. This seems like a unique conundrum, and it would be nice to give an answer to it.
Final edit: code is polished (does not have much in the way of error handling):
The first method, which pulls the fields from the table and populates the form, is activated by choosing a new entry in the combo box and looks like this:
Private Sub QuerySelect_Change()
Dim db As Database
Dim rec As Recordset
Set db = CurrentDb
Set rec = db.OpenRecordset("SELECT [Queries to Run], [Source Table], [Destination Spreadsheet], [Destination Sheet Name] FROM TBL_QRY_SETTINGS WHERE TBL_QRY_SETTINGS.[Query Type] Like '" & [Forms]![QuerySelector]![QuerySelect] & "';")
[Forms]![QuerySelector]![QueriesToRun].Value = rec("Queries to Run")
[Forms]![QuerySelector]![SourceTable].Value = rec("Source Table")
[Forms]![QuerySelector]![FileDest].Value = rec("Destination Spreadsheet")
[Forms]![QuerySelector]![SheetName].Value = rec("Destination Sheet Name")
Set rec = Nothing
Set db = Nothing
End Sub
The second code pulls that data to run the query. I like how this turned out. It runs when a button near the combobox is clicked.
Private Sub DynamicQuery_Click()
Dim qryArray As Variant
Dim i As Integer
qryArray = Split([Forms]![QuerySelector]![QueriesToRun], ",")
DoCmd.SetWarnings False
For i = LBound(qryArray) To UBound(qryArray)
Debug.Print qryArray(i)
DoCmd.OpenQuery (qryArray(i))
Next
DoCmd.SetWarnings True
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, [Forms]![QuerySelector]![SourceTable], _
[Forms]![QuerySelector]![FileDest], _
True, [Forms]![QuerySelector]![SheetName]
End Sub
Note that the final code for part (1) is almost the same as the selected answer, except that I am grabbing more than one field. This works because I know that I have unique "Query Types", and my recordset will only contain one record.
Anyway, I hope some people stumble upon this and find it useful. Send me a message if you do. As far as I can tell from brief googling, this sort of automation work has not been done in access. It should make it easier for access-illiterate to run their own queries, and be simple for designers to add to, if they want all their queries available after a few clicks.
Someone could conceivably use this to automate a variety of reports in sequence, by iterating through a table like the one I reference.
I may be massively misunderstanding what you're doing, but I think it's as easy as creating a new form using the form wizard. It will let you choose the table that contains the data, and it will let you choose which fields you want to add.
You can later change any of the textboxes to combo boxes which will allow you to limit the choices available to fill in.
Am I understanding that correctly?
EDIT: This will fill a variable (MyRandomField) with the contents of a field in a table
Dim db as Database
Dim rec as Recordset
set db = CurrentDB
set rec = db.OpenRecordSet("Select SomeField from SomeTable Where Something = 'SomethingElse'")
MyRandomField = rec("SomeFieldName")
set rec = Nothing
set db = Nothing

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.

Using VBA to create a dynamic table in Access 2010

I have an Access 2010 database with a VBA module that does some statistical analysis on the data. The results of the statistical analysis cannot be generated by SQL, but they can be presented in tabular format. Right now, I can run the VBA function in the Immediate window and it will loop over the results and write them to the terminal using Debug.Print().
I'd like to have the results of this function available to the rest of Access so that I can create queries and reports from the table of results. So what I'm looking for is how to turn my function into a "dynamic table" -- a table that doesn't actually store data, but stores the VBA function that runs and fills in the table data dynamically whenever that table is used.
I've spent quite a bit of time looking at creating tables dynamically via MAKE TABLE queries or using DDL in VBA, but all of these examples use SQL to create the new table from existing records. I can't use SQL to generate the results, so I'm not really sure how to coerce the results into an object that Access will recognize. Part of the problem is that I'm just not familiar enough with Access VBA terminology to know what I should be looking for.
My declaration is just "Public Function GenerateSchedule" . It has three code blocks: the first pulls the data I need from the database using a query and processes the RecordSet into an array. The second block performs the statistical analysis on the array, and the third prints the results of the analysis to the terminal. I'd like to replace the third block with a block that provides the results as a table that is usable by the rest of Access.
I use following code if I don't want to use DDL and SQL Query...
Set dbs = CurrentDb
Set tbl = dbs.CreateTableDef("tbl_Name")
Set fld = tbl.CreateField("Field1", dbText, 255)
tbl.Fields.Append fld
Set fld = tbl.CreateField("Field2", dbText, 255)
tbl.Fields.Append fld
Set fld = tbl.CreateField("Field3", dbInteger)
tbl.Fields.Append fld
Set fld = tbl.CreateField("Field4", dbCurrency)
tbl.Fields.Append fld
dbs.TableDefs.Append tbl
dbs.TableDefs.Refresh
and if you want to add a record you could do
Dim dbs As DAO.Database
Dim rs As DAO.Recordset
Set dbs = CurrentDb
Set rstVideos = dbs.OpenRecordset("tbl_name")
rs.AddNew
rs("field1").Value = "TEST "
rs("field2").Value = "TEXT"
rs("field3").Value = 1991
rs("field4").Value = 19.99
rstVideos.Update
I am not sure why you need to put the retrieved data into an array. It seems and extra step. If you can generate the statistics from the array, the same thing should be possible in a query. create another query, using the results query as one recordsource and make your calculations accordingly for the fields that you want created.
If we saw what you were trying to do, I think it could be made more simple.
This sounds like a disconnected recordset, or maybe "synthetic recordset," which is something ADO can do. I don't use ADO, so can't provide you with instruction, but maybe that will provide you with what you need.
Alternatively, depending on how you want to display it to the users, you might be able to do it native in Access. For instance, if presenting it on a form or report in a listbox is sufficient, then you could write a custom callback function and bind it to the listbox.

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