高效获取大数据集的差异?

Efficiently get diff of large data set?

我需要能够区分两个查询的结果,显示 "old" 集中但不在 "new"... 中的行,然后显示这些行在 "new" 集合中但不在旧集合中。

现在,我将结果拉入一个数组,然后执行 array_diff()。但是,我遇到了一些资源和时间问题,因为每个集合接近 100 万行。

两个结果集中的架构相同(除了 setId 编号和 table 的自动递增编号),所以我认为有一个很好的方法可以直接在 MySQL 中执行此操作。 .. 但我没有找到方法。

Example Table Schema:
rowId,setId,userId,name

Example Data:
    1,1,user1,John
    2,1,user2,Sally
    3,1,user3,Tom
    4,2,user1,John
    5,2,user2,Thomas
    6,2,user4,Frank

我需要做的是找出 setId 1 和 setId 2 之间的 adds/deletes。

所以,diff 的结果应该(例如)显示:

Rows that are in both setId1 and setId2
    1,1,user1,John

Rows that are in setId 1 but not in setId2
    2,1,user2,Sally
    3,1,user3,Tom

Rows that are in setId 2 but not in setId1
    5,2,user2,Thomas
    6,2,user4,Frank

我想这就是所有的细节。我想我的例子是正确的。任何帮助,将不胜感激。 MySQL 或 PHP 中的解决方案对我来说很好。

您可以使用 existsnot exists 来获取在这两个集合中或仅在一组中的行。

第 1 组但不第 2 组的用户(只需翻转相反的表格):

select * from set1 s1
where set_id = 1
and not exists (
  select count(*) from set1 s2
  where s1.user1 = s2.user1
)

两个集合中的用户

select * from set2 s2
where set_id = 2  
and exists (
    select 1 from set1 s1
    where s1.setId = 1
    and s2.user1 = s1.user1
)

如果您只想在两个组中使用不同的用户,那么 group by user1:

select min(rowId), user1 from set1
where set_id in (1,2)
group by user1
having count(distinct set_id) = 2

或组中的用户而不是其他用户

select min(rowId), user1 from set1
where set_id in (1,2)
group by user1
having count(case when set_id <> 1 then 1 end) = 0

我们最终做的是向需要区分的必要表添加校验和列。这样,不必 select 多列进行比较,差异可以针对单个列(校验和值)进行。

校验和值是一个序列化数组的简单 md5 散列,其中包含要进行比较的列。所以...在PHP中是这样的:

$checksumString = serialize($arrayOfColumnValues);
$checksumValue = md5($checksumString);

然后 $checksumValue 将 inserted/updated 放入表中,然后我们可以更轻松地在单个列上执行 joins/unions 等以查找差异。它最终看起来像这样:

SELECT  i.id, i.checksumvalue
FROM    SAMPLE_TABLE_I i
WHERE   i.checksumvalue not in(select checksumvalue from SAMPLE_TABLE_II)
UNION ALL
SELECT  ii.id, ii.checksumvalue
FROM    SAMPLE_TABLE_II ii
WHERE   ii.checksumvalue not in(select checksumvalue from SAMPLE_TABLE_I);

这对我来说运行得足够快了,至少现在是这样:-)