计算未出现在另一个表中的元素数量的最佳方法
Posted
技术标签:
【中文标题】计算未出现在另一个表中的元素数量的最佳方法【英文标题】:Best way to calculate the number of element that NOT appear in another table 【发布时间】:2020-06-22 16:06:52 【问题描述】:考虑以下情况
TableA TableB
+------------+----------+----------+----------+ +---------+
|Column1 | Column2 | Column3 | Column4 | | entB |
+-----------------------+----------+----------+ +---------+
| zzzxxxx | NULL | NULL | zzzyyyy | | zzzxxxx |
+------------+----------+----------+----------+ +---------+
| zzzqqqq | NULL | SomeText | NULL | | zzzyyyy |
+------------+----------+----------+----------+ +---------+
| NULL | zzzxxxx | zzzxxx | NULL | | zzzwwww |
+------------+----------+----------+----------+ +---------+
| zzzyyyy | zzzyyyy | zzzwwww | SomeText |
+------------+----------+----------+----------+
其中 z,y,x = 1-9 并且某些文本可以包含任何数字或字母 - 表 B 在 ent B 列中不能有空值。
我需要找到表 A 中不在表 B 中的值的总和。表 A 中的列不包含唯一(不同)值,并且可能为空。
我的第一次尝试是以下和查询
$"select count(1) from " +
$"(" +
$" select distinct Column1 from TableA where Column1 not in (select entB from TableB)" +
$" union" +
$" select distinct Column2 from TableA where Column2 not in (select entB from TableB)" +
$" union" +
$" select distinct Column3 from TableA where Column3 not in (select entB from TableB)" +
$" union" +
$" select distinct Column4 from TableA where Column4 not in (select entB from TableB)" +
$") as t"
这很好,直到我不得不在具有 ~70000000 行的 TableA 和 ~100000 行的 TableB 上测试此查询,其中该查询的执行时间太长。我正在寻找一种减少时间的方法。
我读到使用Distinct
和Union
是一种简单的破坏性能的方法,所以我想尝试这样的方法
SELECT Column1
FROM TableA a
WHERE NOT EXISTS (SELECT 1 FROM TableB b WHERE a.Column1 = b.entB and a.Column1 is not null )
and Column1 is not null)
获取结果,将其保存在 DataTable 中,然后对其他 3 列重复相同的查询并合并结果,检查内存中的重复项。
不知道有没有更好的解决方案?
编辑:我已编辑表格以更好地显示我的数据的外观。在示例中,我希望结果值为“2”,因为 TableB 中不存在 2 个值(SomeText 和 zzzqqqq)
【问题讨论】:
您似乎有几个 column2。因此,这个问题是不可理解的。见:Why should I provide an MCRE for what seems to me to be a very simple SQL query? @Strawberry 在尝试格式化问题时显然是一个错字...现在已修复 这种问题是架构设计不佳的高度症状。考虑是否有修改的余地。 使用左外连接。项目数是 DefaultEmpty。请参阅:docs.microsoft.com/en-us/dotnet/csharp/linq/… @jdweng DefaultEmpty 应该在我执行了 4 个查询之后在我的代码中使用? 【参考方案1】:一旦我们克服了所有关于 TableA
未标准化的抱怨,这并不难做到。
我猜您想计算 TableA 中与 TableB
不匹配的四列的值。如果您想要更复杂的东西,请花点时间弄清楚如何非常准确地描述它。
从一个子查询开始,它为您提供TableA
中的值以进行比较。因为我们使用UNION
而不是UNION ALL
,所以我们免费获得SELECT DISTINCT
。 (SQL 操作集合。)
SELECT Column1 AS ent FROM TableA
UNION
SELECT Column2 AS ent FROM TableA
UNION
SELECT Column3 AS ent FROM TableA
UNION
SELECT Column4 AS ent FROM TableA
然后,使用LEFT JOIN .... IS NULL
模式来获取不匹配的项目。
SELECT COUNT(*) number_of_unmatched_items
FROM ( SELECT Column1 AS ent FROM TableA
UNION
SELECT Column2 AS ent FROM TableA
UNION
SELECT Column3 AS ent FROM TableA
UNION
SELECT Column4 AS ent FROM TableA
) a
LEFT JOIN TableB b ON a.ent = b.entB
WHERE b.entB IS NULL
WHERE...IS NULL
从您的子查询中提取左连接中未满足 ON
条件的行。
为了使这个速度相当快,我认为您需要在涉及此的TableA
的每一列上建立单独的索引,以及TableB
中的entB
上的索引。但是您需要尝试一下,如果它仍然不能满足您的性能需求,请执行EXPLAIN
。
除非运行 mysql 的机器内存不足,否则 MySQL 应该可以相当有效地处理这些东西。
专业提示:您已经知道这一点。像TableA
这样的非规范化表确实会影响查询性能。
【讨论】:
endB
最好是TableB
中某个索引的开头。尽管如此,查询可能需要几个小时,因为它需要对 70M 行进行 4 次表扫描;加上 4*70M 查找到较小的表。
我想让您知道您的解决方案,至少在我的环境中是迄今为止最慢的。我提出的第一个查询需要大约 5 分钟才能执行,而不存在的版本需要大约 2 分钟半。您的查询需要 27 分钟才能完成。
建议将UNION
更改为UNION DISTINCT
,因为查询中包含的算法依赖于去重。以上是关于计算未出现在另一个表中的元素数量的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章