从链接的值中查找唯一用户

时间:2017-11-17 18:37:02

标签: mysql sql sql-server google-bigquery

我的表格中有值。

id | val1  | val2 
--------------------
1  |   e1  |   m1
2  |   e1  |   m2
3  |   e2  |   m2
4  |   e3  |   m1
5  |   e4  |   m3
6  |   e5  |   m3
7  |   e5  |   m4
8  |   e4  |   m5

由此,我必须恢复这样的独特用户,并为他们提供唯一的ID来识别。

User1 - > (val1:e1,e2,e3 | val2:m1,m2)

e1< - > m1,e1 - > m2,m1 - < - > e3,e2 - < - > m2(< - >表示链接)。

e1连接到m1。

e1连接到m2。

m2连接到e2。

所以e1,m1连接到e2。

同样,我们发现e1,e2,e3,m1,m2都是相互关联的。我们需要识别这些链条。

User2 - > (val1:e4,e5 | val2:m3,m4,m5)

我写了两个查询,分别是我的val1分组,然后分别用val2分组,并用代码(Java)加入它们。

我希望在MySQL / BigQuery查询中直接执行此操作,因为我们正在构建一些报告。

这可以在一个查询中使用吗?请帮忙。

谢谢。

更新:

期望的输出 -

[
 { 
   id : user1,
   val1 : [e1, e2, e3],
   val2 : [m1, m2]
 },
 { 
   id : user2,
   val1 : [e4, e5],
   val2 : [m3, m4, m5]
 }
]

id | val1  | val2 | UUID
------------------------
1  |   e1  |   m1 | u1
2  |   e1  |   m2 | u1
3  |   e2  |   m2 | u1
4  |   e3  |   m1 | u1
5  |   e4  |   m3 | u2
6  |   e5  |   m3 | u2
7  |   e5  |   m4 | u2
8  |   e4  |   m5 | u2

为简单起见,假设val1和val2的值是节点,并且如果存在于同一行则连接。

表的行形成图形(user1,user2),我们需要识别这些图形。

1 个答案:

答案 0 :(得分:1)

希望能够选择使用纯BigQuery(标准SQL)来解决您的任务

   

先决条件/假设:源数据位于sandbox.temp.id1_id2_pairs中 您应该用自己的替换它,或者如果您想用问题中的虚拟数据进行测试 - 您可以创建如下表格(当然用您自己的sandbox.temp替换project.dataset

enter image description here
确保您设置了相应的目标表

注意:你可以在这个答案的底部找到所有相应的查询(作为文本),但是现在我用截图来说明我的答案 - 所以所有内容都显示 - 查询,结果和使用选项

因此,将有三个步骤:

第1步 - 初始化

在这里,我们只根据与id2的连接进行id1的初始分组:
enter image description here

正如您在此处所见 - 我们基于通过id2的简单单级连接创建了具有相应连接的所有id1值的列表

输出表格为sandbox.temp.groups

第2步 - 分组迭代

在每次迭代中,我们将根据已建立的群体丰富分组 查询源是上一步(sandbox.temp.groups)的输出表,而Destination是与覆盖相同的表(sandbox.temp.groups

enter image description here

我们将继续迭代,直到找到的组的数量与之前的迭代相同

enter image description here

注意:您可以打开两个BigQuery Web UI标签(如上图所示)并且不更改任何代码只运行分组,然后反复检查直到迭代收敛

(对于我在先决条件部分中使用的特定数据 - 我有三次迭代 - 第一次迭代产生了5个用户,第二次迭代产生了3个用户,第三次迭代再次产生了3个用户 - 这表明我们完成了迭代。

当然,在现实生活中 - 迭代次数可能超过三次 - 所以我们需要某种自动化(参见答案底部的相​​应部分)。

第3步 - 最终分组
当id1分组完成时 - 我们可以为id2添加最终分组

enter image description here

最终结果现在位于sandbox.temp.users

使用的查询(不要忘记根据上述逻辑和屏幕截图设置各自的目标表并在需要时覆盖):

先决条件:

#standardSQL
SELECT 1 id, 'e1' id1, 'm1' id2 UNION ALL
SELECT 2,    'e1',     'm2' UNION ALL
SELECT 3,    'e2',     'm2' UNION ALL
SELECT 4,    'e3',     'm1' UNION ALL
SELECT 5,    'e4',     'm3' UNION ALL
SELECT 6,    'e5',     'm3' UNION ALL
SELECT 7,    'e5',     'm4' UNION ALL
SELECT 8,    'e4',     'm5' UNION ALL
SELECT 9,    'e6',     'm6' UNION ALL
SELECT 9,    'e7',     'm7' UNION ALL
SELECT 9,    'e2',     'm6' UNION ALL
SELECT 888,  'e4',     'm55'   

第1步

#standardSQL
WITH `yourTable` AS (select * from `sandbox.temp.id1_id2_pairs`
), x1 AS (SELECT id1, STRING_AGG(id2) id2s FROM `yourTable` GROUP BY id1
), x2 AS (SELECT id2, STRING_AGG(id1) id1s FROM `yourTable` GROUP BY id2 
), x3 AS (
  SELECT id, (SELECT STRING_AGG(i ORDER BY i) FROM (
    SELECT DISTINCT i FROM UNNEST(SPLIT(id1s)) i)) grp
  FROM (
    SELECT x1.id1 id, STRING_AGG((id1s)) id1s FROM x1 CROSS JOIN x2
    WHERE EXISTS (SELECT y FROM UNNEST(SPLIT(id1s)) y WHERE x1.id1 = y)
    GROUP BY id1) 
)
SELECT * FROM x3 

第2步 - 分组

#standardSQL
WITH x3 AS (select * from `sandbox.temp.groups`)
SELECT id, (SELECT STRING_AGG(i ORDER BY i) FROM (
  SELECT DISTINCT i FROM UNNEST(SPLIT(grp)) i)) grp
FROM (
  SELECT a.id, STRING_AGG(b.grp) grp FROM x3 a CROSS JOIN x3 b 
  WHERE EXISTS (SELECT y FROM UNNEST(SPLIT(b.grp)) y WHERE a.id = y)
  GROUP BY a.id )   

第2步 - 检查

#standardSQL
SELECT COUNT(DISTINCT grp) users FROM `sandbox.temp.groups` 

第3步

#standardSQL
WITH `yourTable` AS (select * from `sandbox.temp.id1_id2_pairs`
), x1 AS (SELECT id1, STRING_AGG(id2) id2s FROM `yourTable` GROUP BY id1 
), x3 as (select * from `sandbox.temp.groups`
), f  AS (SELECT DISTINCT grp FROM x3 ORDER BY grp
)
SELECT ROW_NUMBER() OVER() id, grp id1, 
  (SELECT STRING_AGG(i ORDER BY i) FROM (SELECT DISTINCT i FROM UNNEST(SPLIT(id2)) i)) id2
FROM (
  SELECT grp, STRING_AGG(id2s) id2 FROM f 
  CROSS JOIN x1 WHERE EXISTS (SELECT y FROM UNNEST(SPLIT(f.grp)) y WHERE id1 = y)
  GROUP BY grp)

<强>自动化
当然,如果迭代快速收敛,上面的“过程”可以手动执行 - 所以最终会有10-20次运行。但在更现实的情况下,您可以使用您选择的任何client轻松实现此自动化