sql-server - 如何在非常大的表中查找行之间的配对关系
问题描述
我有一个非常大的表(60m 行),其中包含两列的行:set_id 和 object_id。set_id 用于标识 object_id 组。就我而言,这些 object_ids 可以出现在多个集合中。
例子:
set_id | object_id
1 | 100
1 | 101
1 | 102
2 | 100
2 | 201
3 | 300
4 | 102
4 | 300
5 | 500
我需要的是检索一组至少共享一个 object_id 的 set_id 对列表。每个 set_id 也将与自身配对。对只出现一次(即:(1,2),但不是(2,1))。对于上面的例子:
set_id_A | set_id_B
1 | 1
1 | 2
1 | 4
2 | 2
3 | 3
3 | 4
4 | 4
5 | 5
编写一个查询来实现这一点相当简单。问题是我的解决方案不能很好地扩展。这是我的代码:
-- #original_sets table created
CREATE TABLE #original_sets
(
[set_id] INT,
[object_id] BIGINT
);
-- #original_sets populated here from other data
-- removed
-- index created on table:
CREATE CLUSTERED INDEX cx_original_sets
ON #original_sets ([object_id], [set_id]);
-- code to create the pairs:
SELECT
ck1.[set_id] AS set_id_A,
ck2.[set_id] AS set_id_B
FROM
#original_sets ck1
INNER JOIN
#original_sets ck2
ON ck1.[object_id] = ck2.[object_id]
AND ck1.[set_id] <= ck2.[set_id]
GROUP BY
ck1.[set_id],
ck2.[set_id];
如果 original_sets 表很小甚至中等大小,它会非常快,但是一旦我达到 60m 行,它真的很慢。我最终在 10 小时后取消了它,所以我不确定它是否会完成。
我知道,在这么大的桌子上进行自联接只是在找麻烦。是否有另一种方法可以更好地扩展?谢谢!
编辑1: 可能有助于提高性能的另一件事:在我得到集合对后,我有另一个过程,然后创建包含原始集合相关的所有对象ID的超级集合(参见:传递闭包聚类 http:// sqlblog.com/blogs/davide_mauri/archive/2017/11/12/lateral-thinking-transitive-closure-clustering-with-sql-server-uda-and-json.aspx顶部的图表很好地展示了它)
因为我在这之后才这样做,所以我并不真正关心 set_ids 本身,只关心它们如何将 object_ids 组合在一起。因此可以安全地消除重复集。也许首先这样做是减小表格整体大小的好方法。
编辑2:
新版本尝试减小原始表的大小
-- #original_sets table created
CREATE TABLE #original_sets
(
[set_id] INT,
[object_id] BIGINT
);
-- #original_sets populated here from other data
-- removed
-- index created on table:
CREATE CLUSTERED INDEX cx_original_sets
ON #original_sets ([object_id], [set_id]);
--added this index:
CREATE CLUSTERED INDEX IDX_original_sets
ON #original_sets ([set_id], [object_id]);
-- added this part to identify sets with only one object_id
CREATE TABLE #lonely_sets
(
[set_id] INT PRIMARY KEY
);
INSERT INTO #lonely_sets
SELECT
[set_id]
FROM
#original_sets
GROUP BY
[set_id]
HAVING
COUNT(*) = 1
--then use that data to eliminate duplicate single object sets (see edit 1 for why)
DELETE FROM #original_sets
WHERE set_id IN
(
SELECT
[set_id ]
FROM
#lonely_sets lonely_sets
WHERE
[set_id ] NOT IN
(
SELECT
MIN(original_sets.[set_id ])
FROM
#original_sets original_sets
INNER join #lonely_sets lonely_sets
ON original_sets.set_id = lonely_sets.set_id
GROUP BY
original_sets.[object_id]
)
)
-- then run this
-- code to create the pairs as before:
SELECT
ck1.[set_id] AS set_id_A,
ck2.[set_id] AS set_id_B
FROM
#original_sets ck1
INNER JOIN
#original_sets ck2
ON ck1.[object_id] = ck2.[object_id]
AND ck1.[set_id] <= ck2.[set_id]
GROUP BY
ck1.[set_id],
ck2.[set_id];
额外的工作将 original_set 减少到 ~16m 行。具有 ~1m 唯一 object_ids 和 ~7m 唯一 set_ids。
以下是每组对象的细分:
object_count_per_set | sets_with_that_count
67 32
49 8
42 197
41 120
38 1
37 101
35 16
30 23
29 18
28 109
27 1643
26 382
25 43
24 35
23 8
22 492
21 703
20 339
19 1548
18 2176
17 358
16 1156
15 852
14 1755
13 1845
12 2452
11 3073
10 4570
9 4723
8 9726
7 16178
6 35493
5 81091
4 211305
3 724627
2 5360781
1 789573
所以总体上要处理一个小得多的表,但只花了一个多小时就完成了(受影响的 1,035,212,815 行),它运行起来仍然很慢。
我知道有很多重复的集合可以安全地消除,我只需要一个好的方法来做到这一点。
解决方案
你说你在表中有 60m 行和大约 50m 唯一 set_ids 和 100k 唯一 object_ids。
所以平均每个 object_id 有 600 行。平均而言ck1.[object_id] = ck2.[object_id] AND ck1.[set_id] <= ck2.[set_id]
,每个外部行将匹配 300 行,因此目前您的查询正在生成和聚合大约 180 亿行的内容
5000 万个集合 id 和 6000 万行意味着大多数集合只会与自己配对,
作为第一次尝试,我想用一个简单的方法找到这些保证不成对的集合,GROUP BY ... COUNT
然后用三角形自连接将它们排除在更昂贵的部分之外。
如果此查询仍然太慢,请提供有关#paired_sets
行数和不同数以及其中最大的大小(行数)方面object_id
的set_id
特征object_id
的信息
CREATE TABLE #lonely_sets
(
[set_id] INT PRIMARY KEY
);
INSERT INTO #lonely_sets
SELECT [set_id]
FROM #original_sets
GROUP BY [set_id]
HAVING COUNT(*) = 1;
CREATE TABLE #paired_sets
(
[set_id] INT,
[object_id] INT,
PRIMARY KEY ([object_id], [set_id])
);
INSERT INTO #paired_sets
SELECT [set_id], [object_id]
FROM #original_sets
WHERE [set_id] NOT IN (SELECT ls.set_id FROM #lonely_sets ls);
--Final Select
SELECT [set_id] AS set_id_A, [set_id] AS set_id_B
FROM #lonely_sets
UNION ALL
SELECT
ck1.[set_id] AS set_id_A,
ck2.[set_id] AS set_id_B
FROM
#paired_sets ck1
INNER JOIN
#paired_sets ck2
ON ck1.[object_id] = ck2.[object_id]
AND ck1.[set_id] <= ck2.[set_id]
GROUP BY
ck1.[set_id],
ck2.[set_id];
推荐阅读
- intellij-idea - 如何清除所有 IntelliJ IDEA 窗口中的所有内容?
- vuex - Vuex 无法停止 setInteval
- azure-data-factory-2 - Azure 仪表板 - 指标:ADF 管道限制
- python - 赋值前引用的局部变量“all_inf”
- logging - 如何在 logback 配置中为不同的 appender 共享相同的文件
- android - Android 有没有办法检测到我的应用程序正在被卸载?
- javascript - 按键排序地图
- algorithm - What are overlapping subproblems in Dynamic Programming (DP)?
- c - 在 SRAM 中为 SRAM 衰减实验保留空间 (C ; AVR atmega644p ; Atmel Studio 7)
- java - Eclipse 无法安装 Jaspersoft Studio 6.15