【问题标题】:How to create a single ID across separate entity IDs based on shared attributes using SQL?如何使用 SQL 基于共享属性跨单独的实体 ID 创建单个 ID?
【发布时间】:2019-11-29 19:28:53
【问题描述】:

背景:我有一个 SQL 表,其中包含 acct_ids 和 component_ids,表示每个帐户使用的组件部分。账户可以有多个组件,同一个组件可以被多个账户使用。

目标:我想根据共享的 component_ids 对 acct_ids 进行“重复数据删除”;因此,如果任何 acct_id 与任何其他 acct_id 共享任何 component_id,请将它们组合成一个新 id。我正在使用 BigQuery SQL 来执行此操作。

这是数据表的示例:

+---------+--------------+
| acct_id | component_id |
+---------+--------------+
|       1 | A            |
|       1 | B            |
|       1 | C            |
|       2 | C            |
|       2 | D            |
|       2 | E            |
|       3 | G            |
|       3 | E            |
|       3 | F            |
|       4 | H            |
|       4 | I            |
|       5 | H            |
|       5 | J            |
+---------+--------------+

例如,acct_ids 1 和 2 共享 component_id C,acct_ids 2 和 3 共享 component_id E,因此所有这 3 个 acct_ids 都应该用一个共享的 id (new_id = 1) 进行标记。同样,acct_ids 4 和 5 共享 component_id H,所以这两个 acct_ids 都应该用一个共享的 id (new_id = 2) 进行标记。

对于上面的示例数据,所需的输出是:

+---------+--------------+--------+
| acct_id | component_id | new_id |
+---------+--------------+--------+
|       1 | A            |      1 |
|       1 | B            |      1 |
|       1 | C            |      1 |
|       2 | C            |      1 |
|       2 | D            |      1 |
|       2 | E            |      1 |
|       3 | G            |      1 |
|       3 | E            |      1 |
|       3 | F            |      1 |
|       4 | H            |      2 |
|       4 | I            |      2 |
|       5 | H            |      2 |
|       5 | J            |      2 |
+---------+--------------+--------+

我一直在想办法解决这个问题 - 或许可以从结合 FULL OUTER JOIN 的方法开始,但我还没有能够构建一个有凝聚力的查询。

有什么建议吗?

【问题讨论】:

    标签: sql google-bigquery duplicates primary-key


    【解决方案1】:

    你需要使用语法

    CASE <field_name> WHEN <value> THEN <new_walue>
    

    我确实在 BigQuery 中对我的数据进行了尝试,以下对我有用:

    SELECT 
    CASE component_id
    WHEN 'A' THEN '1'
    WHEN 'B' THEN '1'
    WHEN 'J' THEN '2'
    ELSE '0'
    END AS new_component_id, *
    FROM `<project>.<dataset>.<table>` LIMIT 1000
    

    您可能也有兴趣阅读documentation for conditional expressions in Standard SQL

    【讨论】:

      【解决方案2】:

      以下是 BigQuery 标准 SQL

      DECLARE rows_count, run_away_stop INT64 DEFAULT 0;
      
      CREATE TEMP TABLE ttt AS 
      SELECT ARRAY_AGG(component_id ORDER BY component_id) arr 
      FROM `project.dataset.your_table`
      GROUP BY acct_id;
      
      LOOP
        SET rows_count = (SELECT COUNT(1) FROM ttt);
        SET run_away_stop = run_away_stop + 1;
      
        CREATE OR REPLACE TEMP TABLE ttt 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 ttt t1, ttt 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 ttt) AND run_away_stop > 1) OR run_away_stop > 10 THEN BREAK; END IF;
      END LOOP;
      
      SELECT acct_id, component_id, new_id FROM `project.dataset.your_table` 
      JOIN (SELECT ROW_NUMBER() OVER() new_id, arr FROM ttt) 
      ON component_id IN UNNEST(arr);
      

      如您所见 - 上面使用的是最近引入的 scripting 功能,如果应用到您的问题中的样本数据,最终输出是

      Row acct_id component_id    new_id   
      1   1       A               2    
      2   1       B               2    
      3   1       C               2    
      4   2       C               2    
      5   2       D               2    
      6   2       E               2    
      7   3       E               2    
      8   3       F               2    
      9   3       G               2    
      10  4       H               1    
      11  4       I               1    
      12  5       H               1    
      13  5       J               1    
      

      应用于真实数据时 - 确保为 run_away_stop 设置适当的最大值(在上面的脚本中为 10 - 请参阅 LOOP 中的最后一条语句)

      顺便说一句,您可以使用下面的“操场”来测试、玩上面的游戏,它模仿您问题中的样本数据

      DECLARE rows_count, run_away_stop INT64 DEFAULT 0;
      
      CREATE TEMP TABLE input AS (
        SELECT 1 acct_id, 'A' component_id UNION ALL
        SELECT 1, 'B' UNION ALL
        SELECT 1, 'C' UNION ALL
        SELECT 2, 'C' UNION ALL
        SELECT 2, 'D' UNION ALL
        SELECT 2, 'E' UNION ALL
        SELECT 3, 'G' UNION ALL
        SELECT 3, 'E' UNION ALL
        SELECT 3, 'F' UNION ALL
        SELECT 4, 'H' UNION ALL
        SELECT 4, 'I' UNION ALL
        SELECT 5, 'H' UNION ALL
        SELECT 5, 'J' 
      );
      
      CREATE TEMP TABLE ttt AS 
      SELECT ARRAY_AGG(component_id ORDER BY component_id) arr 
      FROM input
      GROUP BY acct_id;
      
      LOOP
        SET rows_count = (SELECT COUNT(1) FROM ttt);
        SET run_away_stop = run_away_stop + 1;
      
        CREATE OR REPLACE TEMP TABLE ttt 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 ttt t1, ttt 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 ttt) AND run_away_stop > 1) OR run_away_stop > 10 THEN BREAK; END IF;
      END LOOP;
      
      SELECT acct_id, component_id, new_id FROM input 
      JOIN (SELECT ROW_NUMBER() OVER() new_id, arr FROM ttt) 
      ON component_id IN UNNEST(arr) 
      ORDER BY acct_id, component_id;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-24
        • 2020-12-16
        • 2023-01-04
        • 2021-02-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多