Macro to generate random serial number

dudumomo

Board Regular
Joined
Jun 3, 2014
Messages
65
Hi guys,

I need to generate some serial number to provide to our students and I'm looking for a macro to do it.

Ideally, I enter in cell A1 the number of key code to generate and it will generate those in the following format:
3XA7-BC6D-98EF
Can mix letter (Capital only) and number.
But should exclude 0 and O, 5 and S, and 1 and I
(Easier for them)

And cherry on the top, it keeps all the previously generated serial number to make sure it will not generate twice the same.

Do you have some idea to do that?

Thanks!
 

Excel Facts

How can you automate Excel?
Press Alt+F11 from Windows Excel to open the Visual Basic for Applications (VBA) editor.
Hi dudemomo,

You could try the code below. It will write the unique serial numbers beginning at Cell A3.
There's an optional statement if you want to clear any existing serial numbers in that range.

Before using it, add a reference to Microsoft Scripting Runtime to your VBA Project
(in the VBA Editor: Tools > References... > scroll down and check the box next to Microsoft Scripting Runtime ") > OK)"

Code:
Sub MakeSerialNbrs()
'--generates a user specfied number of unique serial numbers
'  serial numbers meet criteria in function: sGenerateSerialNbr
'  eg: 3XA7-BC6D-98EF with excluded characters
 Dim bUniqueCodeFound
 Dim lNdx As Long, lSerialNbrsToMake As Long
 Dim rResults As Range
 Dim dctSerialNbrs As Scripting.Dictionary
 Dim sSerialNbr As String
 Dim wks As Worksheet
  
 Set dctSerialNbrs = New Scripting.Dictionary
 Set wks = ActiveSheet
 
 On Error GoTo ExitProc
 With wks
   lSerialNbrsToMake = .Range("A1").Value
   '--location to write results
   Set rResults = .Range("A3")
   '--optional: clear previous results
   '  Range(rResults, .Cells(.Rows.Count, rResults.Column)).ClearContents
 End With

'--generate specified count of serial numbers.
'  use dictionary to test each number is unique
 For lNdx = 1 To lSerialNbrsToMake
   bUniqueCodeFound = False
   Do Until bUniqueCodeFound
      '--call function to get string meeting criteria
      sSerialNbr = sGenerateSerialNbr()
      If Not dctSerialNbrs.Exists(sSerialNbr) Then
         dctSerialNbrs.Add sSerialNbr, lNdx
         bUniqueCodeFound = True
      End If
   Loop
 Next lNdx
   
'--write results
 If dctSerialNbrs.Count > 0 Then
   rResults.Resize(dctSerialNbrs.Count).Value _
     = Application.Transpose(dctSerialNbrs.Keys)
 End If
 
ExitProc:
 If Err.Number <> 0 Then
   Set dctSerialNbrs = Nothing
   MsgBox Err.Number & "-" & Err.Description
 End If
End Sub

Private Function sGenerateSerialNbr() As String
'--generates a random string meeting the specified
'    pattern and character exclusions
'
   Dim bValidCodeFound As Boolean
   Dim lCode As Long, lPos As Long
   Dim sSerialNbr As String
   Const sPattern As String = "xxxx-xxxx-xxxx"
      
   For lPos = 1 To Len(sPattern)
      If Mid$(sPattern, lPos, 1) = "-" Then
         sSerialNbr = sSerialNbr & "-"
      Else
         bValidCodeFound = False 'reset
         Do Until bValidCodeFound
            '--get random nbr representing char code from "0" and "Z"
            lCode = Application.RandBetween(48, 90)
            
            '--test if lCode represents excluded char
            Select Case lCode
               Case 58, 59, 60, 61, 62, 63, 64 'exclude ":" to "@"
               Case 48, 79 'exclude 0,O
               Case 49, 73 'exclude 1,I
               Case 53, 83 'exclude 5,S
               Case Else:
                  bValidCodeFound = True
            End Select
         Loop
         sSerialNbr = sSerialNbr & Chr$(lCode)
      End If
   Next lPos
   
   sGenerateSerialNbr = sSerialNbr
End Function
 
Upvote 0
Wow it works!
Thanks Jerry,

However, how to ensure that the script will not generate serial numbers that has been generated already in the past?
It will use the dictionary to check each entry is unique, but within current scope to generate.

May be it could store all the generated serial numbers in sheet2 to keep record? and only display new generated one in cells A3.
Sounds reasonable?

How to do that?

Thank you!

PS: Ideally, it should also delete the entry in A3.
If for example I generate 10, then again 2, it will keep the last 8 ones, generated previously.
 
Last edited:
Upvote 0
PS: Ideally, it should also delete the entry in A3.
If for example I generate 10, then again 2, it will keep the last 8 ones, generated previously.

I think I understand what you want, except the this last part confused me. Did you mean...
PS: Ideally, it should also delete the entry in A1.
If for example I generate 8, then again 2, it will keep the last 8 ones, generated previously.

Please let me know if I misinterpreted.

Replace all the previous code with this...
Code:
Sub MakeSerialNbrs()
'--generates a user specfied number of unique serial numbers
'  serial numbers meet criteria in function: sGenerateSerialNbr
'    eg: 3XA7-BC6D-98EF with excluded characters
'  newly generated serial nbrs are displayed on wksNewNbrs
'    and also appended to list of existing serial nbrs
'
 Dim bUniqueCodeFound
 Dim lNdx As Long, lSerialNbrsToMake As Long
 Dim lLastRowExisting As Long, lLoopCount As Long
 Dim rNewResults As Range
 Dim dctSerialNbrs As Scripting.Dictionary
 Dim sSerialNbr As String
 Dim vExisting As Variant, vNewSerialNbrs As Variant
 Dim wksNewNbrs As Worksheet, wksExistNbrs As Worksheet
  
 On Error GoTo ExitProc
  
 Set dctSerialNbrs = New Scripting.Dictionary
 Set wksNewNbrs = Sheets("Sheet1")
 Set wksExistNbrs = Sheets("Sheet2")

 With wksNewNbrs
   lSerialNbrsToMake = .Range("A1").Value
   '--optional: clear value from A1
   .Range("A1").ClearContents
   '--location to write results
   Set rNewResults = .Range("A3")
   '--clear previous results
   Range(rNewResults, .Cells(.Rows.Count, rNewResults.Column)).ClearContents
   '--size 2d array to hold new results
   ReDim vNewSerialNbrs(1 To lSerialNbrsToMake, 1 To 1)
 End With

'--read existing serial numbers into dictionary
'  also validate there are no duplicates in existing nbrs
 With wksExistNbrs
   lLastRowExisting = .Cells(.Rows.Count, "A").End(xlUp).Row
   vExisting = .Range("A1:A" & lLastRowExisting).Value
   '--handle 0-1 existing items
   If Not IsArray(vExisting) Then vExisting = Array(vExisting)
   For lNdx = 1 To UBound(vExisting, 1)
      sSerialNbr = vExisting(lNdx, 1)
      If Len(sSerialNbr) <> 0 Then
         If Not dctSerialNbrs.Exists(sSerialNbr) Then
            dctSerialNbrs.Add sSerialNbr, lNdx
         Else
            MsgBox "Duplicate found in existing Serial Numbers:" _
               & sSerialNbr
            GoTo ExitProc
         End If
      End If
   Next
 End With

'--generate specified count of new serial numbers.
'  use dictionary to test each number is unique
 For lNdx = 1 To lSerialNbrsToMake
   bUniqueCodeFound = False
   '--prevent endless loop
   lLoopCount = 0
   Do Until bUniqueCodeFound Or lLoopCount > 10
      '--call function to get string meeting criteria
      sSerialNbr = sGenerateSerialNbr()
      If Not dctSerialNbrs.Exists(sSerialNbr) Then
         dctSerialNbrs.Add sSerialNbr, lNdx
         vNewSerialNbrs(lNdx, 1) = sSerialNbr
         bUniqueCodeFound = True
      End If
      lLoopCount = lLoopCount + 1
   Loop
 Next lNdx
   
'--write results to new range and append to existing list
 If lSerialNbrsToMake > 0 Then
   rNewResults.Resize(lSerialNbrsToMake).Value _
      = vNewSerialNbrs
   wksExistNbrs.Cells(lLastRowExisting + 1, "A") _
      .Resize(lSerialNbrsToMake).Value _
      = vNewSerialNbrs
 End If
 
ExitProc:
 If Err.Number <> 0 Then
   Set dctSerialNbrs = Nothing
   MsgBox Err.Number & "-" & Err.Description
 End If
End Sub

Private Function sGenerateSerialNbr() As String
'--generates a random string meeting the specified
'    pattern and character exclusions
'
   Dim bValidCodeFound As Boolean
   Dim lCode As Long, lPos As Long
   Dim sSerialNbr As String
   Const sPattern As String = "xxxx-xxxx-xxxx"
      
   For lPos = 1 To Len(sPattern)
      If Mid$(sPattern, lPos, 1) = "-" Then
         sSerialNbr = sSerialNbr & "-"
      Else
         bValidCodeFound = False 'reset
         Do Until bValidCodeFound
            '--get random nbr representing char code from "0" and "Z"
            lCode = Application.RandBetween(48, 90)
            
            '--test if lCode represents excluded char
            Select Case lCode
               Case 58, 59, 60, 61, 62, 63, 64 'exclude ":" to "@"
               Case 48, 79 'exclude 0,O
               Case 49, 73 'exclude 1,I
               Case 53, 83 'exclude 5,S
               Case Else:
                  bValidCodeFound = True
            End Select
         Loop
         sSerialNbr = sSerialNbr & Chr$(lCode)
      End If
   Next lPos
   
   sGenerateSerialNbr = sSerialNbr
End Function

For the serial number criteria you described the chances of generating duplicate serial number for your students is relatively remote.
There are about 2.0E+13 possible combinations. If you issue 1,000 serial numbers to your students, the odds of having any duplicates is about 1:40 Million :eek:

Nonetheless, it's better to eliminate that possibility. I also tried to write the code so it could be adapted for other patterns and exclusions.
The check for duplicates would definitely be needed for a pattern with less combinations like "xxx-x"
 
Upvote 0
Ah!
It's exactly that.
Sorry I mistyped my previous post. But you understood well!

Works perfect!
 
Upvote 0
I need something similar to this to produce codes for use in testing software. The code should be formatted as such: LLNNNNNNL (no spaces). Where L is letter, and N is number. However, the first two letters must not be D, F, I, Q, U or V - and the second letter cannot be O. Exclude altogether the prefixes BG, GB, NK, KN, TN, NT and ZZ. The letter at the end of the code (suffix letter) is can only be A, B, C or D.

It looks like a modification of you VB script would be able to produce this. Thanks.
 
Upvote 0
Here's a modified version. Paste all this code into the same Standard Code Module.

Before using it, add a reference to Microsoft Scripting Runtime to your VBA Project
(in the VBA Editor: Tools > References... > scroll down and check the box next to Microsoft Scripting Runtime ") > OK)"

The code assumes you have two sheets named "Sheet1" and "Sheet2".
Place the number of Test Codes you want generated in Sheet1 Cell A1, then run the macro "MakeSerialNbrs"

Code:
Sub MakeSerialNbrs()
'--generates a user specfied number of unique serial numbers
'  serial numbers meet criteria in function: sGenerateSerialNbr
'    eg: 3XA7-BC6D-98EF with excluded characters
'  newly generated serial nbrs are displayed on wksNewNbrs
'    and also appended to list of existing serial nbrs
'
 Dim bUniqueCodeFound
 Dim lNdx As Long, lSerialNbrsToMake As Long
 Dim lLastRowExisting As Long, lLoopCount As Long
 Dim rNewResults As Range
 Dim dctSerialNbrs As Scripting.Dictionary
 Dim sSerialNbr As String
 Dim vExisting As Variant, vNewSerialNbrs As Variant
 Dim wksNewNbrs As Worksheet, wksExistNbrs As Worksheet
  
 On Error GoTo ExitProc
  
 Set dctSerialNbrs = New Scripting.Dictionary
 Set wksNewNbrs = Sheets("Sheet1")
 Set wksExistNbrs = Sheets("Sheet2")

 With wksNewNbrs
   lSerialNbrsToMake = .Range("A1").Value
   '--validate input
   If lSerialNbrsToMake < 1 Then
      MsgBox "Value in Cell A1 must be greater than 0"
      GoTo ExitProc
   End If
   '--optional: clear value from A1
   .Range("A1").ClearContents
   '--location to write results
   Set rNewResults = .Range("A3")
   '--clear previous results
   Range(rNewResults, .Cells(.Rows.Count, rNewResults.Column)).ClearContents
   '--size 2d array to hold new results
   ReDim vNewSerialNbrs(1 To lSerialNbrsToMake, 1 To 1)
 End With

'--read existing serial numbers into dictionary
'  also validate there are no duplicates in existing nbrs
 With wksExistNbrs
   lLastRowExisting = .Cells(.Rows.Count, "A").End(xlUp).Row
   vExisting = .Range("A1:A" & lLastRowExisting).Value
   '--handle 0-1 existing items
   If Not IsArray(vExisting) Then vExisting = Array(vExisting)
   For lNdx = 1 To UBound(vExisting, 1)
      sSerialNbr = vExisting(lNdx, 1)
      If Len(sSerialNbr) <> 0 Then
         If Not dctSerialNbrs.Exists(sSerialNbr) Then
            dctSerialNbrs.Add sSerialNbr, lNdx
         Else
            MsgBox "Duplicate found in existing Serial Numbers:" _
               & sSerialNbr
            GoTo ExitProc
         End If
      End If
   Next
 End With

'--generate specified count of new serial numbers.
'  use dictionary to test each number is unique
 For lNdx = 1 To lSerialNbrsToMake
   bUniqueCodeFound = False
   '--prevent endless loop
   lLoopCount = 0
   Do Until bUniqueCodeFound Or lLoopCount > 10
      '--call function to get string meeting criteria
      sSerialNbr = sGenerateSerialNbr()
      If Not dctSerialNbrs.Exists(sSerialNbr) Then
         dctSerialNbrs.Add sSerialNbr, lNdx
         vNewSerialNbrs(lNdx, 1) = sSerialNbr
         bUniqueCodeFound = True
      End If
      lLoopCount = lLoopCount + 1
   Loop
 Next lNdx
   
'--write results to new range and append to existing list
 If lSerialNbrsToMake > 0 Then
   rNewResults.Resize(lSerialNbrsToMake).Value _
      = vNewSerialNbrs
   wksExistNbrs.Cells(lLastRowExisting + 1, "A") _
      .Resize(lSerialNbrsToMake).Value _
      = vNewSerialNbrs
 End If
 
ExitProc:
 If Err.Number <> 0 Then
   Set dctSerialNbrs = Nothing
   MsgBox Err.Number & "-" & Err.Description
 End If
End Sub

Private Function sGenerateSerialNbr() As String
'--this calls sGenerateSerialNbrTry to get random strings
'    in which each character meets requirements for that position.
'  this function further tests for excluded combinations of characters
'  strings returned from this function meet all requirements for
'    a valid serial number. Testing for duplicate serial numbers
'    is performed by the calling procedure.

 Dim bValidStringFound As Boolean
 Dim lLoopCount As Long
 Dim sReturn As String, sTry As String, sPrefix As String
 Dim vExcludePrefixes As Variant
 
 '--these prefixes are not allowed.
 vExcludePrefixes = Split("BG,GB,NK,KN,TN,NT,ZZ", ",")
 
 Do Until bValidStringFound Or lLoopCount > 100
   '--get string that meets requirements for each separate char
   sTry = sGenerateSerialNbrTry()
   '--test if try meets requirements for entire string
   sPrefix = Mid(sTry, 1, 2)
   
   '--test if prefix is in excluded array
   If IsError(Application.Match(sPrefix, vExcludePrefixes, 0)) Then
      '--prefix is not in excluded list
      bValidStringFound = True
      sReturn = sTry
   End If
   '--loop count prevents endless loop
   lLoopCount = lLoopCount + 1
 Loop
 
 sGenerateSerialNbr = sReturn
End Function

Private Function sGenerateSerialNbrTry() As String
'--generates a random string in which each character meets
'    the requirements for that position in the string.
'  excluded combinations of characters are tested by the
'    calling procedure.
'
   Dim bValidCharFound As Boolean
   Dim lCode As Long, lPos As Long
   Dim sReturn As String, sCharType As String
   Dim sExclude As String, sChar As String
   Dim vCode As Variant
   
   Const sPattern As String = "ABNNNNNNC"
           
   For lPos = 1 To Len(sPattern)
      sCharType = Mid$(sPattern, lPos, 1)
      Select Case sCharType
         Case "N"
            '--call returns return random character between the range of input
            '     character codes excluding characters in optional sExclude arg
            sChar = sGetRandChar(iFrom:=Asc("0"), iTo:=Asc("9"))
         Case "A"
            sChar = sGetRandChar(iFrom:=Asc("A"), iTo:=Asc("Z"), sExclude:="DFIQUV")
         Case "B"
            sChar = sGetRandChar(iFrom:=Asc("A"), iTo:=Asc("Z"), sExclude:="DFIQUVO")
         Case "C"
            sChar = sGetRandChar(iFrom:=Asc("A"), iTo:=Asc("D"))
         Case Else
            '--should not have this case since all character codes in pattern
            '    should be covered in one of the cases above
            MsgBox "Error in Generate Serial Number Try function"
            sReturn = vbNullString
            GoTo ExitProc
      End Select
      If Len(sChar) > 0 Then
         sReturn = sReturn & sChar
      Else
         '--char not found
         sReturn = vbNullString
         GoTo ExitProc
      End If
   Next lPos
   
ExitProc:
   sGenerateSerialNbrTry = sReturn
End Function

Private Function sGetRandChar(ByVal iFrom As Integer, _
   ByVal iTo As Integer, Optional ByVal sExclude As String = "") As String
'--attempts to return random character between the range of input
'     character codes, excluding characters within sExclude string
'  if successful, returns character string, else returns null string.

 Dim bValidCodeFound As Boolean
 Dim lLoopCount As Long, lCode As Long
 Dim sChar As String, sReturn As String
 
 If iFrom > iTo Then GoTo ExitProc

 Do Until bValidCodeFound Or lLoopCount > 1000
   '--get random nbr representing char code with range specified
   lCode = Application.RandBetween(iFrom, iTo)
   sChar = Chr$(lCode)
   '--test if char is an excluded character
   If InStr(1, sExclude, sChar, vbBinaryCompare) = 0 Then
      '--code is not in excluded chars string
      bValidCodeFound = True
      sReturn = sChar
   End If
   '--loop count prevents endless loop
   lLoopCount = lLoopCount + 1
 Loop
   
ExitProc:
 sGetRandChar = sReturn
End Function
 
Upvote 0
Thanks a lot, Jerry

It works as it should. I may have to do some modification. I found out there are many more excluded combinations.
 
Upvote 0

Forum statistics

Threads
1,221,310
Messages
6,159,176
Members
451,543
Latest member
cesymcox

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top