I am trying to insert a new employee into a table with a custom ID field when a form saves (Before Insert). To create the custom ID I am using the first 4 letters of the last name and the first 2 numbers of the first name, followed by a 2 digit number that is generated by the number of matching employee names:
i.e.
John Smith = SMITJO01 (first entry)
John Smith = SMITJO02 (second John Smith)
However, I do not know how to add the unique index (01, 02) into the function depending on how many other matching names are in the list:
Function GetNextEmployeeId(ByVal lastName As String, ByVal firstName As String) As String
Dim strNameComp As String
Dim nSEQ As Long
strNameComp = Left(lastName, 4) & Left(firstName, 2)
End Function
Edit:
Since the EMPLOYEE_ID is the primary key, it keeps giving meNull errors when I try to save a new entry, both on the BeforeUpdate and BeforeInsert form events.
Updating with my final solution, I had to modify because it was being finicky about formatting to numbers. Thanks to both you guys for the help!
Function GetNextEmployeeId(ByVal lastName As String, ByVal firstName As String) As String
Dim strNameComp As String
'Dim varMax As Var
Dim nSEQ As Long
strNameComp = UCase(Left(lastName, 4)) & UCase(Left(firstName, 2))
varMax = DMax("EMPLOYEE_ID", "EMPLOYEES", "EMPLOYEE_ID LIKE '" & strNameComp & "*'")
If IsNull(varMax) Then
' no one there yet
nSEQ = 1
Else
' split off the number part, convert to number, add 1
nSEQ = Val(Right$(varMax, 2)) + 1
End If
GetNextEmployeeId = UCase(strNameComp) & Format(nSEQ, "00")
End Function
Following Andre's suggestion of using DMax, try something like this:
Function GetNextEmployeeId(ByVal lastName As String, ByVal firstName As String) As String
Dim strPre As String
Dim varMax As String
strPre = Left(lastName, 4) & Left(firstName, 2)
varMax = DMax("EmployeeId", "tblEmployee", "EmployeeId LIKE '" & strPre & "*'")
GetNextEmployeeId = strPre & Right("0" & Right(Nz(varMax, "0"), 2) + 1, 2)
End Function
Get the maximum Employee ID that matches the Name:
varMax = DMax("EmployeeId", "tblEmployee", "EmployeeId LIKE '" & strNameComp & "*'")
and then you need a distinction:
If IsNull(varMax) Then
' no one there yet
nSEQ = 1
Else
' split off the number part, convert to number, add 1
nSEQ = Val(Right$(varMax, 2)) + 1
End If
Related
I was trying to Generate a unique ID by concatenating the User company + auto-generated ID
My output for my alphanumeric is "SNC001" but when I tried to generate the next ID I got the following error:
Conversion from string "SNC001" to type 'Integer' is not valid.
PS: the "SNC" came from this frm_Main_Menu.lblCompany.Text
Dim maxid As Object
Dim strid As String
Dim intid As Integer
Dim cmdid As New MySqlCommand
cmdid.Connection = cnn_MYSQL
cmdid.CommandText = "SELECT MAX(printed_id) as maxid FROM imports"
maxid = cmdid.ExecuteScalar
If maxid Is DBNull.Value Then
intid = "001"
Else
strid = CType(maxid, String)
intid = CType(strid, String)
intid = intid + 1
End If
Dim autoid As String = frm_Main_Menu.lblCompany.Text & intid.ToString().PadLeft(3, "001")
Dim cmd66 As New MySqlCommand
cmd66.Connection = cnn_MYSQL
cmd66.CommandText = "UPDATE imports " & _
" SET printed='" & "Y" & "', printed_id='" & autoid & "'" & _
" WHERE TIN = '" & id_selected &"'"
cmd66.ExecuteNonQuery()
You're assigning entire ID segment which has String type to Integer field/variable on this line, which is totally wrong and causing InvalidCastException:
intid = CType(strid, String) ' throws conversion error
The correct way is chopping off the prefix using Substring() starting from numeric part (i.e. 4th element which has index of 3) and convert the remainder to integer with either Convert.ToInt32() or Integer.Parse() method:
' using Convert.ToInt32
intid = Convert.ToInt32(strid.Substring(3, 3))
' alternative with Integer.Parse
intid = Integer.Parse(strid.Substring(3, 3))
Side note:
Better to use parameterized query instead of string concatenation to build the query, see following example below:
cmd66.CommandText = "UPDATE imports SET printed = 'Y', printed_id = #autoid WHERE TIN = #id_selected"
cmd66.Parameters.Add("#autoid", MySqlDbType.VarChar).Value = autoid
cmd66.Parameters.Add("#id_selected", MySqlDbType.Int).Value = id_selected
cmd66.ExecuteNonQuery()
The task: Create a report for Part Numbers that shows several types (On Hand, On Order, etc) in date buckets with each type totaled for the specific range.
For example:
Item 1 => (could be over 2000)
2/5/2017 2/19/2017 2/28/2017 (30 weeks)
On Hand 20 42 33
On Order 0 5 4
Each item is shown on it's own page with related metadata about the item. Each date bucket is based on a user-entered start date with a calculation running against the data set to determine what goes in which bucket and what the totals are.
I have this report fully working for one item. User types one item, selects a date, and the report is created using the following:
Inventory Meta general information and description of the item
Inventory Detail gets all the detailed information
Inventory Totals gets totals for each Types
GetInventory() VBA sets up the buckets and populates the totals
Using a query to get the date buckets would perhaps be easier to get the data into the report. Creating a query with 210 calculated columns (7 types, 30 weeks) wasn't a reasonable approach.
Naturally, selecting one item at a time is not what's wanted.
I have a select box that gets whatever Part Numbers are selected and creates a query on the fly for the Inventory Meta (main report). I have similar code working that runs with the Inventory Totals (sub report) to create a query on the fly for that.
But, as with the Inventory Totals query, each date is a unique value and is it's own row. What I need to be able to do is run the code to build the buckets for each item selected.
I'm stuck.
I have created an array of item numbers (whatever was selected). I can see what's in the array.
What I can't seem to figure out is how to feed each to the code that runs the date comparisons and calculations so that I get a full set of data for each Part Number.
With one number it was easy.... "this one"
vItem = [Forms]![fOptions]![ItemNumber]
Set db = CurrentDb
strSelect = "Select * FROM qInventoryTotals WHERE qInventoryTotals.ItemNumber = [this_one]"
Set qdf = db.CreateQueryDef(vbNullString, strSelect)
qdf.Parameters("this_one").Value = vItem
Set inv = qdf.OpenRecordset
The closest I've come is getting the report to show the same set of data for all part numbers. I suspect there is some small but critical thing, like where a particular loop starts or a variable I've missed or something.
The result of the following is a message box that repeats the same total for each of the part numbers.
Private Sub CreateOne_Click()
On Error GoTo Err_cmdOpenQuery_Click
'----------- Selection box check for dates -------------
If IsNull(Forms!fFish1!Week1) Then
MsgBox "A Sunday date must be selected", , "Please select a date"
ElseIf Weekday(Forms!fFish1!Week1) = 1 Then
'MsgBox "That is Sunday"
Forms!fFish1!Week1 = Forms!fFish1!Week1
Else
MsgBox "Starting Week needs to be a Sunday date" _
, , "Sorry, that's not Sunday"
' clears the 'not Sunday' selection
Forms!fFish1!Week1 = ""
Exit Sub
End If
'-------------------------------------------------
' Declarations =====================================
Dim db As DAO.Database
Dim iMeta As DAO.QueryDef
Dim i As Integer
Dim strSQL As String
Dim strWhere As String
Dim strIN As String
Dim flgSelectAll As Boolean
Dim varItem As Variant
Dim strSlect As String
Dim vItem As Variant
' Setup -------------------------------------
Set db = CurrentDb()
strSQL = "SELECT * FROM qInventoryTotals2"
'----------------------------------------------------------------------
' Get whatever is selected and loop through the selections
' This defines which numbers are in the list
'----------------------------------------------------------------------
For i = 0 To Forms!fFish1.box4.ListCount - 1
If Forms!fFish1.box4.Selected(i) Then
If Forms!fFish1.box4.Column(0, i) = "All" Then
flgSelectAll = True
End If
strIN = strIN & "'" & Forms!fFish1.box4.Column(0, i) & "',"
End If
Next i
'Create the WHERE string, and strip off the last comma of the IN string
strWhere = " WHERE [ItemNumber] in " & _
"(" & Left(strIN, Len(strIN) - 1) & ")"
'If "All" was selected in the listbox, don't add the WHERE condition
If Not flgSelectAll Then
strSQL = strSQL & strWhere
End If
'-------------------------------------------------------
' Create a query that has all the selected item numbers
db.QueryDefs.Delete "qInventoryTotals3"
Set iMeta = db.CreateQueryDef("qInventoryTotals3", strSQL)
Set inv = iMeta.OpenRecordset
'==========================================================================
' Create an array to pull out each of the Item numbers one at a time
Dim Count As Integer, r As Integer
Count = 0
For i = 0 To Forms!fFish1.box4.ListCount - 1
If Forms!fFish1.box4.Selected(i) Then
vItem = Forms!fFish1.box4.Column(0, i)
'vItemFilter = Forms!fFish1.box4.Column(0, i)
'MsgBox (vItem), , "one by one"
Count = Count + 1
End If
Next i
''MsgBox (Count), , "count how many items are in the set"
' Get the count for how many items are in the currently selected list
' Displays one item at a time -
' Set up the array ------------------------------
'------------------------------------------------
ReDim vItem(Count)
r = 0
For i = 0 To Forms!fFish1.box4.ListCount - 1
If Forms!fFish1.box4.Selected(i) Then
vItem(r) = Forms!fFish1.box4.Column(0, i)
r = r + 1
End If
Next i
'Check the values stored in array
''For i = 0 To Count - 1
''MsgBox vItem(i), , "show all values from the array"
''Next
' have all values from the array. Each in it's own message box
'===============================================================================
' Set up the item numbers ---------------------------
Dim part As Variant
part = vItem
With vItem
For i = LBound(vItem) To UBound(vItem) - 1
MsgBox ("There are" & " " & (vItem(i)) & " " & "fishies"), , "Whatcha' got now?"
' cycles through each number
' Past Due ============================================
Dim tPOPast As Double
Dim tBCPast As Double
Dim tBPast As Double
Dim tEPast As Double
If inv!ItemNumber = part(i) And inv.Fields("RequiredDate") < Forms!fFish1!Week1 Then
'displays the first part number with it's value, then the remaining numbers with no value
' If inv.Fields("RequiredDate") < Forms!fFish1!Week1 Then
'displays each of the part numbers with the same value
tBPast = inv.Fields("TotalOnHand")
tPOPast = tPOPast + inv.Fields("SumOfSupply")
tBCPast = tBCPast + inv.Fields("SumOfDemand")
' Calculate ending inventory for the week ===================
tEPast = tBPast + tPOPast + tBCPast
' Show something for testing ==============================
MsgBox (tBPast & " " & part(i)), , "show Me the money" ' displays same total for each part number
End If
'end this condition, next condition follows
'----------------- do it again -------------------------------
Next
' Finished with the weekly buckets =====================================
End With
'=========================================================================
'-------------------- error management for the selection box ------------------
Exit_cmdOpenQuery_Click:
Exit Sub
Err_cmdOpenQuery_Click:
If Err.Number = 5 Then
MsgBox "Pick one, or more, item numbers from the list" _
, , "Gotta pick something!"
Resume Exit_cmdOpenQuery_Click
Else
'Write out the error and exit the sub
MsgBox Err.Description
Resume Exit_cmdOpenQuery_Click
End If
'---------------------------------------------------------------------------
End Sub
The solution I found was to set variables for the values from the array and use them to dynamically update a table. From that, I created a query to sum the values and used that as the basis for the report. The key was GetRows()
Get the unique items and read the the rows of data into the first array
Dim rNum As Integer
rNum = myItems.RecordCount
Dim varItem As Variant
Dim intRi As Integer 'rows of unique items
Dim intCi As Integer 'columns of unique items
Dim intNCi As Integer
Dim intRCi As Integer
varItem = myItems.GetRows(rNum)
intNRi = UBound(varItem, 2) + 1
intNCi = UBound(varItem, 1) + 1
For intRi = 0 To intNRi - 1
For intCi = 0 To intNCi - 1
vItem = varItem(intCi, intRi)
Use vItem to dynamically create a new recordset to set up the weekly buckets
strSelect = "Select * FROM qInventoryTotals2 WHERE qInventoryTotals2.ItemNumber = [this_one]"
Set qdf = db.CreateQueryDef(vbNullString, strSelect)
qdf.Parameters("this_one").Value = vItem
Set inv = qdf.OpenRecordset
Count the records, create a second array
Dim invNum As Integer
invNum = inv.RecordCount
Dim varRec As Variant
Dim intR As Integer
Dim intC As Integer
Dim intNC As Integer
Dim intRC As Integer
Dim cItem As String
Dim cRequired As Date
Dim cPO As Double
Dim cBC As Double
Dim cOnHand As Double
varRec = inv.GetRows(invNum)
intNR = UBound(varRec, 2) + 1
intNC = UBound(varRec, 1) + 1
For intR = 0 To intNR - 1
For intC = 0 To intNC - 1
cItem = varRec(0, intR)
cRequired = varRec(1, intR)
cOnHand = varRec(2, intR)
cPO = varRec(3, intR)
cBC = varRec(4, intR)
cSO = varRec(5, intR)
cPD = varRec(6, intR)
cIN = varRec(7, intR)
cJT = varRec(8, intR)
cWO = varRec(9, intR)
'------------- finish getting inventory columns --------------------
Next intC
And then set up the buckets for each week
If cRequired < Week1 Then
recOut.AddNew
recOut.Fields("ItemNumber") = cItem
recOut.Fields("tB") = cOnHand
recOut.Fields("tPO") = cPO
recOut.Fields("tBC") = cBC
recOut.Fields("tSO") = cSO
recOut.Fields("tPD") = cPD
recOut.Fields("tIN") = cIN
recOut.Fields("tJT") = cJT
recOut.Fields("tWO") = cWO
recOut.Fields("tE") = cOnHand + cPO + cBC + cSO + cPD + cIN + cJT + cWO
recOut.Fields("RequiredDate") = cRequired
recOut.Fields("GroupDate") = Week1
recOut.Update
' tE0 = cOnHand + cPO + cBC + cSO + cPD + cIN + cJT + cWO
Dim tryme As Double
tryme = DLookup("teMe", "qBuckets", "GroupDate = Week1")
tE0 = tryme
End If
I need help; I have created a table that stores the premium based on the following criteria:
Table_Policy_Premium
1. Category: A
2. Min Age: 0
3. Max Age: 20
4. Premium: USD1000
The same category may have other ranges of age, min 21 max 64, and premium in the next field.
I have another table namely employee_data, where there is a premium field; I want that to be updated from a TPP table based on the following criteria against each employee.
If Table_Policy_Premium - Category Matches with Category in employee_data, and If the age in employee_data is between 0-20, update the premium field in Employee_Table from Table_Policy_Premium with the appropriate premium.
Simply create public function in module that you will call in query (or on form), that will return premium value for given category and age.
Public Function getPremium(strCat As String, intAge as Integer) As Double
'variables
Dim strQuery As String
Dim rstQuery As dao.Recordset
Dim dbs As dao.Database
'set database
Set dbs = CurrentDb
'prepare query
strQuery = "SELECT Premium " & _
"FROM Table_Policy_Premium " & _
"WHERE Category = '" & strCat & "' AND " & _
"[Min Age] <= " & intAge & " AND " & _
"[Max Age] >= " & intAge & ";"
'open recordset
Set rstQuery = dbs.OpenRecordset(strQuery)
'check if there is a record
If rstQuery.EOF Then
'no record - set premium to 0
getPremium = 0
Else
'record found
getPremium = rstQuery!Premium
End If
Set rstQuery = Nothing
Set dbs = Nothing
End Function
Simply call function by inserting this as field in query: getPremium([category],[age])
I'm newbie to MS Access.
How to display multiple records in a single row as comma separated in MS Access?
For example I have a table
state
AL
AK
MA
HI
TX
VI
GU
I want to retrive data as like below
State
AL, AK, MA, HI, TX, VI, GU
Can anyone help me with a query to do this?
You can write the following "Crosstab" query so the will become fields:
TRANSFORM First(table1.state) AS FirstOfstate
SELECT "State" AS Expr1
FROM table1
GROUP BY "State"
PIVOT table1.state;
or in GUI:
One option is to create a function that will return the concatenated list of values:
Public Function ConcatenateField( _
ByVal Source As String, _
ByVal Field As String, _
Optional ByVal Separator As String = ";") _
As String
' 2011-04-17. Gustav Brock, Cactus Data ApS, CPH.
Dim rs As DAO.Recordset
Dim Rows As Variant
Dim Fields() As Variant
Dim Sql As String
Dim Item As Integer
Dim ItemList As String
' Retrieve one field from table or query.
Sql = "Select [" & Field & "] From [" & Source & "] Order By 1 Asc"
Set rs = CurrentDb.OpenRecordset(Sql, dbOpenSnapshot)
If rs.RecordCount > 0 Then
' Fill array.
Rows = rs.GetRows(rs.RecordCount)
' Convert array to one dimension.
ReDim Fields(UBound(Rows, 2))
For Item = LBound(Rows, 2) To UBound(Rows, 2)
Fields(Item) = Rows(0, Item)
Next
' Create concatenated value list.
ItemList = Join(Fields(), Separator)
End If
rs.Close
Set rs = Nothing
ConcatenateField = ItemList
End Function
Then use this expression:
=ConcatenateField("YourTableName", "State", ", ")
Or in a query:
Select Top 1
ConcatenateField("YourTableName", "State", ", ") As States
From
YourTableName
Ok so a guy at work has a little access database he uses to keep track of things. He has this form that he uses that already queries what he needs and produces the results on a form and that is really all he needs.
One thing is that he has duplicates for every record that comes up with a different "Type" as a field "indentifier" (what I call it)...here is an example:
ID Name Price Type
1 Prodcut A $10 A1
1 Product A $10 A2
1 Product A $10 A3
2 Product B $12 A1
etc
naturally this is supposed to occur and he wants to see all the types but given it ends up being a mile long, he asked me if there was a way to concatenate the "types" so the following would be displayed:
ID Name Price Type
1 Prodcut A $10 A1, A2, A3
1 Product B $12 A1, A2, A3
1 Product C $14 A1, A2, A3
2 Product D $7 A1, A2, A3
...on the form. Can anyone please help me with this? Thanks!
OK, i found a function created in the VBA, which can be used in the query to retrieve the data for the form.
function is
Public Function ConcatRelated(strField As String, _
strTable As String, _
Optional strWhere As String, _
Optional strOrderBy As String, _
Optional strSeparator = ", ") As Variant
On Error GoTo Err_Handler
'Purpose: Generate a concatenated string of related records.
'Return: String variant, or Null if no matches.
'Arguments: strField = name of field to get results from and concatenate.
' strTable = name of a table or query.
' strWhere = WHERE clause to choose the right values.
' strOrderBy = ORDER BY clause, for sorting the values.
' strSeparator = characters to use between the concatenated values.
'Notes: 1. Use square brackets around field/table names with spaces or odd characters.
' 2. strField can be a Multi-valued field (A2007 and later), but strOrderBy cannot.
' 3. Nulls are omitted, zero-length strings (ZLSs) are returned as ZLSs.
' 4. Returning more than 255 characters to a recordset triggers this Access bug:
' http://allenbrowne.com/bug-16.html
Dim rs As DAO.Recordset 'Related records
Dim rsMV As DAO.Recordset 'Multi-valued field recordset
Dim strSql As String 'SQL statement
Dim strOut As String 'Output string to concatenate to.
Dim lngLen As Long 'Length of string.
Dim bIsMultiValue As Boolean 'Flag if strField is a multi-valued field.
'Initialize to Null
ConcatRelated = Null
'Build SQL string, and get the records.
strSql = "SELECT " & strField & " FROM " & strTable
If strWhere <> vbNullString Then
strSql = strSql & " WHERE " & strWhere
End If
If strOrderBy <> vbNullString Then
strSql = strSql & " ORDER BY " & strOrderBy
End If
Set rs = DBEngine(0)(0).OpenRecordset(strSql, dbOpenDynaset)
'Determine if the requested field is multi-valued (Type is above 100.)
bIsMultiValue = (rs(0).Type > 100)
'Loop through the matching records
Do While Not rs.EOF
If bIsMultiValue Then
'For multi-valued field, loop through the values
Set rsMV = rs(0).Value
Do While Not rsMV.EOF
If Not IsNull(rsMV(0)) Then
strOut = strOut & rsMV(0) & strSeparator
End If
rsMV.MoveNext
Loop
Set rsMV = Nothing
ElseIf Not IsNull(rs(0)) Then
strOut = strOut & rs(0) & strSeparator
End If
rs.MoveNext
Loop
rs.Close
'Return the string without the trailing separator.
lngLen = Len(strOut) - Len(strSeparator)
If lngLen > 0 Then
ConcatRelated = Left(strOut, lngLen)
End If
Exit_Handler:
'Clean up
Set rsMV = Nothing
Set rs = Nothing
Exit Function
Err_Handler:
MsgBox "Error " & Err.Number & ": " & Err.Description, vbExclamation, "ConcatRelated()"
Resume Exit_Handler
End Function
and is used in the query as
SELECT Table1.ID, Table1.ProductName, Table1.ProductPrice, ConcatRelated("Type","Table1","ID = " & [Table1]![ID] & " AND ProductName = """ & [Table1]![ProductName] & """ AND ProductPrice = " & [Table1]![ProductPrice]) AS Expr1
FROM Table1
GROUP BY Table1.ID, Table1.ProductName, Table1.ProductPrice, ConcatRelated("Type","Table1","ID = " & [Table1]![ID] & " AND ProductName = """ & [Table1]![ProductName] & """ AND ProductPrice = " & [Table1]![ProductPrice]);
I found an example (here) that seems to be exactly what you are looking for:
Concatenate Column Values from Multiple Rows into a Single Column with Access
From the above link:
The Problem
Coming up with a meaningful title for
this article was the hardest part. The
issue is one that I have seen a couple
times in the Access newsgroups, but it
is hard to describe without a specific
example. One post to
comp.databases.ms-access some years
ago put it this way:
I would like to combine a field's values from multiple records in a single field. For example:
Last First Code
------- --------- ----
Lesand Danny 1
Lesand Danny 2
Lesand Danny 3
Benedi Eric 7
Benedi Eric 14
Result should look like:
Last First Codes
------- --------- -----
Lesand Danny 1,2,3
Benedi Eric 7,14
Something on these lines may suit, but concatenating is usually not a good idea:
Function ConcatList(strSQL As String, strDelim, _
ParamArray NameList() As Variant)
''Reference: Microsoft DAO x.x Object Library
Dim db As Database
Dim rs As DAO.Recordset
Dim strList As String
Set db = CurrentDb
If strSQL <> "" Then
Set rs = db.OpenRecordset(strSQL)
Do While Not rs.EOF
strList = strList & strDelim & rs.Fields(0)
rs.MoveNext
Loop
strList = Mid(strList, Len(strDelim) + 1)
Else
strList = Join(NameList, strDelim)
End If
ConcatList = strList
End Function
FROM: http://wiki.lessthandot.com/index.php/Concatenate_a_List_into_a_Single_Field_(Column)
Why don't you try the "crosstab query" solution?