在MS Access中将多行具有多个值的内容连接成单行

3

我正在尝试创建一个简单的需求管理数据库。基本上,我有2个如下所示的表:

合同要求(Contract_requirements)有2个列:

CR_ReqID    |   Description
reqCR1      |   Contract req description 1
reqCR2      |   Contract req description 2

SW_需求

Title               |   SW_ReqID     |  RootReq
SW req description 1|   reqSW1       |   reqCR1, reqCR2
SW req description 2|   reqSW2       |   reqCR1
SW req description 3|   reqSW3       |   reqCR2

我想编写查询以获得这样的表:

CR_ReqID  |Description                  |where used?
reqCR1    |Contract req description 1   |reqSW1, reqSW2  
reqCR2    |Contract req description 2   |reqSW1, reqSW3

表格“合同要求”和“软件要求”通过列“RootReq”相关联。

我尝试实现了Allen Browne的代码http://allenbrowne.com/func-concat.html#Top

这是我的查询:

SELECT Contract_requirements.CR_ReqID, ConcatRelated("SW_ReqID     ","SW_requirements","RootReq = """ & [CR_ReqID] & """") AS Expr1
FROM Contract_requirements;

但是我在Access中遇到了错误

"错误3831:多值字段“RootReq”不能在WHERE或HAVING子句中使用"

你们能帮我解决这个问题吗? 提前感谢。


只有两个值吗? - Gordon Linoff
所以根据错误信息,RootReq 是一个多值字段。最好将数据结构规范化。 - June7
可能有很多个值,不仅仅是两个... - Stefan87
2个回答

1
构建一个查询,将多值字段元素扩展为单独的记录。
查询1
SELECT SW_Requirements.Title, SW_Requirements.SW_ReqID, SW_Requirements.RootReq.Value 
FROM SW_Requirements;

然后将该查询用作ConcatRelated()函数的来源。
SELECT Contract_Requirements.*, 
ConcatRelated("SW_ReqID","Query1","[SW_Requirements.RootReq.Value]='" & [CR_ReqID] & "'") AS WhereUsed
FROM Contract_Requirements;

建议在命名约定中不要使用空格、标点符号或特殊字符。

在表SW_requirements中,列“RootReq”是数字... 当我在Contract_Requirements中为字段“CR_ReqID”创建查找规则时,Access就像这样创建了它... 我无法将RootReq更改为文本... - Stefan87
然后CR_ReqID也必须是数字类型,或者在Contract_Requirements中有另一个与RootReq连接的关键字段。去掉CR_ReqID周围的撇号。 - June7
我不知道如何检查"SW_Requirements.RootReq.Value"的数据类型。 - Stefan87
你已经说过该字段是数字类型了。那么在Contract_Requirements中对应的字段是什么类型?你说它们是相关的,所以它们必须是相同的数据类型。 - June7
这很奇怪,我现在感觉像个白痴...现在我注意到当我使用查找向导将RootReq与CR_ReqID连接时,在关系窗口中的连接是在Contract_Requirements的[ID]和SW_Requirements中的[RootReq]之间...我将[CR_ReqID]设置为主键,然后通过查找向导将其链接到RootReq...现在一切都像魔法般地运作...谢谢! - Stefan87
显示剩余2条评论

1

您还可以使用我的 DJoin 函数,因为它可以接受 SQL 作为源,因此您不需要额外保存查询:

' Returns the joined (concatenated) values from a field of records having the same key.
' The joined values are stored in a collection which speeds up browsing a query or form
' as all joined values will be retrieved once only from the table or query.
' Null values and zero-length strings are ignored.
'
' If no values are found, Null is returned.
'
' The default separator of the joined values is a space.
' Optionally, any other separator can be specified.
'
' Syntax is held close to that of the native domain functions, DLookup, DCount, etc.
'
' Typical usage in a select query using a table (or query) as source:
'
'   Select
'       KeyField,
'       DJoin("[ValueField]", "[Table]", "[KeyField] = " & [KeyField] & "") As Values
'   From
'       Table
'   Group By
'       KeyField
'
' The source can also be an SQL Select string:
'
'   Select
'       KeyField,
'       DJoin("[ValueField]", "Select ValueField From SomeTable Order By SomeField", "[KeyField] = " & [KeyField] & "") As Values
'   From
'       Table
'   Group By
'       KeyField
'
' To clear the collection (cache), call DJoin with no arguments:
'
'   DJoin
'
' Requires:
'   CollectValues
'
' 2019-06-24, Cactus Data ApS, Gustav Brock
'
Public Function DJoin( _
    Optional ByVal Expression As String, _
    Optional ByVal Domain As String, _
    Optional ByVal Criteria As String, _
    Optional ByVal Delimiter As String = " ") _
    As Variant

    ' Expected error codes to accept.
    Const CannotAddKey      As Long = 457
    Const CannotReadKey     As Long = 5
    ' SQL.
    Const SqlMask           As String = "Select {0} From {1} {2}"
    Const SqlLead           As String = "Select "
    Const SubMask           As String = "({0}) As T"
    Const FilterMask        As String = "Where {0}"

    Static Values   As New Collection

    Dim Records     As DAO.Recordset
    Dim Sql         As String
    Dim SqlSub      As String
    Dim Filter      As String
    Dim Result      As Variant

    On Error GoTo Err_DJoin

    If Expression = "" Then
        ' Erase the collection of keys.
        Set Values = Nothing
        Result = Null
    Else
        ' Get the values.
        ' This will fail if the current criteria hasn't been added
        ' leaving Result empty.
        Result = Values.Item(Criteria)
        '
        If IsEmpty(Result) Then
            ' The current criteria hasn't been added to the collection.
            ' Build SQL to lookup values.
            If InStr(1, LTrim(Domain), SqlLead, vbTextCompare) = 1 Then
                ' Domain is an SQL expression.
                SqlSub = Replace(SubMask, "{0}", Domain)
            Else
                ' Domain is a table or query name.
                SqlSub = Domain
            End If
            If Trim(Criteria) <> "" Then
                ' Build Where clause.
                Filter = Replace(FilterMask, "{0}", Criteria)
            End If
            ' Build final SQL.
            Sql = Replace(Replace(Replace(SqlMask, "{0}", Expression), "{1}", SqlSub), "{2}", Filter)

            ' Look up the values to join.
            Set Records = CurrentDb.OpenRecordset(Sql, dbOpenSnapshot)
            CollectValues Records, Delimiter, Result
            ' Add the key and its joined values to the collection.
            Values.Add Result, Criteria
        End If
    End If

    ' Return the joined values (or Null if none was found).
    DJoin = Result

Exit_DJoin:
    Exit Function

Err_DJoin:
    Select Case Err
        Case CannotAddKey
            ' Key is present, thus cannot be added again.
            Resume Next
        Case CannotReadKey
            ' Key is not present, thus cannot be read.
            Resume Next
        Case Else
            ' Some other error. Ignore.
            Resume Exit_DJoin
    End Select

End Function

' To be called from DJoin.
'
' Joins the content of the first field of a recordset to one string
' with a space as delimiter or an optional delimiter, returned by
' reference in parameter Result.
'
' 2019-06-11, Cactus Data ApS, Gustav Brock
'
Private Sub CollectValues( _
    ByRef Records As DAO.Recordset, _
    ByVal Delimiter As String, _
    ByRef Result As Variant)

    Dim SubRecords  As DAO.Recordset

    Dim Value       As Variant

    If Records.RecordCount > 0 Then
        While Not Records.EOF
            Value = Records.Fields(0).Value
            If Records.Fields(0).IsComplex Then
                ' Multi-value field (or attachment field).
                Set SubRecords = Records.Fields(0).Value
                CollectValues SubRecords, Delimiter, Result
            ElseIf Nz(Value) = "" Then
                ' Ignore Null values and zero-length strings.
            ElseIf IsEmpty(Result) Then
                ' First value found.
                Result = Value
            Else
                ' Join subsequent values.
                Result = Result & Delimiter & Value
            End If
            Records.MoveNext
        Wend
    Else
        ' No records found with the current criteria.
        Result = Null
    End If
    Records.Close

End Sub

完整的文档可以在我的文章中找到:

连接来自表或查询的一个字段的值

如果您没有帐户,请浏览链接:阅读完整的文章。

代码也在GitHub上:VBA.DJoin


谢谢您的帖子,我也会尝试一下 :) - Stefan87

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