powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Помогите оптимизировать запрос
25 сообщений из 31, страница 1 из 2
Помогите оптимизировать запрос
    #39841501
Rom1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем привет! Есть подобная таблица:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
DATE       | USERID | TRIFID
2019-05-01 | 1      | 5
2019-05-02 | 1      | 5
2019-05-03 | 1      | 1
2019-05-04 | 1      | 1
2019-05-05 | 1      | 5
2019-05-06 | 1      | 5
2019-05-07 | 1      | 5
2019-05-08 | 1      | 5
2019-05-09 | 1      | 5
Нужно посчитать, сколько дней каждый USERID, был непрерывно на последнем TARIFID
В данном примере: USERID->1 был 5 раз (на TARIFID->5)

Сейчас используется ресурсоемкий алгоритм (stored procedure), который в лоб перебирает записи. Надо как-то оптимизировать.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841504
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1Сейчас используется ресурсоемкий алгоритм (stored procedure), который в лоб перебирает
записи. Надо как-то оптимизировать.

Используй курсор и выбирай только последние записи для каждого пользователя. Возможно,
пригодится убывающий индекс по USERID, DATE.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841511
rdb_dev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1, и где же запрос, который надо оптимизировать?
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841529
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
select count(*) from table1 t
where
  t.userid = :userid and t.trifid = :trifid  and
  t.dt between (select t.dt+1 as fdt
                from table1 t
                where
                  t.userid = :userid and t.trifid <> :trifid  and t.dt < :dt
                order by t.dt desc rows 1)
          and :dt


Модератор: используйте теги!
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841541
KreatorXXI
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1,

неизвестна версия ФБ. В данном примере 7 записей, а не пять. И что такое последний TARIFID? Максимальный номер? Или последний по дате? Есть ещё таблицы? Если существует только одна плоская таблица, то скорее всего удастся только отсечь по датам. Остальное придётся перебирать в лоб.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841575
Rom1,
без процедур где-то так
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
 
with ug as (select t.userid,  max(t.dt) max_dt
            from  table1 t group by 1),
      u as (select ug.*,
              (select t.trifid from table1 t
               where ug.userid = t.userid and t.dt = ug.max_dt)
            from ug)
select u.* , 
   (select count(*)
    from table1 t
    where
      u.userid = t.userid and t.trifid = u.trifid
      and (t.dt between 
                 coalesce((select t1.dt+1 as fdt
                               from table1 t1
                               where
                                t1.userid = u.userid and  t1.trifid <> u.trifid and t1.dt < u.max_dt
                               order by t1.dt desc rows 1),  '01.01.1999')
                  and u.max_dt )

       ) cnt
from u
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841605
Rom1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
неизвестна версия ФБ.
2.5.2
В данном примере 7 записей, а не пять.
Нужно посчитать непрерываемую последовательность, от текущего дня к "прошлому" - сколько дней на текущем TARIFID
И что такое последний TARIFID? Максимальный номер? Или последний по дате?
INTEGER, по последней дате
Есть ещё таблицы?
Нет
авторЕсли существует только одна плоская таблица, то скорее всего удастся только отсечь по датам.
Важны не даты, а сколько пробыл на текущем TARIFID
авторОстальное придётся перебирать в лоб.
Это я и так делаю, думал оптимизировать.

За остальные примеры спасибо, примерю их, отпишусь.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841737
WildSery
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
"Ресурсоёмкий алгоритм", который перебирает записи с конца - самый простой и быстрый.
Но можно и запросом.

Код: sql
1.
2.
3.
4.
select count(*) from table1 t
  where t.userid = :userid and t.trifid = :trifid  and
  not exists (select * from table1 t1 
                where t1.userid = t.userid and t1.trifid <> t.trifid and t1.dt > t.dt)
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841778
KreatorXXI
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1Есть ещё таблицы?
Нет
авторЕсли существует только одна плоская таблица, то скорее всего удастся только отсечь по датам.
Важны не даты, а сколько пробыл на текущем TARIFID
авторОстальное придётся перебирать в лоб.
Это я и так делаю, думал оптимизировать.

А что таблица совсем гигантская? Сколько записей в таблице за заданный период? В плоской таблице мало путей для оптимизации. Надо наверно оставить как есть. И посмотреть индексы по датам.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841813
Rom1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
KreatorXXI,
Немного вроде, порядка ~60 млн записей. Индексы по ключевым значениям есть.

Данные статистические, не очень критичные, требуются для человека.
Сейчас эта статистика собирается раз в сутки, на это уходит минут ~40. Думаю сделать этот расчет по запросу, по конкретному USERID, т.к. 100% покрытие статистикой совершенно не требуется, а значит зазря 40 минут в день теряются в подсчетах.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841816
pastor
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1KreatorXXI,
Немного вроде, порядка ~60 млн записей. Индексы по ключевым значениям есть.

Данные статистические, не очень критичные, требуются для человека.
Сейчас эта статистика собирается раз в сутки, на это уходит минут ~40. Думаю сделать этот расчет по запросу, по конкретному USERID, т.к. 100% покрытие статистикой совершенно не требуется, а значит зазря 40 минут в день теряются в подсчетах.

денормализация. агрегаты.

отдельная табличка - UserID, MAX_COUNT, TariffID, DateLastCheck.

пересчитывать на после даты.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841820
KreatorXXI
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1,

Это за какой период 60 млн?
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841824
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1требуются для человека.

Таки определитесь для начала: требуются на одного человека или на каждого? И насколько
часто? Для первого случая и частоты запросов менее раза в секунду - цикл при правильном
плане самое лучшее решение.

Хранимые агрегаты, конечно, тоже можно сделать, но там есть грабли.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841834
Rom1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Это за какой период 60 млн?
Это за 13 лет, в статистике участвуют последние 5.

Таки определитесь для начала: требуются на одного человека или на каждого? И насколько
часто? Для первого случая и частоты запросов менее раза в секунду - цикл при правильном
плане самое лучшее решение.
Статистические данные нужно показать оператору(ам), когда тот открывает карточку USERID.
Менее раза в секунду, конечно же.

Пока склоняюсь к созданию процедуры GET_USER_TARIF_DAYS(:USERID), которая в лоб будет считать, но только одного USERID. И конечно же нужна отдельная табличка, как правильно написал pastor , которая будет следить за состоянием статистики, если сегодня уже считали - на готовый результат. Не считали - считаем, сохраняем, возвращаем.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841845
Фотография kdv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1Это за 13 лет, в статистике участвуют последние 5.
зачем постоянно пересчитывать неизменяемые, архивные данные?
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841847
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1Пока склоняюсь к созданию процедуры GET_USER_TARIF_DAYS(:USERID), которая в лоб будет
считать, но только одного USERID.

Пока это количество дней нужно только для карточки, это наилучший вариант.

Rom1И конечно же нужна отдельная табличка, как правильно написал pastor, которая
будет следить за состоянием статистики, если сегодня уже считали - на готовый результат.
Не считали - считаем, сохраняем, возвращаем.
А вот это пока что выглядит совершенно избыточным. Оно бы потребовалось при
десятках-сотнях запросов в секунду по отдельным пользователям или при довольно частом
запросе по всем миллионам пользователей.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841854
Rom1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
зачем постоянно пересчитывать неизменяемые, архивные данные?
Тогда придется заниматься аггрегацией, а за ней придется следить. А это значит что нужно сразу приготовить отходные пути по перерасчету всей аггрегации, в случае ошибок/поломок/недоверия консистетности.

А вот это пока что выглядит совершенно избыточным. Оно бы потребовалось при
десятках-сотнях запросов в секунду по отдельным пользователям или при довольно частом
запросе по всем миллионам пользователей.
Да, эта информация показывается еще в некоторых местах (группа USERIDs, до 100 штук), там AJAX'ом будет подгружаться уже эта статистика по каждому USERID. Дважды открылась группа - это уже 200% подсчетов статистики, а можно гарантированно остановиться на 100%. Так что такое кеширование, думаю не лишним будет.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841858
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1Так что такое кеширование, думаю не лишним будет.

Будет. Поэтому лично я бы с ним не связывался пока не пойдут жалобы на производительность.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841875
WildSery
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А хранили бы только изменения - достаточно было бы только одну последнюю запись взять для подсчёта.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841899
rdb_dev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1, зачем забивать таблицу избыточными данными по каждому дню, когда достаточно хранить лишь дату перехода пользователя на другой тариф?

Самым оптимальным решением, на мой взгляд, является именно обход данных внутри ХП в курсоре FOR SELECT с упорядочением по полям идентификатора пользователя и даты смены тарифа, что позволяет обеспечить самый быстрый и удобный подсчёт кол-ва дней внутри каждого диапазона смены тарифов, а затем, из полученного в ХП набора данных, вычленить максимальное кол-во дней с группировкой по идентификатору пользователя и идентификатору тарифа.

Ещё напрашивается вопрос - что делать, если два разных тарифа пользователя имеют одинаковый максимальный срок действия?
Думаю, запрос будет работать медленнее, хуже пониматься разработчиками и смысл его создания - лишь спортивный интерес
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
WITH ranges AS (
  SELECT user_id, tariff_id,
      Coalesce(end_date, Cast(CURRENT_TIMESTAMP AS DATE) + 1) AS end_date,
      Max(Coalesce(end_date,
          Cast(CURRENT_TIMESTAMP AS DATE) + 1) - start_date) AS day_count
    FROM (
        SELECT ustach.user_id, ustach.tariff_id,
            ustach.chng_date AS start_date,
            Min(ustach_.chng_date) AS end_date
          FROM user_tariff_chng ustach
            LEFT JOIN user_tariff_chng ustach_
              ON ustach_.user_id = ustach.user_id
                AND ustach_.tariff_id != ustach.tariff_id
                AND ustach_.chng_date > ustach.chng_date
          GROUP BY ustach.user_id, ustach.chng_date, ustach.tariff_id)
    GROUP BY user_id, end_date, tariff_id
)
SELECT r.user_id, r.tariff_id, r.end_date, r.day_count
  FROM ranges r,
    (SELECT user_id, Max(day_count) AS max_days
      FROM ranges
      GROUP BY user_id) m
  WHERE r.user_id = m.user_id
    AND r.day_count = m.max_days

...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841901
pastor
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1зачем постоянно пересчитывать неизменяемые, архивные данные?
Тогда придется заниматься аггрегацией, а за ней придется следить. А это значит что нужно сразу приготовить отходные пути по перерасчету всей аггрегации, в случае ошибок/поломок/недоверия консистетности.

А вот это пока что выглядит совершенно избыточным. Оно бы потребовалось при
десятках-сотнях запросов в секунду по отдельным пользователям или при довольно частом
запросе по всем миллионам пользователей.
Да, эта информация показывается еще в некоторых местах (группа USERIDs, до 100 штук), там AJAX'ом будет подгружаться уже эта статистика по каждому USERID. Дважды открылась группа - это уже 200% подсчетов статистики, а можно гарантированно остановиться на 100%. Так что такое кеширование, думаю не лишним будет.

у меня так сейчас остатки на л/с клиентов хранятся.

есть агрегат - берем его, делаем +-, переписываем агрегат. на время работы - select with lock.
нет агрегата - считаем с нуля.

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

побочный положительный эффект - блокировка л/с на момент транзакции.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841959
Rom1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Rom1, зачем забивать таблицу избыточными данными по каждому дню, когда достаточно хранить лишь дату перехода пользователя на другой тариф?
Это билинговая система. Есть таблица тарифных планов каждого USERID, там указаны как раз-таки периоды: TARIFID, начало-окончание/начало-бесконечность.
Есть таблица реальных начислений, которые должны храниться и доставаться без каких либо вычислений - это требование аудита.
При этом тарифы абонента со временим удаляются. Поэтому статистику о том сколько USERID находится на тарифном плане, можно поднять только из реальных начислений.

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

авторесть агрегат - берем его, делаем +-, переписываем агрегат. на время работы - select with lock.
нет агрегата - считаем с нуля.
Аггрегатом придется заниматься всегда и всегда помнить о нем и иметь отходные пути, которые не остановят работу на в ЧНН. Мне такая концепция не нравится.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841964
pastor
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1Аггрегатом придется заниматься всегда и всегда помнить о нем и иметь отходные пути, которые не остановят работу на в ЧНН. Мне такая концепция не нравится.

пока не бросишь программирование.

организуй в это место доступ через хп.

у меня агрегаты еще имеют срок годности, после которого их требуется пересчитать.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841965
KreatorXXI
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rom1Это за какой период 60 млн?
Это за 13 лет, в статистике участвуют последние 5.



Значит участвуют миллионов 20 записей. Если тупо пробежаться по ним, будет в районе двух минут (я бы даже дал меньше - полминуты). Скорее всего Вы своими наворотами всё портите. Как предложил Dimitry Sibiryakov, используйте запрос с курсором в хранимке. Или приведите свой запрос для начала.
...
Рейтинг: 0 / 0
Помогите оптимизировать запрос
    #39841973
Rom1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Значит участвуют миллионов 20 записей. Если тупо пробежаться по ним, будет в районе двух минут (я бы даже дал меньше - полминуты). Скорее всего Вы своими наворотами всё портите. Как предложил Dimitry Sibiryakov, используйте запрос с курсором в хранимке. Или приведите свой запрос для начала.
Я конечно же задачу сократил до минимума, чтобы указать на суть расчета и связанной с этим проблемы. Собрать статистику, удалить данные, добавить - все это достаточно ресурсоемко, пол минуты - это тупо посчитать количество записей в начислениях, без обработки.
...
Рейтинг: 0 / 0
25 сообщений из 31, страница 1 из 2
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Помогите оптимизировать запрос
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]