【问题标题】:How to consolidate two id columns, identifying which rows belong to same set of related IDs如何合并两个 id 列,识别哪些行属于同一组相关 ID
【发布时间】:2019-11-23 09:11:09
【问题描述】:

我有 2 个独立创建/收集的 ID 列。我试图通过根据两个 ID 列中的任何一个确定哪些行是同一相关组 id 的一部分,将这两个 ID 列合并为一个列。我会根据一些规则考虑相关的行:

1:如果一个 LOAN 在多行中具有相同的值,则它们属于同一个组(示例中仅供参考)。我将其命名为loan_group。这里没有问题。

2:如果一个 COLLATERAL 在多行中具有相同的值,则它们属于临时组。我将其命名为lateral_group(与#1 相同的规则。)这里没有问题。

3:最后,我不知道如何准确地表达这一点,但任何时候属于同一组的值之间存在重叠(跨贷款和抵押列),这些组应进一步合并。例如:

LOAN  COLLATERAL  loan_group  collateral_group  final_grouping
----  ----------- ----------  ----------------  --------------
L1    C1*         1           1                 **1**
L2**  C1*         2           1                 **1**
L5    C8          3           2                 2
L2**  C4***       2           3                 **1**
L6    C8          4           2                 2
L7    C9          5           4                 3
L8    C4***       6           3                 **1**

*因为第 1 行和第 2 行的值都是 C1,所以它们将被分配到相同的最终分组中

**因为第 2 行具有 LOAN 值 L2,这意味着我们可以将第 4 行包含在合并的最终分组中。该行可以通过 L2/C1 链接链接回第 1 行

***最后,因为第 4 行包含 COLLATERAL 值 C4,这意味着我们可以将第 7 行包含在合并的最终分组中。该行可以通过 L2/C4 和 L2/C1 链接链接回第一行

该数据集大约是 15m 个独特的 LOAN + COLLATERAL 组合。在某些边缘情况下,这些组可能会交叉几千个(可能 +1 万个)ID。我在 BQ 测试某些解决方案时遇到了一些资源问题(但这些问题主要与我对 BQ 的缺乏经验有关。)如果这会影响任何人的建议,请提个醒。

非常感谢您的宝贵时间,对于我在第一个版本中过于含糊/简短表示歉意...

【问题讨论】:

  • 这看起来像是一个图行走问题。 BigQuery 没有对递归 CTE 或分层查询的原生支持,因此我认为这不能通过单个查询来完成。
  • 这个问题对我来说很清楚!如果重新打开会回答。 @ehitch - 编辑您的问题并添加更多细节,使其符合重新开放的条件
  • @ehitch - 看起来这篇文章不会重新打开 - 你可能想发布新问题,以便我能够回答

标签: sql google-bigquery


【解决方案1】:

以下是 BigQuery 标准 SQL

正如 Gordon 在 cmets 中提到的 - BigQuery 不具备对递归 CTE 或分层查询的原生支持,因此仅靠单个查询无法做到这一点!

但是...,这可以使用最近引入的scripting 来实现,如下例所示

DECLARE rows_count, run_away_stop INT64 DEFAULT 0;

CREATE TEMP TABLE input AS (
  SELECT 'L1' loan, 'C1' collateral UNION ALL
  SELECT 'L2', 'C1' UNION ALL
  SELECT 'L5', 'C8' UNION ALL
  SELECT 'L2', 'C4' UNION ALL
  SELECT 'L6', 'C8' UNION ALL
  SELECT 'L7', 'C9' UNION ALL
  SELECT 'L8', 'C4'
);

CREATE TEMP TABLE initial_grouping AS 
SELECT ARRAY_AGG(collateral ORDER BY collateral) arr 
FROM input
GROUP BY loan;

LOOP
  SET rows_count = (SELECT COUNT(1) FROM initial_grouping);
  SET run_away_stop = run_away_stop + 1;

  CREATE OR REPLACE TEMP TABLE initial_grouping AS
  SELECT ANY_VALUE(arr) arr FROM (
    SELECT ARRAY(SELECT DISTINCT val FROM UNNEST(arr) val ORDER BY val) arr
    FROM (
      SELECT ANY_VALUE(arr1) arr1, ARRAY_CONCAT_AGG(arr) arr    
      FROM (
        SELECT t1.arr arr1, t2.arr arr2, ARRAY(SELECT DISTINCT val FROM UNNEST(ARRAY_CONCAT( t1.arr, t2.arr)) val ORDER BY val) arr 
        FROM initial_grouping t1, initial_grouping t2 
        WHERE (SELECT COUNT(1) FROM UNNEST(t1.arr) val JOIN UNNEST(t2.arr) val USING(val)) > 0
      ) GROUP BY FORMAT('%t', arr1)
    )
  ) GROUP BY FORMAT('%t', arr);

  IF (rows_count = (SELECT COUNT(1) FROM initial_grouping) AND run_away_stop > 1) OR run_away_stop > 10 THEN BREAK; END IF;
END LOOP;

SELECT loan, collateral, final_grouping FROM input 
JOIN (SELECT ROW_NUMBER() OVER() final_grouping, arr FROM initial_grouping) 
ON collateral IN UNNEST(arr) 
ORDER BY loan, collateral; 

上面的脚本产生了下面的结果(我相信这正是你要找的)

Row loan    collateral  final_grouping   
1   L1      C1          1    
2   L2      C1          1    
3   L2      C4          1    
4   L5      C8          3    
5   L6      C8          3    
6   L7      C9          2    
7   L8      C4          1    

请注意:应用于真实数据时 - 确保为 run_away_stop 设置适当的最大值(在上面的脚本中为 10 - 请参阅 LOOP 中的最后一条语句 - 您可能需要增加它以确保转换完成)​​

最后:应用到你的真实表:

1 - 删除 CREATE TEMP TABLE input (...) 声明
2 - 在CREATE TEMP TABLE initial_grouping AS ... 语句中将input 替换为your_project.your_dataset.your_table

【讨论】:

  • 很高兴它对你有用。如果还没有,也考虑投票:o)
【解决方案2】:

您的描述有点模糊,很难判断这是否是所要求的解决方案。尝试更详细地说明您对“属于同一组'路径'然后在组内执行分配”的确切需求。

SELECT Loan, Collateral, COUNT(*) AS Group FROM [TABLE]
GROUP BY Loan, Collateral

您需要更改 tableName [TABLE]

-- 更新

您的演示文稿可能有问题,因为数字不相加。

我尝试在这里进行双重分组,但是 L2 和 C8 周围似乎存在一个问题,这破坏了逻辑,因为这两个值的行为不同。

SELECT * FROM [Tabel]  AS A
LEFT JOIN
(
SELECT [Loan], COUNT(*) AS Rows_Loan FROM [Tabel]
GROUP BY [Loan]
) AS T
ON A.Loan = T.Loan

LEFT JOIN (
SELECT [Collateral], COUNT(*) AS Rows_Collateral FROM [Tabel]
GROUP BY [Collateral]
) AS T2
ON A.Collateral = T2.Collateral

结果:

Loan    Collateral  Loan    Rows_Loan   Collateral  Rows_Collateral
L1      C1          L1      1           C1          2
L2      C1          L2      2           C1          2
L5      C8          L5      1           C8          2
L2      C4          L2      2           C4          1
L6      C8          L6      1           C8          2

【讨论】:

  • 谢谢 Mikhail 和 Gordon,我已经添加了一些细节。我希望它有资格重新开放。
  • 再次感谢 Gordon 提到“grap-walking”,这有助于改进我的一些搜索。此链接似乎是我的问题的答案,但我无法在 BQ 中完全解决...stackoverflow.com/questions/51632251/…
  • 非常感谢您,米哈伊尔·伯利安特!我将在接下来的几天内进行审查。 TBH,查询背后的逻辑需要一些时间来验证。您的输出是正确的,所以我会在接下来的几天内将其标记为正确,但要先验证(不幸的是,您的一些方法有点超出我的控制范围。)非常感谢。
  • 非常感谢米哈伊尔。这成功了。据我所知,这正是我所寻找的。我确实在长“路径”(3k +)中遇到了 BQ 的资源问题,但这可能是我的问题而不是 BQ ......我可能会为 R 提出类似的问题,因为我也一直在尝试解决那里的问题.再次,完全按照我的需要工作......完全正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-13
  • 1970-01-01
  • 1970-01-01
  • 2021-11-24
  • 2022-01-24
  • 1970-01-01
  • 2011-06-28
相关资源
最近更新 更多