2015-08-07 2 views
1

Мне нужно уметь отличать результаты двух запросов, показывая строки, которые находятся в «старом» наборе, но не находятся в «новом» ... и затем показывают строки, которые находятся в «новом», но не старый.Эффективно получить diff от большого набора данных?

Прямо сейчас, я вытягиваю результаты в массив, а затем делаю массив_дифф(). Но, я сталкиваюсь с некоторыми проблемами ресурсов и времени, поскольку наборы близки к 1 миллиону строк.

Схема одинакова в обоих наборах результатов (запрет номера setId и номера автоинкремента таблицы), поэтому я предполагаю, что есть хороший способ сделать это непосредственно в 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 и 2. SETID

Таким образом, результат должен дифф (для примера) показывают:

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 хороши для меня.

ответ

0

То, что мы закончили, добавляло колонку контрольной суммы к разным таблицам. Таким образом, вместо того, чтобы выбирать несколько столбцов для сравнения, diff может быть выполнен против одного столбца (значение контрольной суммы).

Значение контрольной суммы было простым хешем md5 сериализованного массива, который содержал столбцы, подлежащие разграничению. Так ... это было, как это в PHP:

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

Это $ checksumValue затем будет вставлена ​​/ обновлены в таблицах, а затем мы можем легко сделать стыки/союзы и т.д. на одной колонке, чтобы найти различия. Это в конечном итоге выглядеть примерно так:

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); 

Это работает достаточно быстро для моих целей, по крайней мере сейчас :-)

0

Вы можете использовать exists или not exists, чтобы получить строки, которые находятся в обоих или только 1 комплект.

Пользователи в наборе 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 
+0

Going дать этому выстрел в эти выходные. Спасибо за быстрый ответ. –

Смежные вопросы