这个问题很奇怪,因为它询问的是单值字段,但也询问了表关系。在非常严格的解释中,多值字段 (MVF) 可以替换为单个文本框,其中填充了逗号分隔的项目……不需要表关系。相反,我通过“单值”字段假设问题意味着多表关系中的标准字段,其中每个相关行的每个字段都有一个 single 值。但是每个主记录仍然可以与相关值表中的多行相关联,这保留了 MVF 的全部用途。
考虑下面的数据库大纲来说明 MVF 的可能替代品。我没有包括所有可能的属性或如何创建基本对象,只是创建所需行为所必需的——假设有足够的 Access 知识来“填补空白”并且不担心基本代码或 SQL。
基本结构由三个表组成:1)主表,2)“值列表”表,3)用于定义多对多关系的联结表。
-
客户表
客户 ID:自动编号,主要
客户名称:短文本
-
代码表
代码:短文本,长度5,主键
说明:短文本
-
[客户代码] 表
客户 ID:长
代码:短文本
创建表之间的关系。这将需要在表上定义适当的索引(此处不详)。
- 客户表到 [客户代码] 表
CustomerID -> CustomerID 字段(启用强制完整性)
- 代码表到 [客户代码] 表
代码 -> 代码字段(启用强制完整性)
(也可以为值表 [例如代码表] 表创建一个单独的 ID 字段,但这只会使以后的查询和控制等复杂化。在这种情况下,联结表将包含另一个 ID 字段而不是直接取值。)
在标准 VBA 模块中,创建类似的函数
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
没有重复行的主表的有用查询将需要创建某种聚合查询(即 Group By、Count 等)。例如,可以在单个查询中完成简单的选择
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]);
更复杂的选择可能需要多个查询,一个首先选择正确的记录,然后另一个将主表连接到第一个查询。但老实说,这些类型的查询并不比在多值字段上选择所需的复杂。事实上,MVF 的查询语法是非标准的,并且会变得相当复杂和混乱,甚至比拥有联结表和多对多关系还要复杂。在幕后,Access 本质上与我所概述的相同,但由于它隐藏了太多细节,使得一些查询变得更加困难。
关于表单上的多值显示和选择,完全模仿多值组合框是不可能的——主要是因为基本的 Access Combobox 没有显示复选框的多选选项。但是,可以使用属性 [Multi Select] = Simple 填充未绑定的 ListBox。在 Form_Load 事件中,使用 ListBox.AddItem 方法将可用值(例如示例表中的代码)添加到列表框。然后在 Form_Current、Form_AfterUpdate、Form_Undo 事件中,可以添加代码来显示和/或保存选定的值。这需要更多代码,这可能超出了这里的范围。
从技术上讲,这个问题是关于将 MVF“移动”到另一个实现的。要点是在 MVF 列表中使用相同的值填充值表(例如示例中的代码表)。这可能是一个手动过程,但取决于 MVF 的 ComboBox 的填充方式。然后编写一个查询,将每个 MFV 复制到联结表(例如 [客户代码])中以获取相同的主记录,类似于
INSERT INTO [Customer Codes] ( CustomerID, Code )
SELECT Customer.CustomerID, Customer.TestMVF.Value
FROM Customers
WHERE (((Customers.TestMVF.Value) Is Not Null));
一个完整的实现总体上绝对不是一个简单的任务,但是如果你发现MVF有太多问题或者你想迁移到另一个数据库,这种变化是有必要了解的。