Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / FoxPro, Visual FoxPro Новый топик    Ответить
 Возможна ли оптимизация такого кода...  [new]
CTAC-KO
Member

Откуда: Украина, г. Ивано-Франковск
Сообщений: 1012
Привет!

Тут такая задача - есть бд тарификации тел. разговоров. В этой бд есть такие поля
RowMark i - означает тип пакета
abonent c(7) - кто звонил
date d - дата звонка
time c(5) - время
minutes i - минут от полуночи
talk_sec i - время разговора
abonent2 c(20) - набранный номер

Здесь важен RowMark вот в каком смысле. Дело в том, что пакеты, используемые для тарификации официально имеют значение 42316, но они так же дублируются еще какимто технологическим пакетом со значением уже 42317, который обычно создается следом за пакетом 42316. Проблема в том, что:
1) дублирование какбы "неточное", т.е. в дубль-пакете 42317 может отличаться длительность разговора на +-3 секунды, а так же время начала разговора может быть +1 минута (зависит восновном от случаев когда разговор начат в последние секунды минуты), причем секунды начала разговора не отмечаются нигде и никак, т.е. точность начала разговора есть до минуты.
2) иногда в бд попадают только дубль-пакеты с меткой 42317, а их "родителя" 42316 при этом может не быть и наоборот.

Задача.
Отбросить дубль-пакеты с меткой 42317, если в наличии есть оригинал с меткой 42316, учитывая неполное и неидеальное сходство. При этом должны остаться записи с меткой 42316 непродублированные с меткой 42317 и наоборот.

Я сделал следующим образом. В отдельную бд (dbf_42316) выбираю только записи с меткой 42316, т.к. их обычно меньше, а затем по полной базе (dbfTemp) делаю SCAN/LOCATE/DELETE:
     SELECT dbfTemp
	 SCAN FOR RowMark = 42317

		SELECT dbf_42316
		LOCATE FOR NOT DELETED();
				AND Abonent = dbfTemp.Abonent;
				AND Date = dbfTemp.Date;
				AND (BETWEEN(Minutes, dbfTemp.Minutes-1,dbfTemp.Minutes);
					OR Minutes=1439 AND dbfTemp.Minutes=0)
				AND Abonent2 = dbfTemp.Abonent2;
				AND BETWEEN(Talk_Sec,dbfTemp.Talk_Sec-3,dbfTemp.Talk_Sec+3)
		IF FOUND()
		   DELETE
		   SELECT dbfTemp
		   DELETE
		ENDIF
	 ENDSCAN
такой способ очень медленным оказался, оно и понятно.

Я размышлял над индексацией, но так и не пришел к определенному решению.

Непонятно как это можно оптимизировать - может подскажете?

спасибо.

вфп9
5 авг 08, 10:47    [6027166]     Ответить | Цитировать Сообщить модератору
 Re: Возможна ли оптимизация такого кода...  [new]
XAndy
Member

Откуда: Киев
Сообщений: 326
Что связывает пару 42316 и 42317? Ключ какой-то есть или только составной типа кто и когда звонил?
5 авг 08, 10:51    [6027202]     Ответить | Цитировать Сообщить модератору
 Re: Возможна ли оптимизация такого кода...  [new]
CTAC-KO
Member

Откуда: Украина, г. Ивано-Франковск
Сообщений: 1012
Ничего не связывает, кроме того что воочию видно что есть дубликат, т.е. ключа нет и единственная возможность это вот такое сравнение.
5 авг 08, 11:48    [6027644]     Ответить | Цитировать Сообщить модератору
 Re: Возможна ли оптимизация такого кода...  [new]
ОТЭ
Member

Откуда: Орел
Сообщений: 15
Может проиндексировать dbf_42316 по выражению Abonent+dtoc(Date)+Abonent2,
а затем в твоем скане вместо Locate использовать seek() по этому выражению и при удачном поиске
сделать цикл с построчным перебором всех совпадений на предмет сравнения минут и секунд?
5 авг 08, 14:31    [6028894]     Ответить | Цитировать Сообщить модератору
 Re: Возможна ли оптимизация такого кода...  [new]
CTAC-KO
Member

Откуда: Украина, г. Ивано-Франковск
Сообщений: 1012
короче сам оптимизировал, но еще не уверен что все ок, хотя быстрее в разы.
	SELECT dbf_42316
	INDEX on Abonent + DTOS(date) + abonent2 TAG indx
	SELECT dbfTemp
	SCAN FOR RowMark = 42317
		lcSeekExpression = Abonent + DTOS(date) + abonent2
		SELECT kdf_a54c
		SEEK m.lcSeekExpression &&TAG indx
		DO WHILE m.lcSeekExpression == Abonent + DTOS(date) + abonent2
			IF NOT DELETED();
			AND (BETWEEN(Minutes, kdf2dbfTemp.Minutes-1,kdf2dbfTemp.Minutes+1);
				OR Minutes=0 AND kdf2dbfTemp.Minutes=1439);
			AND BETWEEN(Talk_Sec,kdf2dbfTemp.Talk_Sec-3,kdf2dbfTemp.Talk_Sec+3)

				DELETE
				SELECT kdf2dbfTemp
				DELETE

				EXIT
			ENDIF
			SKIP
		ENDDO
	 ENDSCAN
5 авг 08, 14:36    [6028924]     Ответить | Цитировать Сообщить модератору
 Re: Возможна ли оптимизация такого кода...  [new]
CTAC-KO
Member

Откуда: Украина, г. Ивано-Франковск
Сообщений: 1012
ОТЭ
Может проиндексировать dbf_42316 по выражению Abonent+dtoc(Date)+Abonent2,
а затем в твоем скане вместо Locate использовать seek() по этому выражению и при удачном поиске
сделать цикл с построчным перебором всех совпадений на предмет сравнения минут и секунд?
спасибо, пришел к аналогичному :)
5 авг 08, 14:38    [6028934]     Ответить | Цитировать Сообщить модератору
 Re: Возможна ли оптимизация такого кода...  [new]
Dima T
Member

Откуда:
Сообщений: 15271
Можно еще ускорить если выполняются следующие предположения:
1. записи идут в хронологическом порядке как для 42316 так и для 42317
2. Запись 42316 всегда перед 42317
примерно так без промежуточной таблицы и индексов:
SELE dbfTemp
SCAN FOR RowMark = 42316
    lnRe = recno('dbfTemp')
    * Запоминаем содержимое записи
    lcAbonent = dbfTemp.cAbonent
    ltD = dtot(dbfTemp.date) + dbfTemp.minutes * 60
    lnTalk_Sec = dbfTemp.Talk_Sec
    * Ищем в ближайших последующих записях дубль
    do while !eof('dbfTemp')
        skip in dbfTemp
        lnTimeDelta = dtot(dbfTemp.date) + dbfTemp.minutes * 60 - ltD
        do case
            case eof('dbfTemp') or lnTimeDelta > 60
                 * Конец файла или ушли больше чем на 1 минуту вперед, дальше можно не искать
                 exit
            case dbfTemp.RowMark = 42317 and dbfTemp.cAbonent = lcAbonent;
                      and inlist(ltD2 - ltD1, 0, 60);
                      and BETWEEN(dbfTemp.Talk_Sec lnTalk_Sec - 3, lnTalk_Sec + 3)
                 delete in dbfTemp
                 exit
        encase
    enddo
    go (lnRe) in dbfTemp
endscan

Даже если мои предположения 1,2 не верны, то надо индекс по ttoc(dtot(dbfTemp.date) + dbfTemp.minutes * 60, 1) + STR(RowMark, 5)

Для еще ускорения можно убрать EOF() (это довольно медленная операция) - запомнить RECNO() последней записи и проверять его внутри DO WHILE.
5 авг 08, 15:33    [6029384]     Ответить | Цитировать Сообщить модератору
 Re: Возможна ли оптимизация такого кода...  [new]
Dima T
Member

Откуда:
Сообщений: 15271
Не все поправил. Вместо
and inlist(ltD2 - ltD1, 0, 60);
Надо
and inlist(lnTimeDelta, 0, 60);
5 авг 08, 15:36    [6029412]     Ответить | Цитировать Сообщить модератору
 Re: Возможна ли оптимизация такого кода...  [new]
Galyamov Rinat
Member

Откуда:
Сообщений: 658

Маленько дам вопросов для размышления.

В связи с тем, что:
> 1) дублирование какбы "неточное", т.е. в дубль-пакете 42317 может
> отличаться длительность разговора на +-3 секунды, а так же время начала
> разговора может быть +1 минута (зависит восновном от случаев когда
> разговор начат в последние секунды минуты), причем секунды начала
> разговора не отмечаются нигде и никак, т.е. точность начала разговора есть
> до минуты.

Во первых, где гарантия, что отличие именно ДО минуты, а никак не больше (ну
скажем пару минут).

Во вторых, если звонок идет на границе суток, то и даты могут быть разные.

Третье. Я в течении одной минуты совершил два (три) звонка на один и тот же
номер. Длительность разговора 5 - 10 секунд. Но звонки были, разговоры тоже.

В связи с тем, что:
> 2) иногда в бд попадают только дубль-пакеты с меткой 42317, а их
> "родителя" 42316 при этом может не быть и наоборот.

Ты их можешь посчитать за один разговор и, соответственно, затрешь как
дубль.


> Задача.

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


Posted via ActualForum NNTP Server 1.4

6 авг 08, 07:33    [6031463]     Ответить | Цитировать Сообщить модератору
Все форумы / FoxPro, Visual FoxPro Ответить