sqlvbams-accesseval

Looking to get a string evaluated a second time so that I can change a single rule across independent queries


I want the following query to work where [Tryme] is a written rule that would be true or false so that I can run a table of QC things against it and get a table of all the, in this case, Employee and check pairs that break my rules.

SELECT 
    Temp.*, FunctionIWant([Temp.TryMe]) AS Checked
FROM 
    (SELECT 
         Employee.ID, Employee.[First Name], Employee.[Last Name], 
         Employee.Wage, Employee.Tenure, 
         TheseRules.ID, TheseRules.Check AS Tryme
     FROM 
         Employee, TheRules AS TheseRules) AS Temp;

Below is a statement that return with [Checked] returning a boolean the way I want. The text written for the Checked column is the exact text that is in Temp.Tryme (to be more clear, "1.05*[Tenure]+37>[Wage]" is the string)

SELECT 
    Temp.*, **1.05*[Tenure]+37>[Wage]** AS Checked
FROM 
    (SELECT 
         Employee.ID, Employee.[First Name], Employee.[Last Name], Employee.Wage, 
         Employee.Tenure, TheseRules.ID, TheseRules.Check AS Tryme
     FROM 
         Employee, TheRules AS TheseRules) AS Temp;

Because it's just another level of evaluation than normal, I'm hoping this is possible.

I have tried Eval() which either returns the string itself or #Error.

I'm currently trying to create a function that would do it but I can't find out how to just give all the fields to the function for them to be evaluated there


Solution

  • For Eval() to work, would have to replace the string [Tenure] with value of field Tenure (likewise for Wage). Could possibly build a function that would receive field values (including expression string stored in Check field) and accomplish appropriate replace operations to produce an expression string that Eval() can process. I expect this will get rather complicated depending on the variety of "rules" to be evaluated.

    Function could receive as arguments: data table name, record ID, calculation name, expression string. Open a recordset of given table filtered to ID. A SELECT CASE block would locate calculation name and perform replace operations on expression string pulling data from recordset fields.

    Or abandon Rules table and have function hard code expressions. Pass to function data table name and record ID and calculation name. Still use a SELECT CASE but instead of Replace operations on a string, expressions just directly reference recordset fields. No Eval() function needed.

    In either of those approaches, as "rules" are added, procedure will have to be expanded to accommodate. Avoiding this and streamlining procedure could use a "mapping" table that specifies field names and associated strings in expression they replace. Instead of SELECT CASE, open a recordset of the "mapping" table and loop its records to perform appropriate replace operations on string expression then Eval() can process the string.

    Example of mapping table:

    FldNme FldMask TblNme
    Tenure Tenure Employees
    Wage Wage Employees

    Function:

    Function CheckCalc(intI As Integer, strT As String, strE As String) As Boolean
    Dim rsData As DAO.Recordset, rsMap As DAO.Recordset
    Set rsData = CurrentDb.OpenRecordset("SELECT * FROM [" & strT & "] WHERE ID=" & intI)
    Set rsMap = CurrentDb.OpenRecordset("SELECT * FROM ExpMap WHERE TblNme ='" & strT & "'")
    Do While Not rsMap.EOF
        strE = Replace(strE, rsMap!FldMask, rsData(rsMap!FldNme))
        rsMap.MoveNext
    Loop
    CheckCalc = Eval(strE)
    End Function
    

    This assumes every table has key field named ID. If that is not the case, function will have to be modified to pass this field name as another argument and construct SQL statement with that variable. The mask in expression can be whatever you want, just make sure whatever is in the expression is in mapping table, such as [ ] characters, which I removed from expression in my testing and therefore is not seen in Mapping table.

    If exact field name is used in expression as mask, could eliminate mapping table and its recordset. Procedure would instead loop through rsData fields and test with InStr() for its presence in expression string and if there, perform Replace operation.

    Function CheckCalc(intI As Integer, strT As String, strE As String) As Boolean
    Dim rsData As DAO.Recordset, fld As DAO.Field
    Set rsData = CurrentDb.OpenRecordset("SELECT * FROM [" & strT & "] WHERE ID=" & intI)
    For Each fld In rsData.Fields
        If InStr(strE, fld.Name) Then
            strE = Replace(strE, fld.Name, fld)
        End If
    Next
    CheckCalc = Eval(strE)
    End Function