Alternative to multi-valued fields in MS Access

后端 未结 3 1020
梦谈多话
梦谈多话 2021-02-10 03:00

Related question: Multivalued Fields a Good Idea?

I know that multi-valued fields are similar to many-to-many relationship. What is the best way to replace multi-valued

3条回答
  •  滥情空心
    2021-02-10 03:02

    The question is rather odd since it asks about a single-value field, but also asks about table-relationships. In a very strict interpretation, a multi-value field (MVF) could be replaced with a single TextBox filled with comma-separated items... no table-relationships required. Instead, I assume by "single-value" field that the question means standard fields in a multi-table relationship, in which each field of each related row has a single value. But each primary record can still be related to multiple rows in the related value table, which preserves the whole purpose of the MVF.

    Consider the database outline below to illustrate a possible replacement for the MVF. I'm not including every possible property or how to create basic object, just what's necessary for creating desired behavior--assuming enough knowledge of Access to "fill in the blanks" and no fear of basic code or SQL.

    The basic structure consists of three tables: 1) Primary table, 2) "value list" table, 3) junction table used to define many-to-many relationship.

    • Customer Table
      CustomerID: autonumber, primary
      CustomerName: short text
    • Codes Table
      Code: short text, length 5, primary key
      Description: short text
    • [Customer Codes] Table
      CustomerID: long
      Code: short text

    Create relationships between the tables. This will require appropriate indexes defined on the tables (not detailed here).

    • Customer Table to [Customer Codes] Table
      CustomerID -> CustomerID fields (with enforce integrity enabled)
    • Code Table to [Customer Codes] Table
      Code -> Code fields (with enforce integrity enabled)

    (A separate ID field could also be created for the values table [e.g. Codes Table] table, but that just complicates later queries and controls, etc. In such a case, the junction table would contain another ID field and not the value directly.)

    In a standard VBA module, create a function like

    Public Function GetCodeList(ByVal CustomerID As Integer) As String
       Dim sSQL As String
       Dim qry As QueryDef
       Dim rs As Recordset2
    
       sSQL = "PARAMETERS [CustID] LONG;" & _
           " SELECT * FROM [Customer Codes] WHERE [CustomerID] = [CustID]"
       Set qry = CurrentDb.CreateQueryDef("", sSQL)
       qry.Parameters("CustID") = CustomerID
       Set rs = qry.OpenRecordset(dbOpenForwardOnly, dbReadOnly)
    
       Dim sCodes As String
       sCodes = ""
       Dim bFirst As Boolean
       bFirst = True
    
       Do Until rs.EOF
          sCodes = sCodes & IIf(bFirst, "", ",") & rs("Code")
          bFirst = False
          rs.MoveNext
       Loop
    
       rs.Close
       qry.Close
    
       GetCodeList = sCodes
    End Function
    

    Useful queries of the primary table without duplicate rows will require creating some sort of aggregate queries (i.e. Group By, Count, etc.). Simple selection could be done in a single query, for example

    SELECT Customer.CustomerID, Customer.[CustomerName], GetCodeList([CustomerID]) AS Codes, Count(Customer.CustomerID) AS CountOfID
    FROM Customer LEFT JOIN [Customer Codes] ON Customer.ID = [Customer Codes].CustomerID
    WHERE ((([Customer Codes].Code)="Current" Or ([Customer Codes].Code)="Free"))
    GROUP BY Customer.ID, Customer.[CustomerName], GetCodeList([ID]);
    

    More complicated selection may require multiple queries, one to first select the proper records, then another to join the primary table to the first query. But honestly, these types of queries are no more complicated than what can be required to select on Multi-Valued Fields. In fact, the query syntax for MVF is non-standard and can get rather complicated and confusing, even more than having a junction table and many-to-many relationships. Behind the scenes, Access is essentially doing the same thing as I've outlined, but because it hides so much detail it make some queries even more difficult.

    Regarding multi-value presentation and selection on a form, mimicking the multi-valued ComboBox exactly is not possible--mainly because the basic Access Combobox does not have a multi-selection option with the ability to show checkboxes. However, one can populate a non-bound ListBox with property [Multi Select] = Simple. On the Form_Load event, add available values (e.g. Code from the example table) to the listbox using ListBox.AddItem method. Then in the Form_Current, Form_AfterUpdate, Form_Undo events, one can add code to show and/or save the selected values. This requires more code which is probably beyond the scope here.

    Technically the question asked about "moving" a MVF to another implementation. The gist is to populate the values table (e.g. Code table in the example) with the same values in the MVF list. This could be a manual process, but depends on how the MVF's ComboBox is populated. Then write a query which copies each MFV into the junction table (e.g. [Customer Code]) for the same primary record, something like

    INSERT INTO [Customer Codes] ( CustomerID, Code )
    SELECT Customer.CustomerID, Customer.TestMVF.Value
    FROM Customers
    WHERE (((Customers.TestMVF.Value) Is Not Null));
    

    A full implementation is definitely not a simple task overall, but if you find too many problems with MVFs or if you are wanting to migrate to another database, this kind of change will be necessary to understand.

提交回复
热议问题