powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Оконные функции для получения среза последних данных
11 сообщений из 11, страница 1 из 1
Оконные функции для получения среза последних данных
    #39800122
vvvait
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Приветствую

Firebird 3.0

Пытаюсь разобраться, как получить срез последних по времени данных
есть таблица:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
CREATE TABLE MESSAGES (
    UUID            SYS$UUID NOT NULL /* SYS$UUID = CHAR(16) */,
    ON_UUID         SYS$UUID /* SYS$UUID = CHAR(16) */,
    TICK            SYS$TIMETICK NOT NULL /* SYS$TIMETICK = BIGINT */,
    MSG_TIME        SYS$TIMESTAMP /* SYS$TIMESTAMP = TIMESTAMP */,
    MSG_LOCAL_TIME  COMPUTED BY (dateadd(hour, RDB$GET_CONTEXT('USER_SESSION', 'TIMEZONE'), MSG_TIME)),
    SITE            SYS$NAME NOT NULL /* SYS$NAME = VARCHAR(128) */,
    DEVICE          SYS$NAME NOT NULL /* SYS$NAME = VARCHAR(128) */,
    MSG_TYPE        SYS$NAME NOT NULL /* SYS$NAME = VARCHAR(128) */,
    MSG_KIND        SYS$NAME /* SYS$NAME = VARCHAR(128) */,
    JSON            SYS$JSON /* SYS$JSON = BLOB SUB_TYPE 1 SEGMENT SIZE 80 */
);

ALTER TABLE MESSAGES ADD CONSTRAINT MESSAGES PRIMARY KEY (UUID);
CREATE INDEX MESSAGES_TICK ON MESSAGES (TICK, DEVICE, MSG_TYPE, MSG_KIND);
CREATE INDEX MESSAGES_TIME ON MESSAGES (MSG_TIME, DEVICE, MSG_TYPE, MSG_KIND);
CREATE DESCENDING INDEX MESSAGES_TIME_DESC ON MESSAGES (MSG_TIME, DEVICE, MSG_TYPE, MSG_KIND);


набросал запрос который, вроде работает
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
with MDATA as (
  select UUID, DEVICE, MSG_TIME, MSG_TYPE, MSG_KIND, first_value(UUID) over (partition by DEVICE order by MSG_TIME desc) as OVER_UUID
  from MESSAGES
  where MSG_TIME between current_timestamp - 1 and current_timestamp
  order by DEVICE
)
select DEVICE, MSG_TIME, MSG_TYPE, MSG_KIND
from MDATA
where UUID = OVER_UUID


План: PLAN SORT (SORT (MD M INDEX (MESSAGES_TIME)))

Можно ли обойтись без with, и какие нужны индексы чтобы не вычитывать все записи за сутки?
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800124
vvvait
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Корректный план
Код: sql
1.
PLAN SORT (SORT (MDATA MESSAGES INDEX (MESSAGES_TIME)))
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800153
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vvvaitМожно ли обойтись без with

разве что заменить на derived table

vvvaitкакие нужны индексы чтобы не вычитывать все записи за сутки

никакие. Оконные функции никаких индексов применять не умеют, кроме тех по условию во внутреннем WHERE.

Посмотри лучше explain план, тогда поймёшь как оконные функции выполняются.
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800235
vvvait
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
похоже оконные функции не очень подходят для этой задачи
если сделать временную таблицу:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
CREATE GLOBAL TEMPORARY TABLE LAST_DEV_MESSAGE (
    DEVICE    SYS$NAME NOT NULL /* SYS$NAME = VARCHAR(128) */,
    UUID      SYS$UUID /* SYS$UUID = CHAR(16) */,
    ON_UUID   SYS$UUID /* SYS$UUID = CHAR(16) */,
    TICK      SYS$TIMETICK /* SYS$TIMETICK = BIGINT */,
    MSG_TIME  SYS$TIMESTAMP /* SYS$TIMESTAMP = TIMESTAMP */,
    SITE      SYS$NAME /* SYS$NAME = VARCHAR(128) */,
    MSG_TYPE  SYS$NAME /* SYS$NAME = VARCHAR(128) */,
    MSG_KIND  SYS$NAME /* SYS$NAME = VARCHAR(128) */,
    JSON      SYS$JSON /* SYS$JSON = BLOB SUB_TYPE 1 SEGMENT SIZE 80 */
) ON COMMIT PRESERVE ROWS;
ALTER TABLE LAST_DEV_MESSAGE ADD PRIMARY KEY (DEVICE);


и выполнить такой блок:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
execute block
returns (
    DEVICE type of column LAST_DEV_MESSAGE.DEVICE,
    UUID type of column LAST_DEV_MESSAGE.UUID,
    ON_UUID type of column LAST_DEV_MESSAGE.ON_UUID,
    TICK type of column LAST_DEV_MESSAGE.TICK,
    MSG_TIME type of column LAST_DEV_MESSAGE.MSG_TIME,
    SITE type of column LAST_DEV_MESSAGE.SITE,
    MSG_TYPE type of column LAST_DEV_MESSAGE.MSG_TYPE,
    MSG_KIND type of column LAST_DEV_MESSAGE.MSG_KIND,
    JSON type of column LAST_DEV_MESSAGE.JSON
)
as
begin
  delete from LAST_DEV_MESSAGE;
  for select m.DEVICE, m.UUID, m.ON_UUID, m.TICK, m.MSG_TIME, m.SITE, m.MSG_TYPE, m.MSG_KIND, m.JSON
      from MESSAGES m
      where m.MSG_TIME between current_timestamp - 1 and current_timestamp
      order by m.MSG_TIME desc
      into :DEVICE, :UUID, :ON_UUID, :TICK, :MSG_TIME, :SITE, :MSG_TYPE, :MSG_KIND, :JSON
   do begin
      if (exists (select * from LAST_DEV_MESSAGE where DEVICE = :DEVICE))
         then continue;
      insert into LAST_DEV_MESSAGE (DEVICE, UUID, ON_UUID, TICK, MSG_TIME, SITE, MSG_TYPE, MSG_KIND, JSON)
      values (:DEVICE, :UUID, :ON_UUID, :TICK, :MSG_TIME, :SITE, :MSG_TYPE, :MSG_KIND, :JSON);
      suspend;
      end
end


то получится тоже самое, только в 2,5 раза быстрее чем у оконной функции:
------ Информация о производительности ------
Время подготовки запроса = 32ms
Время выполнения запроса = 2s 515ms
Среднее время на получение одной записи = 104,79 ms
Current memory = 42 256 536
Max memory = 113 440 272
Memory buffers = 2 048
Reads from disk to cache = 0
Writes from cache to disk = 0
Чтений из кэша = 91 461


------ Информация о производительности ------
Время подготовки запроса = 15ms
Время выполнения запроса = 6s 406ms
Среднее время на получение одной записи = 206,65 ms
Current memory = 40 992 888
Max memory = 113 440 272
Memory buffers = 2 048
Reads from disk to cache = 1
Writes from cache to disk = 8
Чтений из кэша = 26 189
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800286
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vvvait,

а чего такой кеш мизерный?

Ну и во вторых, конечную сортировку лучше ставить после всех фильтраций, т.е. вот так

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
with MDATA as (
  select UUID, DEVICE, MSG_TIME, MSG_TYPE, MSG_KIND, 
         first_value(UUID) over (partition by DEVICE order by MSG_TIME desc) as OVER_UUID
  from MESSAGES
  where MSG_TIME between current_timestamp - 1 and current_timestamp
)
select DEVICE, MSG_TIME, MSG_TYPE, MSG_KIND
from MDATA
where UUID = OVER_UUID
order by DEVICE
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800287
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vvvait,

и ещё зачем столько составных индексов? У вас есть запросы которые по всем полям одновременно фильтрацию делают? Достаточно одного индекса

Код: sql
1.
CREATE INDEX MESSAGES_TIME ON MESSAGES (MSG_TIME);
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800377
vvvait
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Индексы может и не все нужны, но это замедляет только вставку.

Без сортировки, запрос идет 5 секунд.

Кэш маленький, т.к. firebird крутится на роутере с 256 Мб памяти.
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800393
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vvvait,

Одиночный индекс в данном случае лучше

Потому что Range Scan (full match) лучше чем Range Scan (partial match: 1/4)
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800395
vvvait
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
перенес базу на core i5 8400, 16gb, ssd m.2
срез за 30 дней

с окнами:
------ Информация о производительности ------
Время подготовки запроса = 16ms
Время выполнения запроса = 8s 234ms
Среднее время на получение одной записи = 111,27 ms
Current memory = 36 679 880
Max memory = 111 302 032
Memory buffers = 2 048
Reads from disk to cache = 7 498
Writes from cache to disk = 4
Чтений из кэша = 437 180

без окон:
------ Информация о производительности ------
Время подготовки запроса = 0ms
Время выполнения запроса = 1s 297ms
Среднее время на получение одной записи = 17,53 ms
Current memory = 36 768 864
Max memory = 111 302 032
Memory buffers = 2 048
Reads from disk to cache = 6 266
Writes from cache to disk = 6
Чтений из кэша = 2 156 679
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800412
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vvvait,

а ещё GUID в качестве ключа здесь играет злую шутку. Был бы обычный авто инкремент можно было бы сделать так

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
WITH T AS (
  SELECT MAX(ID) as ID
  FROM MESSAGES
  WHERE MSG_TIME between current_timestamp - 1 and current_timestamp
  GROUP BY DEVICE)
SELECT
  MESSAGES.DEVICE, MESSAGES.MSG_TIME, MESSAGES.MSG_TYPE, MESSAGES.MSG_KIND
FROM T
JOIN MESSAGES ON T.ID = MESSAGES.ID
ORDER BY MESSAGES.DEVICE



потому что чем позже пришло сообщение тем больше ID

В вашем случае можно попробовать

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
WITH T AS (
  SELECT MAX(MSG_TIME) as MSG_TIME, DEVICE 
  FROM MESSAGES
  WHERE MSG_TIME between current_timestamp - 1 and current_timestamp
  GROUP BY DEVICE)
SELECT
  MESSAGES.DEVICE, MESSAGES.MSG_TIME, MESSAGES.MSG_TYPE, MESSAGES.MSG_KIND
FROM T
JOIN MESSAGES ON T.DEVICE= MESSAGES.DEVICE AND T.MSG_TIME= MESSAGES.MSG_TIME
ORDER BY MESSAGES.DEVICE
...
Рейтинг: 0 / 0
Оконные функции для получения среза последних данных
    #39800436
vvvait
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
там есть такое поле, но оно не уникально, и есть очень маленькая вероятность что будет две записи с одинаковым тиком
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
WITH T AS (
  SELECT MAX(m.TICK) as TICK
  FROM MESSAGES m
  WHERE MSG_TIME between current_timestamp - 30 and current_timestamp
  GROUP BY DEVICE)
SELECT
  MESSAGES.DEVICE, MESSAGES.MSG_TIME, MESSAGES.MSG_TYPE, MESSAGES.MSG_KIND
FROM T
JOIN MESSAGES ON T.TICK = MESSAGES.TICK
ORDER BY MESSAGES.DEVICE


это запрос быстрее всех, на 30 дней:

------ Информация о производительности ------
Время подготовки запроса = 16ms
Время выполнения запроса = 703ms
Среднее время на получение одной записи = 9,50 ms
Current memory = 36 809 368
Max memory = 111 302 032
Memory buffers = 2 048
Reads from disk to cache = 7 523
Writes from cache to disk = 4
Чтений из кэша = 437 233

второй запрос, с макс по времени, сработал за такое же время
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Оконные функции для получения среза последних данных
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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