Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Базы";
Текущий архив: 2003.10.16;
Скачать: [xml.tar.bz2];

Вниз

Удалить дубли в таблице   Найти похожие ветки 

 
Vlad   (2003-09-25 10:26) [0]

Доброго всем времени суток.
Есть таблица, в которой основные поля: ID (integer,уникальное), Код(integer), Дата(Date), Наименование(Varchar).
Помимо них есть еще штук 15 полей.
Нужно из таблицы удалить дублирующие записи, так, чтобы связка Код-Дата-Наименование стала уникальной.
Проблема в том что в таблице порядка 12 млн записей.
Кто нибудь подскажет наиболее быстрый алгоритм решения задачи, выполнение которого было бы <= 24 часа ?
Все перепробованные мной способы, по предварительным подсчетам, выполнялись бы как минимум трое суток.
Спасибо.


 
DenK_vrtz   (2003-09-25 10:37) [1]

Думаешь реально на таких объемах быстрее сделать?
А с данными остальных 15 полей как быть? Они одинаковы?


 
Johnmen   (2003-09-25 10:38) [2]

Стандартно
DELETE FROM Table A
WHERE 1<(SELECT COUNT(*) FROM Table B
WHERE (A.Код=B.Код) AND
(A.Дата=B.Дата) AND
(A.Наименование=B.Наименование))


 
Vlad   (2003-09-25 10:47) [3]

>DenK_vrtz © (25.09.03 10:37) [1]
Данные остальных 15 полей во внимание не принимать. Главное чтоб вышеуказанная связка осталась уникальной.
>Johnmen © (25.09.03 10:38) [2]
Интересное решение. Что-то я с утра не соображу как эта хрень работать должна ? Т.е. мне еще одну такую же таблицу создать, куда перелить все данные из исходной чтоли ? :)


 
Vlad   (2003-09-25 10:49) [4]

>Johnmen © (25.09.03 10:38) [2]
Все понял.
Туплю, знаете ли с утра :)
Спасибо.


 
DenK_vrtz   (2003-09-25 10:56) [5]

Johnmen © (25.09.03 10:38) [2], как думаете, на вскидку, времени сколько займет обработать выше указанный объем?


 
Vlad   (2003-09-25 11:02) [6]

Я время пока не прикидывал, но дома попробую на 100 тыс. записях такой запросик. Если минут за 10-15 отработает, то устроит.


 
Vlad   (2003-09-25 11:03) [7]

Правда есть сомнения.... Count по определению медленно работает.


 
DenK_vrtz   (2003-09-25 11:06) [8]

Vlad ©, результатики эксперимента выложи! ОК?!


 
Johnmen   (2003-09-25 11:09) [9]

>DenK_vrtz © (25.09.03 10:56)

Даже навскидку не представляю :)

>Vlad © (25.09.03 11:03)

Для ускорения стоит поэкспериментировать :
1. Создать индексы на Код, Дата, Наименование
2. Создать составной индекс на Код, Дата, Наименование

О результатах - доложить лично, в этой конференции !
:)))


 
Romkin   (2003-09-25 11:10) [10]

В IB есть singular
Я бы процедуру написал...


 
Vlad   (2003-09-25 11:11) [11]

Индексы есть все, кроме составного. Попробую составить составной :)
>О результатах - доложить лично, в этой конференции !
Так точно !


 
Johnmen   (2003-09-25 11:27) [12]

>Romkin © (25.09.03 11:10)

Да, можно и так попробовать
DELETE FROM Table A
WHERE NOT SINGULAR (SELECT * FROM Table B
WHERE (A.Код=B.Код) AND
(A.Дата=B.Дата) AND
(A.Наименование=B.Наименование))


Можно еще ч.-н. придумать с ХП.


 
Sandman25   (2003-09-25 11:28) [13]

[2] Johnmen © (25.09.03 10:38)
>Стандартно

Informix ругается, что "Cannot modify table or view used in subquery".
Это ANSI SQL?


 
Vlad   (2003-09-25 11:35) [14]

А в Yaffil SINGULAR прокатит ?


 
Johnmen   (2003-09-25 11:58) [15]

>Sandman25 © (25.09.03 11:28)

Стандартно для IB и клонов, как минимум.
А не идет скорее всего потому, что это работает на версионниках, а не на блокировочниках. Может быть еще связано с алгоритмами оптимизации запросов, а они разные...


 
Sandman25   (2003-09-25 12:10) [16]

[15] Johnmen © (25.09.03 11:58)

Я думаю, проблема в неоднозначности - конечный результат зависит от того, "с какого конца" таблицы началось выполнение запроса. При одной стратегии останется первая запись, при другой - последняя. То есть нарушается реляционность (независимость от расположения в таблице), происходит переход к навигационному подходу.


 
kaif   (2003-09-25 14:11) [17]

2 Johnmen © (25.09.03 10:38) [2]

DELETE FROM Table A
WHERE 1<(SELECT COUNT(*) FROM Table B
WHERE (A.Код=B.Код) AND
(A.Дата=B.Дата) AND
(A.Наименование=B.Наименование))


Я боюсь, что этот запрос удалит все записи, которые попадаются неоднократно. А человеку скорее всего нужно удалить избыточные дубликаты, оставив по одной записи от имеющихся дубликатов. Нарушение реляционности конечно требуется, так как неясно, каким экземплярам записей отдать предпочтение.

Так что я боюсь, что это решение ошибочное. Я бы не рискнул применить его на 12 млн. А скорость должна оказаться приемлемой, так как вложенный запрос IB скорее всего выполнит 1 раз.
Но еще раз повторюсь, по-моему тут другая задача.


 
Vlad   (2003-09-25 14:14) [18]

>kaif © (25.09.03 14:11) [17]
Ничего, у меня бэкап есть :)


 
kaif   (2003-09-25 14:19) [19]

Я бы сделал так:
1. добавил бы 1 поле типа INTEGER DEFAULT 0.
2. создал бы неуникальный составной индекс на 3 поля.
3. написал бы хранимую процедуру примерно такую:
обхожу таблицу в порядке этого индекса и вставляю 1 каждый
раз, как только значение трех полей повторяется.
4. Проверяю на глаз, все ли правильно получилось.
5. Удаляю все записи одним DELETE, в которых это поле = 1
6. Удаляю поле.

Транзакцию подтверждаю после каждого действия.
Если процент дубликатов небольшой, то многоверсионный мезханизм не должен заметно увеличить файл. Если дубликатов много - тогда не знаю.


 
Vlad   (2003-09-25 14:23) [20]

>kaif © (25.09.03 14:11) [17]
Ты ошибаешься. Удалит именно избыточные.
И вложенный запрос будет выполнен ровно столько раз сколько записей в базе.


 
Johnmen   (2003-09-25 14:25) [21]

>kaif © (25.09.03 14:11)

Нет. Запрос отработает именно так, как хотел автор.
Вложенный запрос отработает столько раз, сколько записей в таблице.


 
kaif   (2003-09-25 14:26) [22]

Процедура, отмечающая дубликаты:

CREATE PROCEDURE AAA
AS
DECLARE VARIABLE ID INTEGER;
DECLARE VARIABLE CODE INTEGER;
DECLARE VARIABLE DATA DATE;
DECLARE VARIABLE NAME VARCHAR(100);
DECLARE VARIABLE LAST_CODE INTEGER;
DECLARE VARIABLE LAST_DATA DATE;
DECLARE VARIABLE LAST_NAME VARCHAR(100);
BEGIN
for select id, code, data, name
from mytable
order by code, data, name //индекс
into :id, :code, :data, :name
do
begin
if ((code = last_code) and
(data = last_data) and
(name = last_name)) then
update mytable set u_field = 1 where id = :id;

last_code = code;
last_data= data;
last_name = name;
end
END


 
kaif   (2003-09-25 14:30) [23]

2 Johnmen © (25.09.03 14:25) [21]

Да, действительно. Извиняюсь. Я проглядел, что в условии позапроса идет сравнение с полями таблицы основного запроса.

Но тогда он выполнится 12 млн раз!

Я ошибся из-за того, что я никогда не применяю таких запросов (со сравнением полей таблиц запроса и подзапроса)

Дико извиняюсь.


 
Vlad   (2003-09-25 14:31) [24]

>kaif © (25.09.03 14:26) [22]
Дык пробовал я такое уже. С индексами.
160 часов - к бабке не ходи !


 
Johnmen   (2003-09-25 14:38) [25]

>kaif © (25.09.03 14:30)

Да перестань извиняться. Со всеми бывает :)
Я могу еще и не такое просмотреть...:)))


 
kaif   (2003-09-25 14:43) [26]

2 Vlad © (25.09.03 14:31) [24]
Может быть скопировать все записи, имеющие дубликаты в отдельную таблицу с аналогичной структурой?
Затем убить все записи в первой таблице, используя какой-нибудь неправильный запрос (который я имел в виду) с однократным поздапросом.
Вторую таблицу отфильтровать методом, который предлагает Johnmen.
Затем вставить из второй таблицы записи обратно в первую.

Однако опять тот же вопрос. Какой процент дубликатов?


 
Polevi   (2003-09-25 14:48) [27]

DELETE FROM Table WHERE ID NOT IN (
SELECT MAX(ID) FROM Table
GROUP BY Код, Дата, Наимнование)


 
kaif   (2003-09-25 14:50) [28]

А просто переписать все в другую таблицу?
Предположим, IB может сделать 1000 вставок в секунду. 3,6 миллиона вставок за час, то есть 12 миллионов за 4 часа максимум.

insert into table2 select * from AAA;

CREATE PROCEDURE AAA
RETURNS( все поля.....)
AS
DECLARE VARIABLE LAST_CODE INTEGER;
DECLARE VARIABLE LAST_DATA DATE;
DECLARE VARIABLE LAST_NAME VARCHAR(100);
BEGIN
last_code = 0;
last_data= "01.01.1800";
last_name = "";

for select id, code, data, name
from mytable
order by code, data, name //индекс
into :id, :code, :data, :name, ......
do
begin
if ((code <> last_code) or
(data <> last_data) or
(name <> last_name)) then
begin
suspend;
last_code = code;
last_data= data;
last_name = name;
end
end
END


 
Johnmen   (2003-09-25 14:51) [29]

>Vlad ©

Когда ожидать отчета о проведенных экспериментах ?
Лично меня интересует сравнение времени [2] и [12].
Да и с [27] тоже...


 
Vlad   (2003-09-25 14:56) [30]

>kaif © (25.09.03 14:43) [26]
Процент дубликатов мал. Думаю не более 1 млн.
>скопировать все записи, имеющие дубликаты в отдельную таблицу
а чем это быстрее чем просто их удалить ? Все равно время на выборку таких записей будет оч. велико.

>Polevi © (25.09.03 14:48) [27]
Пробовал. Group By сильно гадит по времени. Поскольку индексы тут уже роли не играют. Full Scan так сказать...


 
kaif   (2003-09-25 14:56) [31]

у меня хранимые процедуры обычно обходят набор со скоростью порядка 30тыс-100тыс записей в секунду. Вставка (insert) блоком в одном запросе insert into ... seletc from происходит достаточно быстро, не хуже 1000 записей в секнду. Правда я не пробовал работать с 12 миллионами. Возможно, вставка в такую таблицу происходит гораздо медленнее, особенно если в ней 15 полей.

Есть еще какой-то левый способ работать с таблицей через номера строк буквально. Как-то об этом шла речь на форуме. Некоторые используют какое-то секретное в IB внутренне для быстрых апдейтов.


 
kaif   (2003-09-25 15:00) [32]

А индексировать удается за приемлемое время?


 
Vlad   (2003-09-25 15:03) [33]

>kaif © (25.09.03 15:00) [32]
Попробую твой способ, но кажется нечто подобное я уже пробовал.
Индексы создаются за приемлемое время. Скажем так, 4 индекса по varchar-полям сегодня утром создал за 40 минут


 
Vlad   (2003-09-25 15:05) [34]

>Johnmen © (25.09.03 14:51) [29]
Завтра же утром отпишу подробный отчет :)
Самому интересно.


 
Vlad   (2003-09-26 09:56) [35]

DenK_vrtz ©, Johnmen ©, kaif © и все кто участвовал.
Вам еще интересны результаты моих лабораторных опытов, касательно обсуждаемой вчера темы ?


 
DenK_vrtz   (2003-09-26 09:59) [36]

Канэчно!!! :-) С нетерпением ждем(c)


 
Johnmen   (2003-09-26 10:02) [37]

Мне интересно !!!


 
Vlad   (2003-09-26 10:12) [38]

Тестировал при количестве записей - 100 тысяч.
Вобщем результаты были выше всяких похвал.
По совету Johnmen © добавил составной индекс.
Результат:
[2] - 2 сек.
[12] - 3 сек.
[27] - запрос повел себя немного странно. Загрузка ЦП - 99%, при этом обращения к жесткому диску были оч. редкими, судя по лампочке. Короче на 10-й минуте ожидания снял задачу. Чтоб убедиться проверил еще раз. Тоже самое.
А теперь, внимание, смертельный номер. Количество записей 12 МЛН.
Используется запрос [2].
Результат - 1 час 20 мин. !!!
Johnmen ©, ты - гений ! Как я сам не догадался про составной индекс ?! Без него все было на несколько порядков медленнее.


 
DenK_vrtz   (2003-09-26 10:22) [39]

Vlad ©, мои поздравления!
Johnmen ©, мои восхищения! Это круто!


 
Vlad   (2003-09-26 10:29) [40]

В любом случае, спасибо всем кто участвовал.
Конечно не ожидал я такого результата.
При проверке на 100 тыс. надо было видеть мое выражение лица!
Готовый к самому худшему, налил кофе, нарезал бутербродов, взял секундомер, приготовился ждать. И на тебе ! 2 секунды :)))



Страницы: 1 2 вся ветка

Форум: "Базы";
Текущий архив: 2003.10.16;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.54 MB
Время: 0.031 c
1-51223
ligor
2003-10-03 17:54
2003.10.16
dll


6-51384
iWorm
2003-08-21 03:34
2003.10.16
Как передать c cокета TServerSocket на сокет TClientSocket


14-51416
ZeroDivide
2003-09-25 16:08
2003.10.16
(с)Vuk Работает бригада... Каждый должен заниматься своим делом


9-51056
justYura
2003-04-10 14:48
2003.10.16
Smoothing Groups, OpenGL


1-51294
Ольга
2003-10-06 12:29
2003.10.16
адрес exe-шника





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский