MS Access中多值字段的替代方案

5

相关问题:多值字段是一个好主意吗?

我知道多值字段类似于多对多关系。在MS Access应用程序中,替换多值字段的最佳方法是什么? 我有一个包含多值字段的应用程序。我不确定如何完全取消这些字段,并以单值字段的形式实现完全相同的逻辑。

当我想将多值关系移动到单值关系时,表关系的实现方式是什么。

谢谢。


我不会说它们与多对多关系相似,我没有详细阅读过它们,但我想你不能使用多值字段作为主键和外键。您能否告诉我们有关它们在您的数据库中广泛使用的更多信息,例如表格、字段、值等的数量?看起来您需要编写一些VBA逻辑来剥离它们,但首先我们需要更多的结构细节。 - Matt Donnan
1
多值字段确实像多对多关系,与Matt Donnan的假设相反。多值字段本身不能成为PK和FK,但通过使用单独的系统值和连接表,在Access“幕后”实现了它们。 - C Perkins
3个回答

4
以下内容可能比您需要的更详细,但适用于初学者。假设您有一张名为MainTable的表:
ID -> Numeric, primary key
Title -> Text
Surname -> Text
Address -> Text
Country -> Numeric

您可能需要一个包含标题和国家的列表以供选择。
对于标题,将信息存储在表中的字段中并不是最糟糕的选择,因为您只有一个列,数据不太可能更改,并且您可能不会使用数据创建查询。
国家则是另一回事,通常情况下,您会存储一个数字并拥有一个查找表。这种情况很容易让人想使用多值字段。
然而,按照传统做法会更加简单。添加另一个用于国家的表即可。
ID -> Numeric, primary key
Country -> Text

你可能希望将主表中相关字段称为“CountryID”。你现在可以在关系窗口中创建一个关系,显示国家如何与MainTable相关:

relationship

您可以看到已选择Enforce Referential Integrity(强制引用完整性),这意味着您必须在CountryID字段中具有null或来自countries表的一个国家。

要查看数据,您可以创建查询:

SELECT 
    MainTable.ID, 
    MainTable.Title, 
    MainTable.Surname, 
    MainTable.Address, 
    Country.Country
FROM Country 
INNER JOIN MainTable 
ON Country.ID = MainTable.CountryID;

查询

但是最重要的是要有一个允许数据输入的表单。您可以使用向导创建表单,但在此之后,您需要右键单击 CountryID 并将其更改为组合框或者使用向导添加组合框或列表框,第二种选项可能是最简单的。下面是来自向导的大部分步骤:

组合框1 组合框2 组合框3 组合框4 组合框5

现在您在表单上拥有了一个下拉国家列表。

表单

另请参见:创建表单以向多个表中添加记录

在 Access 2010 中,有新的方法可以在用户输入不存在于可能值表中的数据时向组合框添加值。在早期版本(虽然我不确定2007年是否有),您将使用 Not In List 事件将项目添加到查找表中,在2010年,您可以选择将列表项编辑表单添加到属性表中。

向查找表添加项目1 向查找表添加项目2


1
谢谢Remou的回答。假设对于你展示的表格,我想要有如下记录:ID:1,标题:Mr.,姓氏:Watson。就国家而言,我想要将这条记录与2个国家相关联(在多值国家字段的情况下,我将获得选项来在同一国家字段中存储两个国家的名称)。我们如何避免使用多值字段;仍然可以从数据库中获得相同的行为。是否有一种方法可以实现这样的行为? - Jay
1
是的,您需要一个连接表。这将包含PersonID和CountryID,并在组合字段上具有唯一索引。例如:https://dev59.com/NknSa4cB1Zd3GeqPLSNq#1356740 - Fionnuala
3
这不是一个答案,也不允许用户选择多个选项,因此不是多值。 - Albert D. Kallal

1
这个问题有些奇怪,因为它询问了一个单值字段,但也询问了表关系。在非常严格的解释中,多值字段(MVF)可以用填有逗号分隔项的单个文本框来替换...不需要表关系。相反,我认为通过“单值”字段,问题是指具有多个表关系的标准字段,其中每个相关行的每个字段都有一个单一的值。但每个主记录仍然可以与相关值表中的多行相关联,这保留了MVF的整个目的。
考虑下面的数据库大纲,以说明可能替代MVF的方法。我没有包括每个可能的属性或如何创建基本对象,只包括创建所需行为所必需的内容 - 假设具有足够的Access知识“填写空白”并且不惧怕基本代码或SQL。
基本结构由三个表组成:1)主表,2)“值列表”表,3)用于定义多对多关系的连接表。
  • 客户表
    客户ID:自动编号,主键
    客户姓名:短文本
  • 代码表
    代码:短文本,长度5,主键
    描述:短文本
  • [客户代码]表
    客户ID:长整型
    代码:短文本

创建表之间的关系。这将需要在表上定义适当的索引(此处不详细说明)。

  • 客户表到[客户代码]表
    客户ID -> 客户ID字段(启用强制完整性)
  • 代码表到[客户代码]表
    代码 -> 代码字段(启用强制完整性)

(还可以为值表[例如代码表]单独创建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基本上正在执行我概述的相同操作,但由于它隐藏了太多细节,因此使一些查询变得更加困难。
关于在表单上呈现和选择多个值,完全模仿多值ComboBox是不可能的--主要是因为基本的Access Combobox没有具有显示复选框的多选项。但是,可以使用属性[Multi Select] = Simple填充非绑定ListBox。在Form_Load事件中,使用ListBox.AddItem方法向列表框添加可用值(例如示例表中的代码)。然后,在Form_Current、Form_AfterUpdate、Form_Undo事件中,可以添加代码以显示和/或保存所选值。这需要更多的代码,可能超出了此处的范围。

从技术上讲,该问题是关于将MVF“移动”到另一个实现的。要点是将值表(例如示例中的代码表)与MVF列表中的相同值填充。这可能是一个手动过程,但取决于如何填充MVF的ComboBox。然后编写一个查询,将每个MVF复制到连接表(例如[客户代码])中,以便为同一主记录服务,类似于:

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

总体来说,完整的实现绝非一项简单的任务,但如果您发现MVF存在太多问题,或者想要迁移到另一个数据库,了解这种变化是必要的。


0

在Access数据库中,没有什么可以替代MVF。虽然有一些查询技巧可以模仿MVF,但你可能会发现MVF的功能更加优越。1. 它快速且非常易于实现,无需编写代码或SQL。2. 它是可视化的,因此对用户来说很直观。有些事情你无法用MVF完成,所以你需要认真衡量哪个更为重要。


2
“没有代码和SQL”是没有用的,因为任何Access数据库中的代码和SQL量取决于最终所需的行为。很多事情可以在没有代码的情况下完成,但许多事情需要代码和/或使用SQL——这是一个非常普遍的陈述,不仅限于MVF。声称“有些事情是你无法做到的”也相当无用,因为精确了解限制是不容易的。最后,确实可以通过连接表和自定义函数来模拟多值字段的许多行为,以便促进查询等操作。 - C Perkins
2
MVF控件允许在表单中添加多项选择UI,而无需编写代码。我所知道的几乎没有任何系统像Access一样不需要编码就能实现此功能。此功能允许非开发人员创建通常需要昂贵开发人员才能创建的应用程序。认为每个人都必须学习使用标准变速器驾车是愚蠢的,在这种情况下也是如此。这是一个允许人们在没有代码或SQL的情况下构建系统的功能。接下来会是什么,建议每个人学习汇编语言来使用计算机吗?接下来会是什么,强迫使用代码来布局一个表单? - Albert D. Kallal
1
替换是一种多对多关系,使用标准RDB设计中的连接表来实现,因为它们在功能上确实是等效的。Access使用内部连接表实现MVFs,仅将添加的复杂性隐藏在用户界面之下。我认为MVFs唯一的限制与其他DBMS的集成或移植有关,因为连接表需要首先重建。也许未来的Access版本会自动提供此功能,但目前,如果数据将在Access之外使用,则应考虑额外的编程工作量。 - Daniel Saner
1
@Albert 我强调MVFs不应过于简化,仅此而已。除非是极其简单的数据库,否则有人想要超越无代码“多选UI”是很可能的。我现在花了一年时间开发和更新一个带有MVF的数据库。否则可以使用Access设计者(即“无SQL”)完成的简单查询并不总是可以使用MVF完成,特别是更新查询和插入查询,尤其是与连接一起使用时。即使MVF不是正在更新的字段...通常在查询中甚至不能引用它,否则会出现问题。 - C Perkins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接