【问题标题】:Find grouped values and then concatenate rows查找分组值,然后连接行
【发布时间】:2015-08-20 14:47:49
【问题描述】:

我在编写一个或多个可以获得我想要的结果的查询时遇到问题。

基本上,我正在努力寻找独特的“公司”。问题是一些公司在同一个地址拥有多个业务。我想连接这些值,而不是重复在 2 个地址运营的 1 家公司的结果。

样本数据:

Company  Address
A        1
A        2
B        1
C        3

所以我们有独特的companies:

A
B
C

addresses

1
2
3

我想获得两条结果记录:

A, B
C

(理想情况下,在第一个表中,A-1、A-2 和 B-1 的结果都指向不同字段中的相同自动编号。)

【问题讨论】:

  • 请尝试显示您到目前为止编写的查询。

标签: sql algorithm vba ms-access


【解决方案1】:

澄清一下:

  • 一个企业可以有多个地址
  • 企业集团(AKA 公司)中的任何企业与该集团中的任何其他企业共享一个地址
  • 一个业务组可以拥有多个业务
  • 每个业务只与一个业务组相关联(推论到第二点)

SqlFiddle with sample data


我们可以按组中的第一个(按字母顺序排列的最小名称)业务来指代每个业务组。我们称之为关键业务。在我们确定了每个业务的关键业务之后,我们可以按关键业务进行分组并得到结果。

为了获得关键业务:

  • 根据任何共享地址生成一个企业对列表,其中两个企业都在同一个组中。此列表应排除以下内容(请参阅下一点了解原因):

    1. A -> B,当我们有 B -> A
    2. A -> A

    对的左侧应该是唯一的:每个企业应该出现在对的左侧不超过一次,如果有的话。

  • 对于每个业务,从一对到下一个,直到正确的业务永远不会是任何其他对中的左侧业务。这是关键业务。

    这就是第一点排除的原因。如果我们同时拥有A -> BB -> A,我们将进入一个永无止境的循环。 A -> A 也是如此。


第一部分可以直接用纯 SQL 完成:

SELECT Businesses.Business AS Business2, MIN(Businesses_1.Business) AS Business1
FROM Businesses
INNER JOIN Businesses AS Businesses_1 ON Businesses.Address = Businesses_1.Address
WHERE Businesses.Business > Businesses_1.Business
GROUP BY Businesses.Business

SqlFiddle


(其他 RDBMS 支持 recursive joining,因此对于第二部分,我们可以从源数据开始并指示数据库无限期地加入,直到最终的 Business1NULLSee here。AFAIK MS Access 没有'在 SQL 中不支持,所以我们必须在 VBA 中进行。)

我建议为此使用Scripting.Dictionary。您需要添加对 Microsoft Scripting Runtime 的引用(Tools -> References...)。

'Create a Dictionary to hold the pairs
Dim pairs As New Scripting.Dictionary

'Load the pairs using the above SQL
Dim sql = _
    "SELECT Businesses.Business AS Business2, MIN(Businesses_1.Business) AS Business1 " & _
    "FROM Businesses " & _
    "INNER JOIN Businesses AS Businesses_1 ON Businesses.Address = Businesses_1.Address " & _
    "WHERE Businesses.Business > Businesses_1.Business " & _
    "GROUP BY Businesses.Business"
Dim rsPairs = CurrentDb.OpenRecordset(sql, dbOpenForwardOnly)
Do Until rsPairs.EOF
    pairs(rsPairs!Business2) = rsPairs!Business1
Loop
rsPairs.Close
Set rsPairs = Nothing

'Create a Dictionary to hold the groupings
Dim groupings As New Scripting.Dictionary

sql = _
    "SELECT DISTINCT Business " & _
    "FROM Businesses " & _
    "ORDER BY Business"        
Dim rsBusinesses As DAO.Recordset
Set rsBusinesses = CurrentDb.OpenRecordSet(sql, dbOpenForwardOnly)
Do Until rsBusinesses.EOF
    Dim business As String
    business = rsBusinesses!Business
    If Not pairs.Exists(business) Then
        Dim col As New Collection
        col.Add(business)
        groupings(business) = col
    Else
        'Find the group's key business
        Dim prevBusiness As String
        prevBusiness = business
        Do While pairs.Exists(prevBusiness)
            prevBusiness = pairs(prevBusiness)
        Loop
        groupings(prevBusiness).Add(business)
    End If
Loop

最终的字典将如下所示:

Key    Collection
A      (A, B, W, X)
C      (C)
D      (D, E, F, G)

并且可以如下迭代:

Dim key As Variant, item As Variant
For Each key In groupings
    Debug.Print "Grouping " & key
    For Each item In groupings(key)
        Debug.Print "Business - " & item
    Next
Next

【讨论】:

  • 我更新了我的答案,并更多地反映了业务问题以及 SQL 和字典方法之间的相似之处。我可以看到字典方法处理层次结构(使用我从中学到的一种很好的技术——我以前没有使用过字典,但使用了分层 SQL)。我看不到层次结构在数据中的位置。你能帮我吗。我很确定你的方法是最好的方法,但我自己正在尝试更多地了解这一点。我认为主要问题是 OP 业务问题没有真正彻底记录。
  • 我根据这个问题中概述的问题打开了一个新的 SO 问题。见here
  • @HarveyFrench 我已经修改了这个答案,所以意图是在一开始。在我对您的其他问题的回答中描述递归连接方法时,这也更有效。
【解决方案2】:

我无法理解您的问题!
请确认我的解释,然后考虑我的回答

您在 tableBA 中有此示例数据

BUSINESS  Address   < notice I call this BUSINESS not company
A        1
A        2
B        1
C        3
D        4          < I have added four businesses sharing the same address
E        4
F        4
G        4
X        5          < X does not share an address with any other businesses
X        6            but it does operate at multiple addresses

您确实需要澄清您希望如何处理上述情况。

有些公司在同一个地址拥有多家企业。我假设可能有两个以上的企业共享地址的情况,尽管在您的示例数据中并非如此。

  1. 如果没有其他企业在经营企业使用的任何地址,我们可以使用企业名称作为公司名称。 (如上面的 C 所示)。请注意,A 在地址 2 中单独运行,但由于它与 B 共享地址,因此不能包含在内。

  2. 如果许多企业在同一个地址运营,则通过将它们连接在一起(逗号分隔)将它们的所有企业名称用作公司名称。

上面创建的每个“公司名称”都应该只列出一次,比如这样

A, B
C

我的回答

按地址排序上述数据,然后按公司名称排序:

Data       Resulting text wanted
1, A       A, B
1, B

2, A       NONE

3, C       C

4, D       D,E,F,G
4, E
4, F    
4, G

5, X      X
6, X

可能会出现以下情况,怎么办?即 P 与另外两家公司共享两个不同的地址

7, O   
7, P

8, P
b, Q

第 1 部分。获得“C”和“X”

SELECT Tab1.address, Tab1.Business
FROM tableBA AS Tab1
WHERE NOT EXISTS 
    (
    SELECT 1
    FROM tableBA AS Tab2
    WHERE Tab2.Business <>  Tab1.Business
      AND Tab2.Address IN (SELECT Address 
                             FROM  tableBA AS Tab3
                            WHERE tab3.Business = Tab1.Business)
    )      
GROUP BY Tab1.Business      

仅当 Tab1 中的商家在其任何地址都没有其他商家时,EXISTS 子句才返回 true。


第 2 部分. 得到 "A, B" 和 "D,E,F,G"

SELECT Address, Business
FROM tableBA
    INNER JOIN 
    (
    SELECT Address
    FROM tableBA
    GROUP BY Address
    HAVING COUNT (*)>1
    ) As Tab1       
    ON tableBA.Address = Tab1.Address

子查询只返回多个业务使用的地址,并限制返回的行数。

您现在需要“透视”结果,以便每个地址只有一行,所有公司名称作为列。

您可以通过多种方式使用代码执行此操作,我可能会使用记录集来打开上述第 2 部分的查询并遍历所有附加到字符串的行。


使用第 1 部分和第 2 部分的结果

结果字符串列表的存储位置是另一回事,听起来您想将其写入表以供以后使用。

我会将第 2 部分中提到的记录集基于第 1 部分和第 2 部分联合在一起的查询。

SELECT ... ie the part 1 query 
UNION 
SELECT ... ie the part 2 query 

同时处理所有行,可能会将结果列表插入到表中。

就我个人而言,我发现这种方法比使用字典对象更直接,因为它允许您使用 SQL 来查看正在处理和创建的记录。

【讨论】:

  • 会的。这就是为什么您需要处理生成的记录集,以按照描述“旋转”它。
猜你喜欢
  • 1970-01-01
  • 2021-09-26
  • 1970-01-01
  • 1970-01-01
  • 2019-09-07
  • 1970-01-01
  • 2015-08-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多