How do I extract a numeric value from a field in Access 2007?

Posted on 2010-09-10
Last Modified: 2013-11-27
OOps.  I closed the following question too early:
"I need to run a query and extract a numeric value from a field in a transactions table using Access 2007. The value that I need is part of a larger string of alpha-numeric characters. The new query results, therefore, will contain the extracted value that I will use to look up other values in other tables.
 Here are the details:
1)  The value in the source field is variable length and alphanumeric in the form of ABC123, ABC12, ABC1, AB123, AB12, or AB1.
2) I need to extract ALL the numeric characters from right to left.  In the examples above, for example, the query returns 123, 12, 1, 123, 12, 1, respectively."

I used SQL code provided by experts that Access translated from SQL as follows::

SELECT tblSales.Office, Val(StrReverse(CStr(Val(StrReverse([Office]))))) AS OfficeNo
FROM tblSales;
The results looked good until I noticed that the final zeros were being truncated.  For example, ABC30 became "3" instead of "30" and AB10 become "1" instead of "10".
Can someone help me refine this code to not drop the ending zero?  Can the numeric value be formatted as Text instead of a number?

Question by:thutchinson
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 4
  • 3
  • +2

Author Comment

ID: 33646944
The SQL code that I used came from hnasr (with help from MathewsPatrick) as
SELECT txtFld, Val(StrReverse(CStr(Val(StrReverse(txtFld)))))
Substituting my field and table names I entered:
SELECT Office, Val(StrReverse(CStr(Val(StrReverse(Office)))))
FROM tblSales;

Ref: ID # 26463615
LVL 120

Accepted Solution

Rey Obrero (Capricorn1) earned 500 total points
ID: 33646952
place this  function in a regular module (module 1)

Function getNumeric(s as string)
if len(s & "")=0 then exit function

dim j, nStr as string, vNum

for j=1 to len(s)
       if instr(nStr,mid(s,j,1))>0 then
          vnum=vnum & mid(s,j,1)
      end if
end function

to use in your query

SELECT tblSales.Office,getNumeric([Office]) AS OfficeNo
FROM tblSales;
LVL 92

Expert Comment

by:Patrick Matthews
ID: 33646989

If you try the RegExp-powered approach I described in your previous question, you will find that it works.  Repeating it here...


This may be a good slot for Regular Expressions.  Please see my article:

From that article, add the RegExpFind function to a regular VBA module (source code below).

You can then use it in a query like this:

SELECT OriginalColumn, RegExpFind(OriginalColumn, "\d+", 1) AS DigitsOnly
FROM SomeTable

Please see the attached file for an example.
Function RegExpFind(LookIn As String, PatternStr As String, Optional Pos, _
    Optional MatchCase As Boolean = True, Optional ReturnType As Long = 0, _
    Optional MultiLine As Boolean = False)
    ' Function written by Patrick G. Matthews.  You may use and distribute this code freely,
    ' as long as you properly credit and attribute authorship and the URL of where you
    ' found the code
    ' For more info, please see:
    ' This function relies on the VBScript version of Regular Expressions, and thus some of
    ' the functionality available in Perl and/or .Net may not be available.  The full extent
    ' of what functionality will be available on any given computer is based on which version
    ' of the VBScript runtime is installed on that computer
    ' This function uses Regular Expressions to parse a string (LookIn), and return matches to a
    ' pattern (PatternStr).  Use Pos to indicate which match you want:
    ' Pos omitted               : function returns a zero-based array of all matches
    ' Pos = 1                   : the first match
    ' Pos = 2                   : the second match
    ' Pos = <positive integer>  : the Nth match
    ' Pos = 0                   : the last match
    ' Pos = -1                  : the last match
    ' Pos = -2                  : the 2nd to last match
    ' Pos = <negative integer>  : the Nth to last match
    ' If Pos is non-numeric, or if the absolute value of Pos is greater than the number of
    ' matches, the function returns an empty string.  If no match is found, the function returns
    ' an empty string.  (Earlier versions of this code used zero for the last match; this is
    ' retained for backward compatibility)
    ' If MatchCase is omitted or True (default for RegExp) then the Pattern must match case (and
    ' thus you may have to use [a-zA-Z] instead of just [a-z] or [A-Z]).
    ' ReturnType indicates what information you want to return:
    ' ReturnType = 0            : the matched values
    ' ReturnType = 1            : the starting character positions for the matched values
    ' ReturnType = 2            : the lengths of the matched values
    ' If you use this function in Excel, you can use range references for any of the arguments.
    ' If you use this in Excel and return the full array, make sure to set up the formula as an
    ' array formula.  If you need the array formula to go down a column, use TRANSPOSE()
    ' Note: RegExp counts the character positions for the Match.FirstIndex property as starting
    ' at zero.  Since VB6 and VBA has strings starting at position 1, I have added one to make
    ' the character positions conform to VBA/VB6 expectations
    ' Normally as an object variable I would set the RegX variable to Nothing; however, in cases
    ' where a large number of calls to this function are made, making RegX a static variable that
    ' preserves its state in between calls significantly improves performance
    Static RegX As Object
    Dim TheMatches As Object
    Dim Answer()
    Dim Counter As Long
    ' Evaluate Pos.  If it is there, it must be numeric and converted to Long
    If Not IsMissing(Pos) Then
        If Not IsNumeric(Pos) Then
            RegExpFind = ""
            Exit Function
            Pos = CLng(Pos)
        End If
    End If
    ' Evaluate ReturnType
    If ReturnType < 0 Or ReturnType > 2 Then
        RegExpFind = ""
        Exit Function
    End If
    ' Create instance of RegExp object if needed, and set properties
    If RegX Is Nothing Then Set RegX = CreateObject("VBScript.RegExp")
    With RegX
        .Pattern = PatternStr
        .Global = True
        .IgnoreCase = Not MatchCase
        .MultiLine = MultiLine
    End With
    ' Test to see if there are any matches
    If RegX.Test(LookIn) Then
        ' Run RegExp to get the matches, which are returned as a zero-based collection
        Set TheMatches = RegX.Execute(LookIn)
        ' Test to see if Pos is negative, which indicates the user wants the Nth to last
        ' match.  If it is, then based on the number of matches convert Pos to a positive
        ' number, or zero for the last match
        If Not IsMissing(Pos) Then
            If Pos < 0 Then
                If Pos = -1 Then
                    Pos = 0
                    ' If Abs(Pos) > number of matches, then the Nth to last match does not
                    ' exist.  Return a zero-length string
                    If Abs(Pos) <= TheMatches.Count Then
                        Pos = TheMatches.Count + Pos + 1
                        RegExpFind = ""
                        GoTo Cleanup
                    End If
                End If
            End If
        End If
        ' If Pos is missing, user wants array of all matches.  Build it and assign it as the
        ' function's return value
        If IsMissing(Pos) Then
            ReDim Answer(0 To TheMatches.Count - 1)
            For Counter = 0 To UBound(Answer)
                Select Case ReturnType
                    Case 0: Answer(Counter) = TheMatches(Counter)
                    Case 1: Answer(Counter) = TheMatches(Counter).FirstIndex + 1
                    Case 2: Answer(Counter) = TheMatches(Counter).Length
                End Select
            RegExpFind = Answer
        ' User wanted the Nth match (or last match, if Pos = 0).  Get the Nth value, if possible
            Select Case Pos
                Case 0                          ' Last match
                    Select Case ReturnType
                        Case 0: RegExpFind = TheMatches(TheMatches.Count - 1)
                        Case 1: RegExpFind = TheMatches(TheMatches.Count - 1).FirstIndex + 1
                        Case 2: RegExpFind = TheMatches(TheMatches.Count - 1).Length
                    End Select
                Case 1 To TheMatches.Count      ' Nth match
                    Select Case ReturnType
                        Case 0: RegExpFind = TheMatches(Pos - 1)
                        Case 1: RegExpFind = TheMatches(Pos - 1).FirstIndex + 1
                        Case 2: RegExpFind = TheMatches(Pos - 1).Length
                    End Select
                Case Else                       ' Invalid item number
                    RegExpFind = ""
            End Select
        End If
    ' If there are no matches, return empty string
        RegExpFind = ""
    End If
    ' Release object variables
    Set TheMatches = Nothing
End Function

Open in new window

Business Impact of IT Communications

What are the business impacts of how well businesses communicate during an IT incident? Targeting, speed, and transparency all matter. Find out more in this infographic.


Author Comment

ID: 33647403
That's a beautiful thing!  Seems to work great.  I will test today before I close this ticket just in case I need followup.  Thanks again.

Author Comment

ID: 33647420
Your solution probably works great however it may be a little over my head.  Forgive me for trying the simpler suggestions first.  Thanks for your help. For now, Capricorn's code seems to be working.
LVL 92

Expert Comment

by:Patrick Matthews
ID: 33647526

No worries.  Just a couple of words of caution:

1) The RegExp function is extremely flexible :)  While the simpler function hit the spot here, once things get complicated, it can really be a godsend

2) If you ever have an entry like A1B2, my function will return "1" while cap1's returns "12".  I have no idea if that is a problem or not.


LVL 44

Expert Comment

ID: 33648398
An extension of your original logig

v = "AB120"
? Mid(v,instr(v,strreverse(val(strreverse(v)))))
LVL 44

Expert Comment

ID: 33648403
logig should be logic ;-)
LVL 30

Expert Comment

ID: 33654618
Try the expression as:

Int(Val(StrReverse(CStr(Val(StrReverse(strFld & "9")))))/10)

Author Comment

ID: 33661351
For clarification, if I use the hnasr code on the latest post then I don't need the Function proposed by Capricorn?
LVL 120

Expert Comment

by:Rey Obrero (Capricorn1)
ID: 33661426
hnasr code, does not have anything to do with the function i posted.
LVL 44

Expert Comment

ID: 33665851
Did you try my formula at http:#a33648398?  I assumed from your question explaining how the zero(s) got dropped that you wanted the result as a string as all your examples were string.

Author Comment

ID: 33665996
Capricorn's solution worked.  
Since I'm not a programmer, I'm not prepared to evaluate the different alternate solutions proposed by other experts.
I am, however, very interested in understanding the differences between the solutions offered.  I anyone would like to explain, I would be grateful. Otherwise, I will close this ticket (with my sincerest thanks to all) within 24 hours.
LVL 92

Expert Comment

by:Patrick Matthews
ID: 33666300
thutchinson,I'll leave it to the others to describe their solutions, as they know their work best.  So, I'll focus on my own.My solution used Regular Expressions, a very powerful class for a variety of text parsing and replacement chores.  I like to refer to my RegExpFind and its sister functions from my article as "Swiss army knife" functions because they are insanely adaptable to new situations.Basically, that RegExpFind function can find a huge variety of substrings within larger strings, so long as you are able to specify a set of rules governing what constitutes a match.  Those rules are passed to RegExp as a pattern string.In the example I provided above, RegExpFind(OriginalColumn, "\d+", 1)...a) The text being searched is OriginalColumnb) The pattern is "\d+"; \d stands for any digit 0-9, and the + indicates "1 or more of the preceding item".  Thus, \d+ matches 1 or more consecutive digit charactersc) The position is 1That means:1) We look in the value for OriginalColumn2) In that column, we are look for matches (1 or more consecutive digit characters)3) Because we passed a 1 for the position argument, the function returns the first such match it finds.This will thus grab the string "17" if the original string is:17A17AB17ABC17ABCD foo 17ABCD17hhhAB 17AB-17AB17-fooet ceteraPatrick

Author Comment

ID: 33666504
Very cool, MathewsPatrick.  I really appreciate the explanation.  I'm going to spend some time with this.  Sounds like a very useful tool that I can use often.  Many thanks.
LVL 30

Expert Comment

ID: 33667009
I prefer working with direct query expressions for easy maintenance, unless a repetitive use of such an expression, where I make use of user defined functions.

This was introduced to address a problem in a solution to a related problem.
This solution works for 10  and A10, A 10, A-10, and 10A10 but not for A10A.


Int(Val(StrReverse(CStr(Val(StrReverse(strFld & "9")))))/10)


append 9 to it ==> AB109  ' to take care of the 0, it can be any digit 1 to 9
Reverse it  ====> 901BA
Numeric value ==> 901
Change to string=>901
Reverse it =====> 109
Numeric value===> 109
Integer part dividing by 10 ===> 10

strFld = "AB10"
Int(Val(StrReverse(CStr(Val(StrReverse(strFld & "9")))))/10) ===> 10
LVL 44

Expert Comment

ID: 33674200
AB120 - the string
021BA - reverse it
21 -  apply Val() function
12 -  reverse it
Take the Mid substring at the position where '12' is in the string 'AB120' using InStr()
------------------------^-evaluates to 3
Mid('AB120',3) = '120'
without the second qualifier in Mid(), the function returns the substring from position 3 to the end of the string.

Author Comment

ID: 33675965
Messrs hnasr and GrayL,
Thanks for the explanations.  Very helpful.  
my regards,

Featured Post

Complete VMware vSphere® ESX(i) & Hyper-V Backup

Capture your entire system, including the host, with patented disk imaging integrated with VMware VADP / Microsoft VSS and RCT. RTOs is as low as 15 seconds with Acronis Active Restore™. You can enjoy unlimited P2V/V2V migrations from any source (even from a different hypervisor)

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

It’s the first day of March, the weather is starting to warm up and the excitement of the upcoming St. Patrick’s Day holiday can be felt throughout the world.
Did you know that more than 4 billion data records have been recorded as lost or stolen since 2013? It was a staggering number brought to our attention during last week’s ManageEngine webinar, where attendees received a comprehensive look at the ma…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…
What’s inside an Access Desktop Database. Will look at the basic interface, Navigation Pane (Database Container), Tables, Queries, Forms, Report, Macro’s, and VBA code.

724 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question