Hey all, I managed to integrate my Database quite well with Excel in the end, but in the end after I showed it to the bosses they asked me to develop the forms and reports in Access again. That did not take too long fortunately, so I ended up doing two front ends for one back end Database. But in the end it'll be a fully Access database with only one bit of Excel integration utilized; basically the transfer spreadsheet method to transfer daily end of trade Share price into Access.
Now I've come to a point where the database is pretty much ready to be split and populated.(What's the best way to tackle this, populate first or split first?)
The question I'm asking is below:
This might seem like a fairly simple question but so far, I haven't been helped with google or maybe I'm not using the right keywords in my search so I thought what better place to ask then on here; Is there a way to format the numbers that are generated through some VBA code and placed in a new table in Access, to make them look like:
So if it's 1,000,000 then it would appear as 1m
Or if it's 10,000 then it appears as 10k
So basically if it has three 0's then it's a K
If it has six 0's then it's an M
I have used some VBA initially to format the numbers in the following way:
ChangeInShare = Format(ChangeInShare, "#,##.00")
But then the request came to shorten some numbers down to make the reports look neater.
The final table, takes the values in a Text format btw.
Thank you
You can use the modulo operator to test if the number is dividable by 1000000 or by 1000 and then replace the last zeros.
Maybe this function points you to the right direction:
Public Function fmt(val As Long) As String
Dim result As String
If val Mod 1000000 = 0 Then
result = (val \ 1000000) & "M"
ElseIf val Mod 1000 = 0 Then
result = (val \ 1000) & "K"
Else
result = val
End If
fmt = result
End Function
Then some test calls:
? fmt(471000)
471K
? fmt(4711)
4711
? fmt(4000000)
4M
? fmt(40000)
40K
Hi Muffi D
additional to vanjes acceptable very good answer there is another idea:
what about the scientific notation?
Debug.Print FStr(10)
Debug.Print FStr(2000)
Debug.Print FStr(300000)
Debug.Print FStr(40000000)
Debug.Print FStr(5000000000#)
Debug.Print FStr(12)
Debug.Print FStr(2345)
Debug.Print FStr(345678)
Debug.Print FStr(45678901)
Debug.Print FStr(5678901234#)
Function FStr(ByVal d As Double) As String
FStr = Format(d, "0.####E+0")
End Function
then you will get:
1,0E+1
2,000E+3
3,E+5
4,E+7
5,E+9
1,2E+1
2,345E+3
3,4568E+5
4,5679E+7
5,6789E+9
if you need for Doubles (or Currency) you can go with vanjes answer but use the ModF-function instead:
Function ModF(ByVal value As Double, _
ByVal div As Double) As Double
ModF = value - (Int(value / div) * div)
End Function
Function fmtF(ByVal value As Double) As String
Dim result As String
If ModF(value, 1000000000) = 0 Then
result = (value / 1000000000) & "G"
ElseIf ModF(value, 1000000) = 0 Then
result = (value / 1000000) & "M"
ElseIf ModF(value, 1000) = 0 Then
result = (value / 1000) & "K"
Else
result = value
End If
fmtF = result
End Function
regards
Oops
You can split your database anytime before distribution to your users but I don't know what you mean by "populate."
See the "Splitting your app into a front end and back end Tips" page for info on splitting.. See the free Auto FE Updater utility to make the distribution of new FEs relatively painless.
I was also wondering; I have a CurrencyCode which defines whatever type of currency the Company is in; now all the calculations in VBA are done already. I wanted to know is there a way to write a SQL statement where for example; if the CurrencyCode=GBP then the resultant field is displayed as £23m instead of 23m etc for all the different currencies that are there in the field.
This again, needs to be hard-coded right now; the only thing is, if in the future if I'm not with the company, someone has to add a company in, which doesn't have the code to do that; then what will be the way to deal with? Will they have to have someone that needs to know about how to use SQL or VBA and be able to change; for this I would prefer doing it in SQL if possible because then I could put this in a manual for the database and SQL is much less daunting to use then VBA for beginners.
The resultant values have to be in the same column, so all the different IF's and Wheres need to be part of the same SQL statement, this is because of the reports that are there which use a single column for the different companies with different currencies.
Thanks!
Related
I see a lot of people that filter their Userform's ListBox using a TextBox or a ComboBox.
I'd like it to do mine, but unlike them, my Listbox is filled using a MySQL Recordset, while they are using an Excel Spreadsheet
There is my actual code, where SelectProduct is my Userform and ListRef is my Listbox.
Requete = "SELECT Reference,Nom,Marque,PrixVente FROM Produits_Beta"
rs.Open Requete, oConnect
SelectProduct.ListeRef.Clear
SelectProduct.ListeRef.Column = rs.GetRows
The Listbox is soon going to show 700+ results and I need a way for my user to filter them to find what they need.
If I used a Spreadsheet to get the Listbox value, my Filter code owuld look like this.
(Code originally from Ralph)
Dim i As Long
Dim arrList As Variant
Me.ListeRef.Clear
If TheoricalSheet.Range("A" & TheoricalSheet.Rows.Count).End(xlUp).Row > 1 And Trim(Me.TXBoxFilter.Value) <> vbNullString Then
arrList = TheoricalSheet.Range("A1:A" & TheoricalSheet.Range("A" & TheoricalSheet.Rows.Count).End(xlUp).Row).Value2
For i = LBound(arrList) To UBound(arrList)
If InStr(1, arrList(i, 1), Trim(Me.TXBoxFilter.Value), vbTextCompare) Then
Me.ListeRef.AddItem arrList(i, 1)
End If
Next i
End If
If Me.ListeRef.ListCount = 1 Then Me.ListeRef.Selected(0) = True
I could, but I would need a way to paste all a MySQL table to an hidden Spreadsheet, and, again, I have no idea how to do so.
The canonical way to filter SQL data for display is to use a WHERE clause in your query. That will work when you have seven, seven hundred, or seven million rows.
You might try something like this:
SELECT Reference,Nom,Marque,PrixVente
FROM Produits_Beta
WHERE Produit LIKE CONCAT('filter value', '%')
ORDER BY Produit
LIMIT 100
If you give an empty string for filter value you'll get the first hundred rows; your user will quickly see that a filter is necessary.
With no filter value, you get WHERE Produit LIKE '%' to not filter. With Pom as the filter value you'll get WHERE Produit LIKE 'Pom%' That matches Pomme, Pomme de terre, and Pomade, for example.
Edit You can use %pom% in LIKE. Here's the thing, however: if your search term has % coming first, the DBMS cannot use index lookups to find your data, so the searches will be slower. With a thousand rows to search, this doesn't matter. With millions of rows, it matters a lot.
Many developers of this kind of software use queries a lot to filter their data. DBMSs are built for it. The whole point of a DBMS is to allow software to handle vast sets of data efficiently.
Pro tip: Always use ORDER BY in your queries. If you don't the database server is free to present results in any order it finds most efficient at the moment. That's called unstable sorting and it drives users crazy.
The Worksheet.Visible attribute has three options, as follows:
xlSheetVisible 'The usual visible worksheet.
xlSheetHidden 'Worksheet that is hidden but may be turned visible by the user.
xlSheetVeryHidden 'Worksheet that is hidden but may only be turned visible via VBA.
If you were to create a hidden sheet to receive this data, you could try it as such:
Let's say you have first created a Worksheet in your workbook, (vba)named ws. In order to fetch the data from this recordset, you'll have to loop through its records and copy the value of each to a row:
'Header
With ws
.Cells(1,1) = "Reference"
.Cells(1,2) = "Nom"
.Cells(1,3) = "Marque"
.Cells(1,4) = "PrixVente"
End With
'Rows
Dim i as Long: i = 2
with Requete
If not (.EOF and .BOF) then
.movefirst
Do until .EOF
ws.Cells(i,1) = .Fields("Reference")
ws.Cells(i,2) = .Fields("Nom")
ws.Cells(i,3) = .Fields("Marque")
ws.cells(i,4) = .Fields("PrixVente")
.MoveNext
i=i+1
Loop
End If
End With
Then, if you don't want the user to have access to your sheet, just do:
ws.visible = xlSheetVeryHidden
If you plan to refresh the query and fetch the data again, you'll have to clear your worksheet beforehand.
Also, it might be good to order your data in your SQL query, so that the listbox gets populated alphabetically, should it suit your needs.
I'd like to add the following that deletes row that contains 0
If Cells(intFile, "CU") = "0" Then
Sheet1.Rows(r).EntireRow.Delete
End If
to my code below.
If CDate(rst(3)) >= Date Then
For intCount = 0 To rst.Fields.Count - 1
strHold = strHold & rst(intCount).Value & "|"
Currently on 'Cells' ElseIf Cells(intFile, "CU") = "0" Then
I'm getting compile error: sub or function not defined.
what should i put instead of Cells?
My reference: https://msdn.microsoft.com/en-us/library/752y8abs.aspx
The issue here is that there are many "flavours" of Visual Basic, and they aren't interchangeable.
Your application is running in MSAccess, and it will have certain objects, functions, etc, that are specifically applicable to that environment. The Cells object is something that is specifically applicable to the MS Excel environment and therefore Access VBA has no understanding of what a Cells object is.
(And the documentation you referred to is regarding the syntax for VB.Net - but the specific topic you were looking at is treated the same in all these different versions of "Visual Basic". But it does appear from that web page that the Then in an If statement is an optional keyword in VB.Net, while it is required in VBA, so there's one difference on even such an elementary piece of coding.)
If you change your code from
If CDate(rst(3)) >= Date Then
ElseIf Cells(intFile, "CU") = "0" Then
Sheet1.Rows(r).EntireRow.Delete
to be
If CDate(rst(3)) >= Date And rst(98) <> 0 Then
I believe you will be doing what you set out to do, i.e. exclude all records where the 4th field (3 due to zero-base) is before today and also exclude all records where the 99th field is 0.
I am currently making an search loop in VBA for my database. In this database I Have 2 Tables, one with the customers records called Main and another one with search words called Sparr. The idea is to use a filter to filter out the customers that match any of the search words in the Sparr table. Any customer that does not match any of the search words gets added to another table called filteredCustomerT.
For example:
table "Main"
Field "Mail"
mike#coolmail.com
john#hotmail.com
dave#mail.com
jonny#mailx.com
table "Sparr"
Field "sparrord"
hotmail
jonny
table "Testtable"
Field "testMail"
mike#coolmail.com
dave#mail.com
So if I run this VBA code I want john#hotmail.com and jonny#mailx.com to be filtered out. The Main table contains 200k records and the Sparr table contains 2k search words. I have wrote some VBA code that should loop through the Main table. For every record in the Main table a have another nested loop that loops through the Sparr table so see if there is any match. If there is not a match the VBA code copies the entry to anther table called Testtable. I Use the inStr function to do the matching.
Below I have posted the VBA code that does not seem to work. Can anyone help me ant maybe point out a fault in the code. I am very new to VBA programming.
Option Compare Database
Option Explicit
Sub filter()
Dim mainMail As Recordset
Dim sparrSokord As Recordset
Dim testtableTestmail As Recordset
Dim mainTemp As String
Dim sparrTemp As String
Dim match As Integer
Set mainMail = CurrentDb.OpenRecordset("Main")
Set sparrSokord = CurrentDb.OpenRecordset("Sparr")
Set testtableTestmail = CurrentDb.OpenRecordset("Testtable")
Do Until mainMail.EOF
mainTemp = mainMail![Mail]
match = 0
sparrSokord.MoveFirst
Do Until sparrSokord.EOF
sparrTemp = sparrSokord![sparrord]
If (InStr(mainTemp, sparrTemp) <> 0) Then
match = 1
Exit Do
End If
sparrSokord.MoveNext
Loop
If (match = 0) Then
testtableTestmail.AddNew
testtableTestmail![testMail] = mainTemp
testtableTestmail.Update
End If
mainMail.MoveNext
Loop
End Sub
InStr can operate in unexpected ways is you have nulls/empty strings/etc, involved.
I've noticed that you have nothing resetting the position of SearchwordWord back to the beginning of the record set once you reach the end.
Naturally you would do something like SearchwordWord.MoveFirst before the Do Until SearchwordWord.EOF. It doesn't hurt to do one before the Do Until customerMail.EOF either
I'll also note the Do Until always executes the contents of the loop, and then checks the condition at the end (which could be giving you unexpected results, especially with SearchwordWord being at EOF after the first successful pass of the loop.
You probably want to use a while/wend instead for both do untils (I practically never use them as it is). This is probably the biggest cause of your grief.
The problem is now solved. I just had to close some programs and try again with the updated code above. Worked just fine!
I am using Access 2007 and writing a macro in VBA.
"Analyze" subroutine is passed a recordset rev_rec. This recordset has two fields "PeriodNo" (Integer) and "Revenue"(double).
Minrev is a variable where I have to store the minimum value of "Revenue" field. The code that I am using is below.
Public Sub Analyze(rev_rec As DAO.Recordset)
Dim Minrev As Double
rev_rec.MoveFirst
Minrev = rev_rec("Revenue")
While Not rev_rec.EOF
If rev_rec("Revenue") < Minrev Then
Minrev = rev_rec("Revenue")
End If
rev_rec.MoveNext
Wend
rev_rec.MoveFirst
.
.
.
End Sub
I tried using DMin() with recordset but am not able to find a way to do that. Is there a way to do this without using any kind of loop?
I think your best bet is to build a new recordset using a SQL statement that only retrieves one record, the one with the minimum for the desired period. Another option is, you could open this particular recordset with the Order By on the Revenue column Ascending. This way you would know that the smallest value will be in the first record.
Andy Brown's suggestion of using DMin should also work. It's actually an idea very similar to my first suggestion.
The problem is that you're passing a recordset. If you just had the table or query name (or SQL statement), you could just use DMIN. Eg:
MinRev = DMIN("Revenue","TableOrQueryNameInQuotes","")
The third argument can be used to set some criteria. For example:
MinRev = DMIN("Revenue","TableOrQueryNameInQuotes","PeriodNo > 5")
Be warned, however, that the functions starting with D (DMIN, DLOOKUP, DSUM) run very slowly, although if you've got less than about 10,000 records you won't notice this.
So, in an Access Form or Report a good way to show something dynamic on the screen that is more complex than =[UnitPrice]*[Quantity] is to drop into VBA.
e.g. in this simplified example, the underlying table for this Report has two fields ShowTax and TaxRate. By making a TextBox's control source =GetTaxInfo I get to introduce some complexity in VBA:
Public Function GetTaxInfo() As String
Dim result As String
If Me!ShowTax = 0 Then
result = "Tax included # " & Me!TaxRate
Else
result = ""
End If
GetTaxInfo = result
End Function
OK, this works ... so long as I have a field somewhere else that refers to TaxRate. Otherwise it just prints #Error. It's as if it needs to preload the field before it can be used in VBA code. It isn't the end of the world because I can have a bunch of fields in the report all set to not be visible, but it's untidy.
So is it the case that you can't refer to a field in VBA code backing a report unless you have already referred to the field in the conventional way as a field baked into the report?
I can't remember encountering this limitation before. Or is it that I have a corrupt report? (I have tried the usual compact/repair, export/reimport the Report etc)
Edit:
the weird thing is ... now it's working again. And - I'm pretty sure - there is no control in the report. which is why I was thinking it was a corruption in the report.
You'll need a control on the form/report.
If this is too messy, you could put the function in a Module and use in the RecordSource (based on a query). No sense burying all this logic in a report when it could be used in other places as well.
Public Function GetTaxInfo(ShowTax as Boolean, TaxRate as Single) As String
Dim result As String
If ShowTax = 0 Then
result = "Tax included # " & TaxRate
Else
result = ""
End If
GetTaxInfo = result
End Function
Then the control is set to this field in this report and others.