I'm working with a script designed to compare values returned from a form against values from a database dumped to an array, via GetRows. The purpose of the check is to compare form values against database values and only update the matching ids' rows in the database.
I've seen this done with hidden variables in forms, but as we have quite a few users online at any given time, the values on the db end could change while a user was completing the form.
Currently, the code uses an inner and outer loop to run this comparison, with a temporary variable being assigned the current col/row from the aforementioned array. An lcase and trim operation are performed on the value to obtain the temporary variable.
This is causing a considerable performance drain, and I was wondering if the lcase/trim functionality could perhaps be performed during the creation of that array, rather than in a looping situation?
Here's my code:
**note: this utilizes the FastString Class for concatenation, thus the "FastString" and ".Append"
dim iRowLoop, iColLoop, zRowLoop, strChange, tempDbValsCase
Set strChange = New FastString
for iRowLoop = 0 to ubound(arrDbVals, 2)
for zRowLoop = 0 to ubound(arrFormComplete)
''#****below line is what is causing the bottleneck, according
''#****to a timer test
tempDbValsCase = lcase(trim(arrDbVals(1, iRowLoop)))
''#****
if (mid(trim(arrFormComplete(zRowLoop)),1,8) = trim(arrDbVals(0, iRowLoop))) AND (mid(trim(arrFormComplete(zRowLoop)),9) <> tempDbValsCase) then
dim strFormAllVals
strFormAllVals = arrFormComplete(zRowLoop)
strChange.Append strFormAllVals & ","
end if
next
next
On the database side (MS SQL Server 2008), the table from which the array is derived through GetRows contains the bit datatype column "Complete". The lcase and trim operations are performed upon this column of the array. Does the bit datatype add any hidden characters in the output? Visually, I don't detect any, but when I compare a value of "True" from the form input against a value from the array that looks like "True," it doesn't match, until I run the lcase and trim on the "Complete" column.
Try
dim iRowLoop, iColLoop, zRowLoop, strChange, tempDbValsCase
dim iCount1, iCount2, match
Set strChange = New FastString
iCount1 = ubound(arrDbVals, 2)
iCount2 = ubound(arrFormComplete)
for iRowLoop = 0 to iCount1
for zRowLoop = 0 to iCount2
' Assign array lookup to a variable '
tempDbValsCase = arrDbVals(1, iRowLoop)
' ...and then perform operations on it one at a time '
tempDbValsCase = trim(tempDbValsCase)
tempDbValsCase = lcase(tempDbValsCase)
' Assign this array lookup to a variable and perform trim on it '
match = trim(arrFormComplete(zRowLoop))
if (mid(match,1,8) = trim(arrDbVals(0, iRowLoop))) AND (mid(match,9) <> tempDbValsCase) then
strChange.Append match & ","
end if
next
next
Related
I am wishing to replace an include file with ~ 25 declared Constants by dynamically creating those constants from a Recordset (or Array). I have tried looping through a record set and have also tried populating a 2D array and then trying to set these Constants from the array values but it seems to take what is after the "Const" literal and not the value.
If (not rsVars.Eof) Then
cCount = rsVars.RecordCount
ReDim cArray(cCount - 1, 1)
For i = 0 To cCount - 1
cArray(i, 0) = rsVars("VariableNm")
cArray(i, 1) = rsVars("Value")
rsVars.MoveNext
Next
End If
For i = 0 To cCount - 1
' Display the Array Elements - WORKS and Displays All Variables and Desired Values
Response.write "("&(i+1)&") " & cArray(i, 0) & " = " & cArray(i, 1) & "<br>"
' Attempt to set Constant Variables - this next line errors - "Name_redefined"
Const cArray(i, 0) = cArray(i, 1)
Next
You could use the server.execute method to achieve this.
But it needs a FILE path to do so.
You will need to create a file dynamically using the FileSystemObject and pass the file's full path to the server.execute method to make those variables available as constants.
ex:
constants.asp
Note
Values will be CONSTANT Across user sessions and requests.
If you need a different set of constants by user, company, date etc, you will need to change the filename to include the unique constants-.asp and execute that file.
References
https://www.w3schools.com/asp/met_execute.asp
https://www.w3schools.com/ASP/asp_ref_filesystem.asp
I'm kind of new to Access VBA and I’m having issues with my loop.
I have reviewed various books, websites, asked various friends. Currently I’m trying to have a user input two characters, and then if the two characters equal a certain combination then it is supposed to equal a numeric value, and then use the value in a distance calculation.
The user inputed values are strings and everything else is declared as double.
I have 200+ combinations that I am testing and have tried case statements, DLookup, do while, do until, if, and elseif loops with no success.
I'm also limited by IT and I’m unable to use DAO code.
Any assistance would be greatly appreciated.
I would first suggest creating a table to formally define the various character combinations and the corresponding value for each combination. This has the advantage that the combinations may be easily maintained going forward, rather than defining such combinations as part of queries or in VBA code.
Such a table could be as simple as containing three fields:
Character 1 (Text)
Character 2 (Text)
Value (Number)
You could then populate such a table with your valid combinations:
With the combinations rigorously defined, you have many options regarding how to prompt the user to enter the two characters and obtain the correponding value.
A very simplistic approach might be to use a form with three textboxes:
Here, the first two textboxes would be unbound, and might be called char1 and char2, and the third textbox might be called result and have a Control Source property equal to:
=DLookup("Value", "LookupTable", "char1 = '" & [char1] & "' and char2 = '" & [char2] & "'")
This would yield the following behaviour:
Consider using Access as a database and GUI application by storing your 200+ combinations in a table with corresponding numeric value. Then have an SQL query filter by user input and use resulting value for needed calculations. Below requires creating and using four Access objects: table, query, form, and module.
Table (tblCombinations)
id combn1 combn2 numeric_value
1 A B 150
2 C D 225
3 E F 100
4 G H 75
5 I J 200
...
SQL (no loops needed; using Access form control values)
SELECT c.combn1, c.combn2, c.numeric_value
FROM tblCombinations c
WHERE c.combn1 = Forms!myForm!myUserInputField1
AND c.combn2 = Forms!myForm!myUserInputField2
And even pass needed numeric value in distance calculation formula which can be a VBA function in a standard module:
SELECT c.combn1, c.combn2, Distance_Calculation(c.numeric_value) As distance_result
FROM tblCombinations c
WHERE c.combn1 = Forms!myForm!myUserInputField1
AND c.combn2 = Forms!myForm!myUserInputField2
VBA (place in standard module)
Public Function Distance_Calculation(num_value As Long) As Double
...
Distance_Calculation = some_value
End Function
You can use following type function in your result form button or after event on both textboxes-
Dim resultValue as Integer
If DCount("numeric_value", "tblCombinations", "[combn1] = '" & Forms!myForm!myUserInputField1 & "' and [combn2] = '" & Forms!myForm!myUserInputField2 & "'") > 0 then
resultValue = Dlookup("numeric_value", "tblCombinations", "[combn1] = '" & Forms!myForm!myUserInputField1 & "' and [combn2] = '" & Forms!myForm!myUserInputField2 & "'")
txtResult = Distance_Calculation(resultValue)
Else
Msgbox "No such combination exist. Kindly check combimation", vbinformation, "Information"
txtResult = 0
End if
I need to summarize unique values from a row into a column that's in the same row. My goal is in the second row of the attached image where T:Z contains the data and AA:AC contains the summary (I typed the values in for the demo). The first row is what is currently occurring where I tried using a nested if function for values greater than zero, but I also tried using an index match function to no avail. The issue is I either receive duplicates in AA:AC or not all values are included.
Currently using Excel 2016
So if I understand you correctly, you are going to have a sheet of rows of data. You want to look in the columns T:Z and then generate a list of unique values (non-zero) in the columns AA:AC. I assume that you know you will never have more than 3 unique values, but I can't be sure that this wasn't just an omission.
Either way, the below code should work:
Sub Find_Uniques()
Dim X As Integer, Y As Integer, Z As Integer
Dim Temp_Strings() As String
For X = 1 to 10000 'This assumes you don't have more than 10,000 rows of data
ReDim Temp_Strings(1 to 5) As String
For Y = 20 to 26
If Range(Cells(X,Y).Address).Value <> "" And Range(Cells(X,Y).Address).Value <> 0 Then
For Z = 1 to 5
If Temp_Strings(Z) = "" Then
Temp_Strings(Z) = Range(Cells(X,Y).Address).Value
Exit For
End If
If Temp_Strings(Z) = Range(Cells(X,Y).Address).Value Then Exit For
Next Z
End If
Next Y
For Z = 1 to 5
If Temp_Strings(Z) <> "" Then Range(Cells(X,Z+26).Address)).Value = Temp_String(Z)
Next Z
Next X
End Sub
Thank you all for your help. Instead of extracting the data from the row, I wrote a macro that changed the zeros to blanks, deleted the blank cells, and shifted them to the left. After that it was easy to cut the range and paste it into the old data set to be analyzed.
Sub clean_data()
Sheets("Reason data").Range("H:Z").Replace 0, ""
Call delete_blanks
End Sub
Sub delete_blanks()
Sheets("Reason data").Range("H:Z").SpecialCells(xlCellTypeBlanks).Delete (xlToLeft)
Call move_data
End Sub
Sub move_data()
'Copies reason data and pastes it into data worksheet
Sheets("Reason data").Range("A3:K3", Sheets("Reason data").Range("A3:F3").End(xlDown)).Cut _
Sheets("Data").Range("A1").End(xlDown).Offset(1)
End Sub
My model takes two numbers from one sheet, adds the average to another sheet in the last cell of a defined column. The problem that I have is that when I insert a new column, the references get missed up and I'm trying to have a macro that would 1. take the average 2. look for a specific column on the second sheet 3. paste the averaged value to the last cell.
Please help me with this I have been trying to get my head around it for a long time.
my problem is that I have to insert new columns and I need to keep the references dynamic when adding a value to the last empty cell in a column. For example: if i have salary as col A, and expenses as Col B - in this model that I have now I put in .Cells(emptyRow, 1) and .Cells(emptyRow, 2) now if I insert a column between A and B the references 1 and 2 will not work. Is there anyway that I can work around this where if i add a new column it wont mess up the references in the macro?
Thank you.
This is the code that I have right now but it does not really work - when I insert a new column the column defined name does not shift right.
Sub demo()
Dim expCol As Long, FirstEmptyRow As Long
Range("B:B").Cells.Name = "expenses"
expCol = Range("expenses").Column
FirstEmptyRow = Cells(Rows.Count, expCol).End(xlUp).Row + 1
Cells(FirstEmptyRow, expCol).Value = 123
End Sub
P.S. 123 here is just an example for testing purposes. The value that would replace it in my model is the average I talk about in the question.
If your columns have headers (I guess they do), and your data has no gaps just use
Range("1:1").Find(columnName).End(xlDown).Offset(1,0) = 123
If a column can have just a header but no values, you need to add additional check if second row isn't empty.
If you create a named range this way (rather than the Range.Cells.Name way you were using), then when inserting columns the reference will be dynamic. Now if you insert columns between A and B later in the code, you can still use expCol and FirstEmptyRow to reference the first empty cell in the expenses column, where ever it may have moved to on the sheet, as long as you update them after each column insertion.
Sub Demo()
Dim expensesrng As Range
Dim Expenses As Range
Dim expCol As Long
Dim Exprng As Range
Dim FirstEmptyRow As Long
'set the original range to use for the expense column
Set expensesrng = Range(Range("B1"), Range("B1").End(xlDown))
'add the named range
ActiveWorkbook.Names.Add Name:="Expenses", RefersTo:=expensesrng
' create a variable to refer to the Expenses Range
Set Exprng = ActiveWorkbook.Names("Expenses").RefersToRange
expCol = ActiveWorkbook.Names("Expenses").RefersToRange.Column
FirstEmptyRow = Exprng.End(xlDown).Offset(1, 0).Row
Cells(FirstEmptyRow, expCol).Value = 123
'after inserting columns then you will have to get/update the column number
'of the expense named range and the first empty row before adding your new expense
'data to it
Range("B:B").Insert Shift:=xlShiftToRight
expCol = ActiveWorkbook.Names("Expenses").RefersToRange.Column
FirstEmptyRow = expensesrng.End(xlDown).Offset(1, 0).Row
Cells(FirstEmptyRow, expCol).Value = 123
End Sub
I am trying to load a large amount data in SQL server from a flat file using BULK INSERT. However, my file has varying number of columns, for instance the first row contains 14 and the second contains 4. That is OK, I just want to make a table with the max number of columns and load the file into it with NULLs for the missing columns. I can play with it from that point. But it seems that SQL Server, when reaching the end of the line and having more columns to fill for that same row in the destination table, just moves on to the next line and attempts to put the data on that line to the wrong column of the table.
Is there a way to get the behavior that I am looking for? Is there an option that I can use to specify this? Has anyone run into this before?
Here is the code
BULK INSERT #t
FROM '<path to file>'
WITH
(
DATAFILETYPE = 'char',
KEEPNULLS,
FIELDTERMINATOR = '#'
)
BULK INSERT isn't particularly flexible. One work-around is to load each row of data into an interim table that contains a single big varchar column. Once loaded, you then parse each row using your own routines.
My workaround (tested in T-SQL):
Create table with colum count = minimum column count of your import file
Run bulk insert (it will succeed now)
In last table column, you will find all rest items (including your item separator)
If it is necessery for you, create another full-columned table, copy all columns from first table, and do some parsing only over last column.
Example file
alpha , beta , gamma
one , two , three , four
will look like this in your table:
c1 | c2 | c3
"alpha" | "beta" | "gamma"
"one" | "two" | "three , four"
Another workaround is to preprocess the file. It may be easier to write a small standalone program to add terminators to each line so it can be BULK loaded properly than to parse the lines using T-SQL.
Here's one example in VB6/VBA. It's certainly not as fast as the SQL Server bulk insert, but it just preprocessed 91000 rows in 10 seconds.
Sub ColumnDelimiterPad(FileName As String, OutputFileName As String, ColumnCount As Long, ColumnDelimiter As String, RowDelimiter As String)
Dim FileNum As Long
Dim FileData As String
FileNum = FreeFile()
Open FileName For Binary Access Read Shared As #FileNum
FileData = Space$(LOF(FileNum))
Debug.Print "Reading File " & FileName & "..."
Get #FileNum, , FileData
Close #FileNum
Dim Patt As VBScript_RegExp_55.RegExp
Dim Matches As VBScript_RegExp_55.MatchCollection
Set Patt = New VBScript_RegExp_55.RegExp
Patt.IgnoreCase = True
Patt.Global = True
Patt.MultiLine = True
Patt.Pattern = "[^" & RowDelimiter & "]+"
Debug.Print "Parsing..."
Set Matches = Patt.Execute(FileData)
Dim FileLines() As String
Dim Pos As Long
Dim MissingDelimiters
ReDim FileLines(Matches.Count - 1)
For Pos = 0 To Matches.Count - 1
If (Pos + 1) Mod 10000 = 0 Then Debug.Print Pos + 1
FileLines(Pos) = Matches(Pos).Value
MissingDelimiters = ColumnCount - 1 - Len(FileLines(Pos)) + Len(Replace(FileLines(Pos), ColumnDelimiter, ""))
If MissingDelimiters > 0 Then FileLines(Pos) = FileLines(Pos) & String(MissingDelimiters, ColumnDelimiter)
Next
If (Pos + 1) Mod 10000 <> 0 Then Debug.Print Pos + 1
If Dir(OutputFileName) <> "" Then Kill OutputFileName
Open OutputFileName For Binary Access Write Lock Read Write As #FileNum
Debug.Print "Writing " & OutputFileName & "..."
Put #FileNum, , Join(FileLines, RowDelimiter)
Close #FileNum
Debug.Print "Done."
End Sub
The varying number of columns means it can't be parsed by the bulk insert code.
How does it know the correct number of columns? What if you supply too many?
You'll have to upload it to a table with 4 columns, and split out the rest later (or one big column)
Or pre-process it to generate an equal number of columns.
Try specifying a ROW terminator along with your field terminator.
BULK INSERT #t
FROM '<path to file>'
WITH
(
DATAFILETYPE = 'char',
KEEPNULLS,
FIELDTERMINATOR = '#',
ROWTERMINATOR = '\n' --Or whatever signifies the end of a row in your flatfile.
)
More info on this can be found here:
http://msdn.microsoft.com/en-us/library/ms191485.aspx