Время округления для агрегирования записей, закрывающихся по времени

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

Время округления для агрегирования записей, закрывающихся по времени

Моя текущая попытка состоит в том, чтобы округлить метку времени до секунд unix, но возникают проблемы, когда округление (unix_ceil) не дает одинакового числа для контента, который должен быть сгруппирован вместе. Как я могу гарантировать, что записи, которые находятся в пределах ~ 20 секунд друг от друга, суммируются? Обычно между отдельными записями, которые не следует группировать вместе, существует интервал не менее нескольких минут (например, 999+37 в ~09:50, а затем 136 в ~09:59 для source = 1).

SELECT source
,entry_dt 
,SUM(content_len) AS full_length
FROM (
    SELECT source
    ,entry_dt
    ,entry_time
    ,(TO_DATE(CONCAT(entry_dt, entry_time), 'yyyymmddHH24MISS') - TO_DATE('2020-01-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')) * 86400 AS unix_from_2020
    ,CEIL(86400 * (TO_DATE(CONCAT(entry_dt, entry_time), 'yyyymmddHH24MISS') - TO_DATE('2020-01-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')) / 20) * 20 AS unix_ceil -- round to 20 seconds
    ,content_len
    FROM schema.text_length_records
) s
GROUP BY source, entry_dt, unix_ceil

Если у вас есть одна запись в момент времени 0, затем вторая в момент времени 19 и третья в момент времени 25, то все они в одной группе (поскольку вторая находится в пределах 20 секунд от первой, а третья — в пределах 20 секунд от второй) или есть две группы (поскольку третья не в пределах 20 секунд от первой)?

MT0 18.03.2022 11:50
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
1
30
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Не храните даты и время отдельно и не храните их в не-DATE типах данных. В Oracle a DATE — это двоичный тип данных, состоящий из 7 байтов, которые содержат компоненты век, год века, месяц, день, час, минуту и ​​секунду, и он ВСЕГДА содержит эти компоненты и НИКОГДА хранится в любом конкретном формате.

Затем вы можете использовать один столбец DATE для хранения даты и времени и делать это более эффективно и с лучшей проверкой ошибок, чем если бы вы хранили значения отдельно в виде строк или чисел.


В Oracle 12 вы можете использовать MATCH_RECOGNIZE для построчной обработки.

Если вы хотите, чтобы каждая строка была в пределах 20 секунд от предыдущей строки, тогда:

SELECT *
FROM   (
  SELECT source,
         TO_DATE(entry_dt || LPAD(entry_time, '0', 6), 'YYYYMMDDHH24MISS') AS entry_dt,
         content_len
  FROM   text_length_records
)
MATCH_RECOGNIZE(
  PARTITION BY source
  ORDER     BY entry_dt
  MEASURES
    FIRST(entry_dt)  AS start_entry_dt,
    LAST(entry_dt)   AS end_entry_dt,
    SUM(content_len) AS content_len
  ONE ROW PER MATCH
  PATTERN (within_20* last_time)
  DEFINE
    within_20 AS entry_dt + INTERVAL '20' SECOND >= NEXT(entry_dt)
)

Если вы хотите, чтобы каждая строка находилась в пределах 20 секунд от первой строки группы, то:

SELECT *
FROM   (
  SELECT source,
         TO_DATE(entry_dt || LPAD(entry_time, 6, '0'), 'YYYYMMDDHH24MISS') AS entry_dt,
         content_len
  FROM   text_length_records
)
MATCH_RECOGNIZE(
  PARTITION BY source
  ORDER     BY entry_dt
  MEASURES
    FIRST(entry_dt)  AS start_entry_dt,
    LAST(entry_dt)   AS end_entry_dt,
    SUM(content_len) AS content_len
  ONE ROW PER MATCH
  PATTERN (within_20*)
  DEFINE
    within_20 AS entry_dt <= FIRST(entry_dt) + INTERVAL '20' SECOND
)

Что для примера данных:

CREATE TABLE text_length_records (source, entry_dt, entry_time, content_len) AS
SELECT 1, 20210910, 95059,  37 FROM DUAL UNION ALL
SELECT 1, 20210910, 95102, 999 FROM DUAL UNION ALL
SELECT 1, 20210910, 95959, 139 FROM DUAL UNION ALL
SELECT 2, 20210910, 83320, 999 FROM DUAL UNION ALL
SELECT 2, 20210910, 83322, 999 FROM DUAL UNION ALL
SELECT 2, 20210910, 83324, 456 FROM DUAL;

Оба выхода:

SOURCESTART_ENTRY_DTEND_ENTRY_DTCONTENT_LEN
12021-09-10 09:50:592021-09-10 09:51:021036
12021-09-10 09:59:592021-09-10 09:59:59139
22021-09-10 08:33:202021-09-10 08:33:242454

Примечание. Хотя запросы выдают один и тот же результат для ваших выборочных данных, они будут давать немного разные результаты, если у вас есть какие-либо образцы данных, где строка 3th не находится в пределах 20 секунд от строки 1st группы, но находится в пределах 20 секунд. 2nd ряда группы.

дб <> рабочий пример здесь

Спасибо за это! Я не знал об этой функции. Не могли бы вы объяснить * и на что ссылается/делает last_time? Я понял, что он работает независимо от того, как его переименовывают.

fluent 21.03.2022 04:09

@fluent Предложение PATTERN устанавливает шаблон сопоставления строк и в основном похоже на сопоставление шаблонов регулярных выражений. В этом случае шаблон within_20 может совпасть ноль или более раз (как обозначено *), и тогда шаблон last_time будет совпадать ровно один раз. Шаблон within_20 определен в предложении DEFINE, но last_time нет, и в этом случае он будет соответствовать любой строке. Да, вы можете переименовать его во что угодно или добавить в предложение DEFINE как что-нибудь, что всегда истинно, например DEFINE last_time AS 1 = 1, within_20 AS ....

MT0 21.03.2022 10:12

Другие вопросы по теме