从链接值中查找唯一用户

Posted

技术标签:

【中文标题】从链接值中查找唯一用户【英文标题】:Finding unique users from linked values 【发布时间】:2017-11-17 18:37:02 【问题描述】:

我的表格中有值。

编号 | val1 | val2 -------------------- 1 | e1 |米1 2 | e1 |平方米 3 | e2 |平方米 4 | e3 |米1 5 | e4 |立方米 6 | e5 |立方米 7 | e5 | m4 8 | e4 |米5

由此,我必须恢复这样的唯一用户,并给他们一个唯一的 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]
 
]

编号 | val1 | val2 | UUID ---------------------- 1 | e1 |平方米 | u1 2 | e1 |平方米 | u1 3 | e2 |平方米 | u1 4 | e3 |平方米 | u1 5 | e4 |立方米 | u2 6 | e5 |立方米 | u2 7 | e5 |平方米 | u2 8 | e4 |米5 | u2

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

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

【问题讨论】:

不清楚 - 将 val1,2 分组到不同用户的逻辑是什么?显示你提到的你已经完成的查询 - 所以它可能有助于我们理解逻辑 用解释更新了问题。我的逻辑是半代码半查询将它们分组 明白了——现在说得通了 也许这对 Mikhail 来说是有意义的,但我仍然对这里所需的输出感到困惑。你能像输入一样以表格形式显示它吗? 更新了更多信息。请检查 【参考方案1】:

想要加入使用纯 BigQuery(标准 SQL)解决您的任务的选项

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

确保您设置了相应的目标表

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

因此,将分为三个步骤:

第 1 步 - 初始化

这里,我们只是根据与 id2 的连接对 id1 进行初始分组:

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

输出表为sandbox.temp.groups

第 2 步 - 分组迭代

在每次迭代中,我们将根据已建立的组来丰富分组。 查询的来源是上一步的输出表(sandbox.temp.groups),目标是同一个表(sandbox.temp.groups),覆盖

我们将继续迭代,直到找到的组数与上一次迭代中的相同

注意:您可以只打开两个 BigQuery Web UI 选项卡(如上所示),无需更改任何代码,只需运行 Grouping,然后反复检查直到迭代收敛

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

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

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

最终结果现在在sandbox.temp.users 表中

Used Queries(不要忘记根据上述逻辑和屏幕截图设置相应的目标表并在需要时覆盖):

先决条件:

#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'   

第一步

#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 轻松自动化此操作

【讨论】:

非常感谢。我现在正在尝试将此问题扩展到更多类型的值(id1、id2 到 id1、id2、id3、id4、id5 ...)我将在 600 万行上运行它。有什么建议吗? 您是否能够在您的真实数据上使用/实施上述方法?就像只使​​用两列数据和 600 万行一样。在我们更进一步之前分享您的结果 我能够使用两列将其应用于大约 1500 行。完成完整数据集后将更新 更新:在 10,000 行上运行此查询。它进行了 3 次迭代,最终解决了 295 个唯一用户。在手动检查时,它工作正常,但空值存在严重问题。如果任何一个值(id1 或 id2)在任何行上为空,它将合并两个不同的用户。基本上,它也基于空值进行合并。我们可以防止这种情况发生吗? 在第 1 步中将 WITH `yourTable` AS (select * from `sandbox.temp.id1_id2_pairs`) 替换为 WITH `yourTable` AS (select * from `sandbox.temp.id1_id2_pairs` WHERE NOT(id1 IS NULL OR id2 IS NULL),并且很可能与第 3 步相同

以上是关于从链接值中查找唯一用户的主要内容,如果未能解决你的问题,请参考以下文章

“\/”文本显示在我的 JSON 链接值中 [重复]

无法从 PHP URL 重写生成的值中获取值

Rubocop 唯一性验证应该使用唯一索引,在从某些特定值开始的值中

HTML 电话链接可以接受值中的空格吗?

如何从其 String 值中查找 Java 枚举?

MySQL JSON:如何从键值中查找对象