Модераторы: skyboy

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> запрос для биллинга 
:(
    Опции темы
tzirechnoy
Дата 29.8.2015, 19:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 2
Всего: 16



1) Никогда не указывайте напрямую имя базы данных, если не пишэте софт для управления кластером баз данных. Если нужны какие-то внешние данные, и СУБД можэт их получить -- сделайте локальный (в своей БД) альяс/ссылку/что там СУБД позволяет.

2) А зачем Вы, собственно, сделали две DATABASE? Вам мало проблем?
Люди, которые будут это деплоить, за такое и побить могут.

3) Никогда не используйте + или - или какие-то ещё знаки операцый в именах таблиц. И русских букв не используйте. Вообще, используйте только a-zA-Z0-9_ . И не начинайте имя с цыфры. Людям, которые ковыряют потроха движков и делают какие-то метабазыданных иногда можно использовать какие-нибудь @ или % или $ для служэбных цэлей, прикладному программисту -- нет.



PM MAIL   Вверх
tzirechnoy
Дата 29.8.2015, 20:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 2
Всего: 16



Цитата
сколько получилось время выполнения?


0.35c. А самому запустить тот запрос?

Цитата
ок, выкладывай как будет возможность потестируем.


Что выкладывать? Базу с интэгрированной таблицэй cdr? Ну, можно, но мне лень пока. Учитывая, что всё, что нужно -- перенести одну таблицу в основную базу. Да и на скорость влиять не должно -- это общая такая рекомендацыя.

Цитата
WHERE  b.number


Для времени, я сказал. И BETWEEN строго эквивалентно a >= begin AND a <= end, тут уж это чисто синтаксическая разница. А для времени надо полуоткрытый интэрвал.

Цитата
добавляю к запросу  Код inner join astcdr.codes  c on b.prefix=c.abcdef inner join astcdr.zoneinfo zi on zi.uid=c.zoneinfouid


Не верю. У меня минут 15 выполнялось -- оно и понятно, там несколько миллионов записей в результсете, поскольку на каждую запись из cdr выдастся всё с совпадающим ABC/DEF префиксом -- это в среднем по полторы тысячи записей.
И это пото ещё отсортировать.

Добавлено через 4 минуты и 44 секунды
Цитата
И да, тройной индэкс для abcdef, start, end безсмысленен -- можно оставить только первые два поля.


Впрочем, да, тут я подумал -- для твоего варианта, с INNER JOIN, оно можэт ускорить... В среднем вдвое, наверное. Но только за счёт того, что этот поиск не будет выполняться нормально.
PM MAIL   Вверх
alligator
Дата 31.8.2015, 17:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 730
Регистрация: 28.1.2004

Репутация: нет
Всего: 1



Цитата(tzirechnoy @  29.8.2015,  20:39 Найти цитируемый пост)
2) А зачем Вы, собственно, сделали две DATABASE? Вам мало проблем?
Люди, которые будут это деплоить, за такое и побить могут.

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

Цитата(tzirechnoy @  29.8.2015,  20:39 Найти цитируемый пост)
сделайте локальный (в своей БД) альяс/ссылку/что там СУБД позволяет.

можно по подробней....?

Цитата(tzirechnoy @  29.8.2015,  20:39 Найти цитируемый пост)
3) Никогда не используйте + или - или какие-то ещё знаки операцый в именах таблиц.

Полностью согласен.

Цитата(tzirechnoy @  29.8.2015,  21:09 Найти цитируемый пост)
0.35c. А самому запустить тот запрос?

Только добрался до компа, до этого небыло возможности....
Потестировал твой запрос и более детально посмотрел код, спасибо что нашел время!
Работает быстро, но в хранимке проверяется только поле start, а это не очень подходит для биллинга, может неверно считать...
конечно понятно что это только пример обхода бага оптимизатора...
Так вроде, точнее....
Код


CREATE DEFINER=`root`@`localhost` FUNCTION `get_cuid`(`prefix` SMALLINT(3), `num` INT(7)) RETURNS int(15)
    READS SQL DATA
    DETERMINISTIC
BEGIN

  DECLARE maxstart int(7);
  DECLARE res int(15);
  
SELECT max(cdir.start)
     FROM codes cdir
     WHERE cdir.abcdef = prefix AND cdir.start <= num
  LIMIT 1
  INTO maxstart;
  SELECT uid
    FROM codes
    WHERE codes.abcdef = prefix and codes.start=maxstart  AND codes.end >= num
  LIMIT 1
  INTO res;
  RETURN res;

END


Запрос занял 0.3904 сек

убираю из запроса:
Код

     INNER JOIN numtype t ON t.uid=c.type

Explain на этом поле показывает ALL следовательно единственный для этой таблицы primary key не используется
Запрос занял 0.0129 сек

Цитата(tzirechnoy @  29.8.2015,  21:09 Найти цитируемый пост)
Не верю. У меня минут 15 выполнялось

Да , уже потом заметил что забыл добавить WHERE......etc...

Это сообщение отредактировал(а) alligator - 31.8.2015, 17:55
PM MAIL   Вверх
tzirechnoy
Дата 31.8.2015, 20:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 2
Всего: 16



Цитата
Работает быстро, но в хранимке проверяется только поле start, а это не очень подходит для биллинга, может неверно считать...


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

У меня для обхода этого в результате было поле code_is_valid (оно должно было проверяться в приложэнии). Твой вариант -- тожэ вполне вариант. Но в любом случае непопавшые никуда звонки надо как-то обрабатывать, и, для начала, их выбрать (например, таки добавив поле code_is_valid и сделав везде LEFT OUTER JOIN вместо INNER JOIN).

Цитата
Запрос занял 0.3904 сек

Цитата
Запрос занял 0.0129 сек


Непохожэ на правду. Скорее, в первом случае он с диска читался. У меня строго одинаковые 0.35с получились в обоих случаях. Да и чему там тормозить: в numbertype две записи, они и безо всякого ключа в столько жэ сравнений фильтруются.

Ну и, кстати, попробовал тожэ самое в postgres (без stored function, просто двумя запросами в коде) -- оно сразу втрое быстрее, чем такой жэ запрос mysql с stored function стало.

PM MAIL   Вверх
alligator
Дата 1.9.2015, 09:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 730
Регистрация: 28.1.2004

Репутация: нет
Всего: 1



Цитата(tzirechnoy @  31.8.2015,  21:20 Найти цитируемый пост)
Ну и, кстати, попробовал тожэ самое в postgres (без stored function, просто двумя запросами в коде) -- оно сразу втрое быстрее, чем такой жэ запрос mysql с stored function стало.

Почитал про postgresql, заинтересовало ..... поставил но пока ищу как дамп импортировать
PM MAIL   Вверх
tzirechnoy
Дата 1.9.2015, 10:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 2
Всего: 16



Руками импортировать, разумеется. С отдельно сохранил весь DDL, отдельно DML.

DDL переписывал довольно заметно. int(3) -> smallint, int(7) -> int, int(15) -> bigint. ADD KEY -> create index, дажэ макрос в виме записал, поскольку их много было.

Да, весьма удобно тэстировать в транзакцыи. Т.е. 
Код
begin;
\i pg_ddl.sql
^C
rollback;begin;


После многочисленных экспериментов с INSERT/DELETE полной таблицы жэлательно сделать vacuum full verbose analyze -- поскольку старые версии записей с диска удалятся не так чтобы скоро дажэ в версиях, в которых есть autovacuum.

Ну и да, плюсы постгрэсса всё-таки не в скорости. Со скоростью всё довольно неплохо, в сложных случаях -- так и лучшэ, чем в mysql, но всё-таки дело не в этом.

Добавлено через 11 минут и 28 секунд
Цитата
можно по подробней....?



Честно говоря, посмотрел на mysql в очередной раз -- действительно у него нет ничего хорошэго чтобы делать ссылки внутри. А т.н. "базы данных" очень похожы на то, что в других СУБД называется "scheme" или "namespace", потому действительно не настолько это всё требуется переименовывать. Точнее, можэт и требуется -- но всё равно нет нормальных способов.

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

Впрочем, конкретно в твоём случае, возможно, что ENGINE=federated было бы во многих смыслах более общим решэнием, и позволило действительно разнести cdr и ведение счёта по разным серверам.
PM MAIL   Вверх
alligator
Дата 1.9.2015, 12:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 730
Регистрация: 28.1.2004

Репутация: нет
Всего: 1



В общем сделал базу на PostgreSQL, изначально конечно тяжеловато..... много незнакомого, но результат впечатлил....
база + primary key + индексы с таким запросом в лоб:
Код

SELECT b.calldate,b.src, CONCAT(b.prefix, b.number) as dst , b.duration, b.billsec, b.billmin, b.trunk 
FROM (SELECT h.calldate ,h.src,SUBSTRING( RIGHT( LPAD( h.dst, 12,  '9' ) , 10 ) , 1, 3 ) AS prefix ,SUBSTRING( RIGHT( LPAD( h.dst, 12,  '9' ) , 10 ) , 4, 7 ) AS number,h.duration,h.billsec , ROUND((CASE h.billsec WHEN 0 THEN 1 ELSE h.billsec END)/60, 2) as billmin,
REGEXP_REPLACE(h.dstchannel, '(?:SIP|OOH323|LOCAL|SCCP)/(?:FMPR|FMGL)?([a-zA-Z_]+)?.*', '\1') as trunk
FROM  cdr h
WHERE  h.calldate BETWEEN  DATE'2015-05-01' AND  DATE'2015-05-31' AND LENGTH( h.src ) <=3 AND LENGTH( h.dst ) >3 AND h.disposition='ANSWERED') as b
inner join codes  c on CAST(b.prefix AS smallint) = c.abcdef
inner join zoneinfo zi on zi.uid=c.zoneinfouid
inner join isp i on i.uid=zi.ispuid
inner join region r on r.uid=zi.regionuid
inner join subregion s on s.uid=zi.subregionuid
WHERE  CAST(b.number AS integer) between c.start and c.end and b.trunk='Beeline' ORDER BY b.calldate DESC

700 ms

если целиком:
Код

SELECT b.calldate,b.src, CONCAT(b.prefix, b.number) as dst , b.duration, b.billsec, b.billmin, b.trunk , td.price,  ROUND(CEIL(b.billmin)*td.price, 2) as bill, i.ispname,s.subregionname,r.regionname
FROM (SELECT h.calldate ,h.src,SUBSTRING( RIGHT( LPAD( h.dst, 12,  '9' ) , 10 ) , 1, 3 ) AS prefix ,SUBSTRING( RIGHT( LPAD( h.dst, 12,  '9' ) , 10 ) , 4, 7 ) AS number,h.duration,h.billsec , ROUND((CASE h.billsec WHEN 0 THEN 1 ELSE h.billsec END)/60, 2) as billmin,
REGEXP_REPLACE(h.dstchannel, '(?:SIP|OOH323|LOCAL|SCCP)/(?:FMPR|FMGL)?([a-zA-Z_]+)?.*', '\1') as trunk
FROM  cdr h
WHERE  h.calldate BETWEEN  DATE'2015-05-01' AND  DATE'2015-05-31' AND LENGTH( h.src ) <=3 AND LENGTH( h.dst ) >3 AND h.disposition='ANSWERED') as b
    LEFT OUTER JOIN codes  c on CAST(b.prefix AS smallint) = c.abcdef
    LEFT OUTER JOIN zoneinfo zi on zi.uid=c.zoneinfouid
    LEFT OUTER JOIN isp i ON i.uid=zi.ispuid
    LEFT OUTER JOIN region r ON r.uid=zi.regionuid
    LEFT OUTER JOIN subregion s ON s.uid=zi.subregionuid
    LEFT OUTER JOIN tariff tr on tr.trunk=b.trunk
    LEFT OUTER JOIN tariff_data td on td.tuid=tr.uid
    LEFT OUTER JOIN tariff_zones tz on tz.zone=zi.uid and tz.tduid=td.uid
WHERE  CAST(b.number AS integer) between c.start and c.end and b.trunk='Beeline' ORDER BY b.calldate DESC

10 секунд


Это сообщение отредактировал(а) alligator - 1.9.2015, 17:19
PM MAIL   Вверх
tzirechnoy
Дата 1.9.2015, 18:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 2
Всего: 16



Цитата
 индексы с таким запросом в лоб:


Э-э-э. Ну, надо заметить, что если интересует скорость -- то таки или пытаться сделать cube и GiST-индэкс на abcdef||start/abcdef||end (не знаю точно как, ни разу с ними не работал) или таки делать почти тот жэ самый хак для выборки одного значения по индэксу:

Код
                  (SELECT c.uid
                     FROM codes  c
                     WHERE
                         SUBSTRING( LPAD( h.dst, 12,  '9' ) , 3, 3)::bigint
                              = c.abcdef
                         AND SUBSTRING( LPAD( h.dst, 12,  '9' ), 6, 7 )::bigint
                              > c.start
                        ORDER BY c.abcdef, c.start DESC LIMIT 1)
                     AS codeuid


Это, собственно, логично если знать как индэксы работают и практически неизбежно.

Разница с mysql -- в том, что не требуется оборачивать это в функцыю (у mysql без оборачивания планировщик херню делал).

Цитата
WHERE  CAST(b.number AS integer) between c.start and c.end 


Слушай, ну это надо в JOIN CONDITION записывать. И, если с INNER JOIN это был такой синтаксический кунштюк (ну, физически серверу никакой разницы, где это условие написано), то с LEFT OUTER JOIN -- это обязательно должно быть в JOIN CONDITION.

И да, наличие измеримой разницы между первым и вторым -- говорит, что что-то не то с индэксами. Там по идее всё, что ты добавлял -- это выборка одного значения по ключу, это несопоставимое время должно занимать. Ну, у меня оно отличается всё-таки -- 1.6с и 1.8с, но незначительно.


PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Составление SQL-запросов | Следующая тема »


 




[ Время генерации скрипта: 0.1395 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.