I am handling a few Excel things professionally and are running into issues. I am forced to use web queries to pull an exportable Excel files with parameters defined in the excel document. For reasons we are not able to use power query and I have to drudge up my old programming knowledge and put something together. This is not anything critical, just improving our work environment.
I have nailed down most of the process but have to condense everything or else I will reach the maximum size limit very quickly running it sequentially.
The sections I am working with will be set in rows of 4 and as many columns as needed until there are no more. I am planning on basing the criteria in the code on the first cell of section 1 because there will be a value in this cell if there is more. The cells do need to be formatted as text due to the web connection URL but this has not caused any issues.
The code I have is more individual instruction for excel then any kind of VBA logic but it has worked for the bit I have tested. The issue I have is condensing it down to loop through the sections in the "Input" sheet. I apologize for any sloppy declarations or names as I am going to clean it up to add error messages and comments once I have something that is functional. (I have left out the url information since it is internal.)
Sub myworkbook()
Dim wsi As Worksheet
Dim sourl, sosd, sosd2, soed, soed2, sodm, soaf, soev, soexc, soflag, sobn As String
Dim sa, sb, sc, ea, eb, ec, dm, af, ev, exc, flag, bn As String
Set wsi = Sheets("Input")
sourl = "URL1"
sosd = "URL2"
sosd2 = "URL3"
soed = "URL4"
soed2 = "URL5"
sodm = "URL6"
soaf = "URL7"
soev = "URL8"
soexc = "URL9"
soflag = "URL10"
sobn = "URL11"
sa = wsi.Range("A2").Value
sb = wsi.Range("B2").Value
sc = wsi.Range("C2").Value
ea = wsi.Range("A4").Value
eb = wsi.Range("B4").Value
ec = wsi.Range("C4").Value
dm = wsi.Range("F2").Value
af = wsi.Range("I2").Value
ev = wsi.Range("L2").Value
Sheets("Sheet1").Activate
Cells.Select
Selection.Delete Shift:=xlUp
flag = wsi.Range("A11").Value
bn = wsi.Range("A14").Value
With ActiveSheet.QueryTables.add(Connection:="URL;" & _
sourl & sosd & sa & sosd2 & sb & sosd2 & sc & soed & _
ea & soed2 & eb & soed2 & ec & sodm & dm & soaf & af & _
soev & ev & soexc & exc & soflag & flag & sobn & bn, _
Destination:=Range("A1"))
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.AdjustColumnWidth = True
.Refresh BackgroundQuery:=False
End With
ActiveWorkbook.Connections("Connection").Delete
The issue I am having at the moment is I want to take 1 cell from section 1 and 1 cell from section 2, update the querytable and output the exported to a separate sheet then move on to the second cell in section 2 with the same cell in section 1. We do not have any issues with the formatting with the output as we use it to put together reports manually but it would help if I could use a variable for the destination in the query table to add a column but I have been unable to figure out how to use a range variable for the destination so far.
Using the example image the flow needs to be;
Start at A1
Use value of A1 and A7
Update querytable, output to another sheet
Use value of A1 and B7
Update querytable, output to same sheet with one column of space
etc...
Move to next section using values G1 and G7 when section 1 is done
Update querytable, output to separate sheet
etc..
I have been unable to figure out how to do this and do not have any code examples as nothing has been functional enough to use as a start. I have been playing around with using the name as a starting point and creating a new sheet based on the name to output the information to and having the macro delete all of the sheets before running a new set when the macro is executed. If this is something that can be done I would love to do this as it would make it more automated and need less input and just require the users to keep to the format in the document if new sections are added.
The main issues I have been having is the sparse amount of information on using query tables for web connections due to power query's implementation and my knowledge of Visual Basic has diminished to a point that I feel like a baby learning to walk.
Related
I'm facing a odd behavior by applying different colours within one cell via VBA.
In my case there are hundrets of cells within one column, showing different work-packages.
My vba code exaclty does what it should do, by coloring identified strings (respecively work packages) via looping through the cells and identifiying each work package via RegExp.
Here there is one extract that is doing the coloring job:
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
.Global = True
.Pattern = suchmuster
If .test(objWks_myTable.Cells(row_myTable, 20).Value) Then
Set RegMC = .Execute(objWks_myTable.Cells(row_myTable, 20).Value)
For Each RegM In RegMC
objWks_myTable.Cells(row_myTable, 20).Characters(RegM.FirstIndex + 1, RegM.Length).Font.Color = vbOrange
Next
End If
End With
The issue appears as soon as I double click the cell after my makro run.
Then without any recognizable pattern, some characters are shown in a different color (mostly not only one character but a connected bunch of). In the picutre, the first cell shows the colours after my vba run, the second cell shows how it will immediately look like, if i double click it.
If I leave the edit mode via Escape, the original vba set colors will stay, If I leave the edit mode via Return, the undefined changes happen.
There are no formats nor format conditions set within the cells.
I really need somebodys help here. Would be great to find a solution!
Many Thanks !
This picture should show the issue:
Picture of the issue
I've found the issue.
First I tried also Instr instead of using a RegExp but the issue didn't disappear.
So I was investigating in my code that writes the strings into the cells.
And within that code I did the following:
dummy = dummy & " # " & z_trim(ctrl.Caption) & vbCrLf
ActiveCell.Value = dummy
The issue is because of vbCrLf
If I write the strings into the cells the following way, the changes within my coloring run are fixed, there is no change by entering the cell in edit mode:
dummy = dummy & " # " & z_trim(ctrl.Caption) & Chr(10)
ActiveCell.Value = dummy
Picture of fixed issue
It works, so I'm fine. But still interessted, why vbCrLf is causing such confusing thing?
I programmed a communication tool for the production floor. This tool will register what they have done, who has done it and on what time.
The following should check whether the textbox value equals the value in the worksheet or if the textbox (textbox is TextTools1) is empty. If this is true, then nothing should happen and the thus the value of the textbox is gonna stay the same.
If the textbox is not empty or is not equal to what has been previously saved in the worksheet (thus the value has changed), then it should be registered which operator has done it and what date and what time.
It works when the textbox is empty, but when the value of the textbox has stayed the same (thus TextTools.value=ActiveCell.Offset(0,23).value (Correct)) it still adds the operators name, date and time.
Something is going wrong when trying to compare the textbox value and the cell value, but cant put my finger on it.
Sheets("Checklist & overdracht").Visible = True
Sheets("Checklist & overdracht").Select
If TextTools1.Value = Range("AZ1").Value Or TextTools1.Value = Empty Then
Sheets("Checklist & overdracht").Select
rowloc1.Value = ActiveCell.Row
ActiveCell.Offset(0, 23).Value = TextTools1.Value
Else
Sheets("Checklist & overdracht").Select
rowloc1.Value = ActiveCell.Row
ActiveCell.Offset(0, 23).Value = TextTools1.Value & " " & "(" & cboOperator.Value & " " & Format(DateValue(CStr(Now)), "short date") & " " & Format(TimeValue(CStr(Now)), "hh:mm") & ")"
End If
Edit; changed it to the code above. I tested this in another userform (and used f8) and it works brilliantly, but when I put in the userform that will actually run this code, than it doesnt have the same result...
Edit2; So apparently something goes wrong with Range(AZ1).Value reference. Because when I enter a random value instead of the range and then run the code, it does work. Is there a different way of referencing?
Ok based on your comments
Stop using active cell when code from a user form is communicating to the compiler what sheet is what. You need to fully qualify what sheet you are using. Im not entirely sure where in the code the active sheet is being set but I am fairly certain the answer is never. Another reason selecting and referencing .ActiveWhatever is bad is a cardinal sin of vba is interacting with the actual application object instead of doing everything in memory. It bogs everything done and performance suffers considerably. When you start writing pretty dense stuff then you will inevitably suffer from issues where the compiler gets confused as to what thing it should be looking at and you'll have a grand ol' time of troubleshooting that nonsense.
Also, it might be a good idea to check for more than just "=Empty". What if there is a null or empty string? I tend to check for:
.value = "" OR ISNULL(.Value)=True OR .Value = vbNullstring
this isnt real feedback though - tons of people have different ways of doing the same thing.
Try:
Thisworkbook.Sheets("YOURSHEETNAME").Range("YOURRANGE").Offset(0,23).Value = Someothervalue.
Let me know if youre still facing issues.
I've written an export (Access to Excel) function in an application that works in ranges.
The user has 4 RadioButtons: A-F, G-M, N-R and S-Z.
Let's say the user has selected rbtnAF, which will load all customers into the grid where the Customer_Code field starts with an A, B, C, D, E or F.
The code to load in the data is as follows:
Dim strFields As String = "[Customer_Addresses].[Cust_Code], [Customers].[Customer_Name], [Customer_Addresses].[Contact_Code], [Customer_Addresses].[Contact_Name], " & _
"[Customer_Addresses].[Contact_Type], [Customer_Addresses].[Add1], [Customer_Addresses].[Add2], [Customer_Addresses].[Add3], [Customer_Addresses].[Add4], " & _
"[Customer_Addresses].[Add5], [Customer_Addresses].[Postcode], [Customer_Addresses].[Country], [Customer_Addresses].[Telephone], [Customer_Addresses].[Fax], " & _
"[Customer_Addresses].[Email], [Customer_Addresses].[Mobile_Phone], [Customers].[Customer_Category], [Customers].[Average_Payment_Terms], " & _
"[Customers].[Notes], [Customers].[salesRep], [Customers].[hoEmail], [Customers].[webpage] FROM Customers " & _
"INNER JOIN Customer_Addresses ON [Customers].[Customer_Code] =[Customer_Addresses].[Cust_Code]"
If rbtnAF.Checked = True Then
sql = "SELECT " & strFields & " WHERE [Customer_Addresses].[Cust_Code] BETWEEN " & _
"'A*' AND 'F*' ORDER BY [Customer_Addresses].[Cust_Code]"
Dim da As New OleDbDataAdapter(sql, con)
Dim ds As New DataSet
Dim dt As New System.Data.DataTable
da.Fill(ds)
dt = ds.Tables(0).Copy()
ugExport.DataSource = Nothing
ugExport.DataSource = dt
This was, I thought, working fine, I was able to load the correct ranges into the grid and export them as I wanted.
However, the user has come back to me and said it's not loading all customers.
I thought this was a bit weird, so I loaded up their database and tested it for myself. In the DB, there are 4 customers who fit into the range of A-F, as you can see in this image.
However, when I then view the customer range A-F in the export list, there are only 2 customers displayed.
It's also worth noting, in the Customer List screen there is a TextBox to allow the user to search by customer code - When I type in just a single F, all 4 customers are displayed as expected.
What on Earth is going on to only display 2 of the results in the Export List, despite there being 4 records that fit the criteria?
Checking the between syntax you will find that it treats * as a literal character see here https://support.office.com/en-us/article/Between-And-Operator-a435878d-63f7-4825-8c31-999432ae8223
You can use
Like "[A-F]*"
Instead though.
I would like to download prices from the internet. The concept works, when I define symb as a constant value (e.g. K15). But now, I want to download data from different links, where the part symb changes according to the value of the cells G13 to G22 in my spreadsheet. (In other words, I want to go through each row from G13 to G22 - each containing a different value for symb - and download the data from the respective link).
I tried that with a simple loop, defining the variable symb in each one of the loops:
For i = 1 To 10
Symb = Worksheets("Futures").Range("G12").Offset(i, 0).Value
Set qt = querysheet.QueryTables.Add( _
Connection:="URL;" & "http://download.finance.yahoo.com/d/quotes.csv?s=" & Symb & ".cbt&f=sl1d1t1c1ohgv&e=.csv", _
Destination:=querysheet.Cells(5 + i, 1))
Next i
Obviously, it doesn't work like this. I assume that it is not possible to define a variable within the loop, is it? Can somebody give me a hint how I can make that work?
There's something worng with your Connection String. When I get rid of the .cbt in the URL string, it works. Or, you may have forgotten to include some letters, so debug it in your browser and get the Connection string correct and it should work.
You may also want to modify the destination, like so, and refresh the table:
Set qt = querySheet.QueryTables.Add( _
Connection:="URL;" & "http://download.finance.yahoo.com/d/quotes.csv?s=" & Symb & "&f=sl1d1t1c1ohgv&e=.csv", _
Destination:=querySheet.Cells(5, 1))
qt.Refresh
you could use an array like this
symb(1 to 10) as String
for i=1 to 10
symb(i)=cells(i,1)
next i
I have an excel dokument (tab delimitted). Every time before i can insert the excel in my program, i need to map the clients column names to the ones in my app.
So before i begin developing a mapping tool - it would be better if such already existed. But, i don't know i didn't find it.
This tool would actually read an excel or txt file, allow me to name all the names of the new columns on the right and drag and drop (for example).
Maybe this tool has an xml or something where i can define my custom columns, and then it would show op on the right side.
I hope you know what i mean and that someone also had the need for this.
Thanks
info update
I wanted to mention a few things as an update to my question if it's not to late: I have about 50 headers/columns (example: First Name, Middle Name, Street1, Street2,..). So what i always need do every time a client gives me his excel file (contacts backup) is manually copy data for each of his column to the one on my side. And the problem is, every client has different column names, and also some of the columns of the client can go to multiple columns on my side.
So i think, if i can't find a solution, i would make a c++/qt app, which takes an excell and lets me to assign (dragdrop,etc..) every column of his side to on or more columns of my side.
I haven't done any vb programming, so if you can be a bit more detailed about how to aproach the solution i vb that would be great.
Firs part of answer: You can achieve it with a simple vbscript. Copy this code in a vbs extension text file and double click.
Dim oCN As Connection
Dim fs As Scripting.FileSystemObject
Set oCN = New Connection
Set fs = New Scripting.FileSystemObject
sCSVFileName = "C:\Temp\Test1.csv"
sSourceSql = "SELECT field1 as f1, field2 as f2, ... FROM " &
fs.GetFileName(sCSVFileName )
sDestinationTable = "yourAppTable"
With oCN
.Provider = "Microsoft.Jet.OLEDB.4.0"
.Properties("Extended Properties").Value =
"TEXT;HDR=YES;FMT=TabDelimited;MAXSCANROWS=0"
.Open fs.GetFile(sCSVFileName).ParentFolder
End With
sSql = "INSERT INTO [ODBC;DRIVER={SQL Server};Server=" &
ServerName & ";Database=" & DBName & _
IIf(IntegratedSecurity, ";Trusted_Connection=Yes;", _
";UID=" & UID & ";PWD=" & PWD & ";") & _
"]." & sDestinationTable & " " & sSourceSql
oCN.Execute sSql, , adExecuteNoRecords
More info at microsoft social forums
Also, you can parametrize mapping (source and destination tables and mapping fields) with a external xml file.
Second part of answer: You ask if somebody else have this needed and if this is a good idea. Well, this is a very good proposal. And for this reason they are some solutions with this functionality.
First of all, this kind of technology is named ETL. Extract - Transform - Load.
Each database has its own tool
SQL Server Import and Export Wizard
Oracle data pump and loader
etc.
Also exists specific technologies:
SSIS from microsoft.
IBM WebSphere DataStage
etc.
All this tools have mapping columns capabilities.
i use a generic mapping mechanism for that, configured by a couple arrays where you put the names used in your app (aDsNames) with these in your source, the csv file in your case (aDbNames)
I walk the fields in the source, check if the getDbName(name) is in aDsNames and if so write the value in the insertstring to the database.
You can do this both ways, usually my app requests a field to the databasemodule on the server, this module translates to the databasename and does the select.
Hope this is helpfull..
Cheers
select case store
case "store1Midoc"
aDbNames = array("id" , "beheerder", "datumlijst", "rnr13" , "datvan", "dattot", "opmerking", "status" , "waarde", "kode" , "type")
aDsNames = array("id" , "persnr ", "datum ", "rnr13" , "datvan", "dattot", "opmerking", "status" , "waarde", "kode" , "type")
aTypes = array("number", "string", "date" , "string", "date" , "date" , "string" , "number", "number", "string", "string")
case .....
end select
Function getDbName(dsName)
Dim a
getDbName = "undefined"
If instr(join(aDsNames,","),dsName) Then
For a = 0 to UBound(aDbNames)
If aDsNames(a) = dsName Then
getDbName = aDbNames(a)
End If
Next
End If
End Function