3

我有一个 Access 数据库,需要在一行上呈现具有多个一对多关系的数据(例如,它将项目列为“a、b、e、f”,并且我将有多个这样的列) . 我知道以这种方式存储数据也是一个坏主意,但考虑到我允许用户过滤其中的几个列,我想不出比违反第一个范式更好的处理数据的方法.

举个例子:假设我有几篇期刊文章,每篇文章都可能报道多种动物和多种蔬菜。用户可以过滤源名称,或者他们可以过滤一种或多种动物和一种或多种蔬菜。输出应该看起来像

Source name....animals...............vegetables
Source 1.......dogs, cats, birds.....carrots, tomatoes
Source 2.......leopards, birds.......tomatoes, zucchini, apples
Source 3.......cats, goldfish........carrots, cucumbers

通常,您将有一个带有源名称 + 动物的单独表:

Source name......animal
Source 1.........dog
Source 1.........cats
Source 1.........birds
Source 2.........leopards

ETC

和一个类似的蔬菜表。但考虑到数据需要如何呈现给用户(逗号分隔的列表),以及用户如何过滤数据(他可能会过滤以仅查看包括狗和猫的源,以及带有胡萝卜和西红柿的源),我认为将数据存储为动物和蔬菜的逗号分隔列表是有意义的。使用逗号分隔的列表,当用户选择多种蔬菜和多种动物时,我可以说

WHERE (Vegetables like "*carrots*" and Vegetables like "*tomatoes*") AND (Animals like *dogs*" and Animals like "*cats*")

在不使用大量 VBA 和多个查询的情况下,我想不出一种在 Access 中执行相同类型查询的有效方法。

4

3 回答 3

4

您总是可以构建一个违反任何规则都有意义的场景,因此您标题中问题的答案是肯定的。

然而,这不是其中一种情况。您提出的搜索和表示问题对于大多数一对多关系(或者至少对于许多一对多关系)来说是常见的,如果这是违反第一范式的原因,那么您将看不到很多规范化的数据库。

正确构建数据库,您不必担心逗号、相互嵌入的搜索词以及由于缺少索引而导致的缓慢搜索。编写一段可重用的代码来为您执行逗号分隔的汇总,这样您就不会不断地重新发明轮子。

于 2012-10-01T19:28:45.817 回答
0

我仍然会正确地对此进行规范化-然后担心演示文稿。

在 Oracle 中 - 这将使用用户定义的聚合函数来完成。

于 2012-10-01T19:28:45.843 回答
0

为什么不构建一个连接表,以便您可以维持与字段引用完整性的 1:1 关系。否则,您将不得不解析出 1:many 字段值,然后才能获得引用关联,因此一切都可以神奇地工作(呵呵呵呵;))

当我发现自己需要违反第一范式时,9:10 的答案是创建一个连接表并构建一些方法来产生预期的效果。

编辑:2012-10-09 上午 9:06

  • 这种设计是为了响应在未知数量的列/字段中显示的未知数量的信息。虽然我的方向是数值,但您可以简单地开发一种 vba 方法来连接信息字段以生成单个数据字段。

表格1

gid (Number) <- Table2.id
cid (Number) <- Table3.id
price (Number)
  • gid 许多 <- 一个 Table2.id
  • cid 许多 <- 一个 Table3.id

crt_CategoryPG

TRANSFORM Sum(Table1.price) AS SumOfprice
SELECT View1.tid, Table1.cid, View1.category
FROM Table2 INNER JOIN (View1 INNER JOIN Table1 ON View1.cid = Table1.cid) ON Table2.gid = Table1.gid
WHERE (((Table2.active)=True) AND ((View1.active)=True))
GROUP BY View1.tid, Table1.cid, View1.category, Table2.active, View1.active
ORDER BY View1.tid, Table1.cid
PIVOT [Table2].[gid] & " - " & [Table2].[nm];

刷新 CT 表

Public Function RefreshCategoryPricing(Optional sql As String = "")
    If HasValue(lstType) Then
Application.Echo False      'Turn off Screen Updating

        Dim cttbl As String: cttbl = CreateCTTable("crt_CategoryPG") 'Create Table to store the Cross-Tab information
        If IsNullOrEmpty(sql) Then
            sql = SQLSelect(cttbl)
        End If

        Dim flds As DAO.Recordset: Set flds = CurrentDb.OpenRecordset(sql)
        Dim fldwd As String, fldhd As String      'Store the Field Width pattern and Field Header Row
        fldhd = "-1;-1;Category"
        fldwd = "0"";0"";2.5"""   'Handles `tid`, `cid`, and `category` columns in the ListBox
        'Assign the number of columns based on the number of fields in CTtable
        lstCategoryPG.ColumnCount = flds.Fields.Count

        Dim fld As Long
        For fld = 3 To (flds.Fields.Count - 1)
            fldwd = fldwd & ";.75"""
            fldhd = fldhd & ";" & flds.Fields(fld).Name
        Next

        GC flds

        lstCategoryPG.ColumnHeads = True
        lstCategoryPG.ColumnWidths = fldwd

        sql = SQLSelect(cttbl, , ("tid = " & lstType.Value))

        lstCategoryPG.Enabled = True
        RefreshControl CurrentDb, lstCategoryPG, sql, , False
        lstCategoryPG.AddItem fldhd, 0

Application.Echo True       'Turn on Screen Updating
    End If
End Function

创建交叉表

'@ct - String value, Source Cross-Tab to base Table design off of
Public Function CreateCTTable(ct As String) As String
    Dim tbl As String: tbl = "tbl_" & ct
    Dim sql As String

    If TableExists(tbl) Then
    'Table exists and needs to be dropped
        sql = SQLDrop(tbl)
        CurrentDb.Execute sql
    End If

    'Create Table
    sql = SQLSelect(ct, "* INTO " & tbl)
    CurrentDb.Execute sql

    CreateCTTable = tbl
End Function
于 2012-10-01T19:44:17.873 回答