Я пытаюсь суммировать 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


Не храните даты и время отдельно и не храните их в не-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;
Оба выхода:
SOURCE START_ENTRY_DT END_ENTRY_DT CONTENT_LEN 1 2021-09-10 09:50:59 2021-09-10 09:51:02 1036 1 2021-09-10 09:59:59 2021-09-10 09:59:59 139 2 2021-09-10 08:33:20 2021-09-10 08:33:24 2454
Примечание. Хотя запросы выдают один и тот же результат для ваших выборочных данных, они будут давать немного разные результаты, если у вас есть какие-либо образцы данных, где строка 3th не находится в пределах 20 секунд от строки 1st группы, но находится в пределах 20 секунд. 2nd ряда группы.
дб <> рабочий пример здесь
Спасибо за это! Я не знал об этой функции. Не могли бы вы объяснить * и на что ссылается/делает last_time? Я понял, что он работает независимо от того, как его переименовывают.
@fluent Предложение PATTERN устанавливает шаблон сопоставления строк и в основном похоже на сопоставление шаблонов регулярных выражений. В этом случае шаблон within_20 может совпасть ноль или более раз (как обозначено *), и тогда шаблон last_time будет совпадать ровно один раз. Шаблон within_20 определен в предложении DEFINE, но last_time нет, и в этом случае он будет соответствовать любой строке. Да, вы можете переименовать его во что угодно или добавить в предложение DEFINE как что-нибудь, что всегда истинно, например DEFINE last_time AS 1 = 1, within_20 AS ....
Если у вас есть одна запись в момент времени
0, затем вторая в момент времени19и третья в момент времени25, то все они в одной группе (поскольку вторая находится в пределах 20 секунд от первой, а третья — в пределах 20 секунд от второй) или есть две группы (поскольку третья не в пределах 20 секунд от первой)?