еще одна загадка даты заполнить пробел в SQL - PullRequest
2 голосов
/ 27 декабря 2011

Я использую Vertica, что, к сожалению, не позволяет мне использовать CROSS APPLY. И, очевидно, в Vertica нет такой вещи, как CTE.

Вот что у меня есть:

t:
    day    | id | metric | d_metric
-----------+----+--------+----------
2011-12-01 |  1 | 10     | 10
2011-12-03 |  1 | 12     | 2
2011-12-04 |  1 | 15     | 3

Обратите внимание, что в первый день дельта равна значению метрики. Я хотел бы заполнить пробелы, как это:

t_fill:
    day    | id | metric | d_metric
-----------+----+--------+----------
2011-12-01 |  1 | 10     | 10
2011-12-02 |  1 | 10     | 0 -- a delta of 0
2011-12-03 |  1 | 12     | 2
2011-12-04 |  1 | 15     | 3

Я думал о том, как сделать это изо дня в день, но мне бы очень хотелось, чтобы решение работало за один раз.

Я думаю, что мог бы получить что-то, работающее с LAST_VALUE, но я не могу придумать правильные операторы JOIN, которые позволили бы мне правильно разделять и упорядочивать ежедневную историю каждого идентификатора.

редактирование: предположим, у меня есть такая таблица:

calendar:
    day 
------------
2011-01-01
2011-01-02
   ...

, который может быть связан с объединениями. Мое намерение состояло бы в том, чтобы поддерживать диапазон дат в calendar , чтобы соответствовать диапазону дат в t .

редактирование: Еще несколько заметок о том, что я ищу, просто чтобы быть конкретным:

При генерации t_fill я бы хотел точно охватить диапазон дат в t , а также любые даты, которые отсутствуют между ними. Таким образом, правильный t_fill начнется в ту же дату и закончится в ту же дату, что и t . t_fill имеет два свойства:

1) как только идентификатор появляется на какой-то дате, у него всегда будет строка для каждой последующей даты. Это заполнение пробела, подразумеваемое в первоначальном вопросе.

2) Если никакая строка для идентификатора не появится снова после некоторой даты, решение t_fill должно весело генерировать строки с таким же значением метрики (и 0 дельта) от даты, когда эти последние данные указывают вверх до даты окончания т .

Решение может заполнить более ранние даты до начала диапазона дат в t . То есть для любого идентификатора, который появляется после первой даты в t , строки между первой датой в t и первой датой для идентификатора будут заполнены метрикой = 0 и d_metric = 0. Я не предпочитаю подобное решение, поскольку оно имеет более высокий фактор роста для каждого идентификатора, который входит в систему. Но я мог бы легко справиться с этим, выбрав в новую таблицу только те строки, где метрика! = 0 и d_metric! = 0.

Ответы [ 4 ]

1 голос
/ 03 января 2012

Это то, что предложил Джонатан Леффлер, но в старомодном низкоуровневом SQL (без причудливых CTE или оконных функций или агрегирующих подзапросов):

SET search_path='tmp'
DROP TABLE ttable CASCADE;
CREATE TABLE ttable
        ( zday date NOT NULL
        , id INTEGER NOT NULL
        , metric INTEGER NOT NULL
        , d_metric INTEGER NOT NULL
        , PRIMARY KEY (id,zday)
        );
INSERT INTO ttable(zday,id,metric,d_metric) VALUES
 ('2011-12-01',1,10,10)
,('2011-12-03',1,12,2)
,('2011-12-04',1,15,3)
        ;

DROP TABLE ctable CASCADE;
CREATE TABLE ctable
        ( zday date NOT NULL
        , PRIMARY KEY (zday)
        );
INSERT INTO ctable(zday) VALUES
 ('2011-12-01')
,('2011-12-02')
,('2011-12-03')
,('2011-12-04')
        ;

CREATE VIEW v_cte AS (
        SELECT t.zday,t.id,t.metric,t.d_metric
        FROM ttable t
        JOIN ctable c ON c.zday = t.zday
        UNION
        SELECT c.zday,t.id,t.metric, 0
        FROM ctable c, ttable t
        WHERE t.zday < c.zday
        AND NOT EXISTS ( SELECT *
                FROM ttable nx
                WHERE nx.id = t.id
                AND nx.zday = c.zday
                )
        AND NOT EXISTS ( SELECT *
                FROM ttable nx
                WHERE nx.id = t.id
                AND nx.zday < c.zday
                AND nx.zday > t.zday
                )
        )
        ;
SELECT * FROM v_cte;

Результаты:

    zday    | id | metric | d_metric 
------------+----+--------+----------
 2011-12-01 |  1 |     10 |       10
 2011-12-02 |  1 |     10 |        0
 2011-12-03 |  1 |     12 |        2
 2011-12-04 |  1 |     15 |        3
(4 rows)
0 голосов
/ 03 января 2012

Если вы хотите использовать что-то вроде CTE, как насчет использования временной таблицы? По сути, CTE - это представление для определенного запроса.

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

Мне все еще любопытно узнать, почему заполнение пробелов с помощью постоянной интерполяции здесь не работает.

0 голосов
/ 03 января 2012

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

Ваш запрос должен быть сформулирован достаточно точно, что обычно является залогом успеха в любом вопросе о том, «как написать запрос».Я думаю, что вы ищете:

  • Для каждой даты в Календаре между минимальной и максимальной датами, представленными в T (или другом оговоренном диапазоне),
  • Для каждого отдельного идентификатора, представленного в T,
  • Найти метрику для данного идентификатора для самой последней записи в T на дату или до даты.

Это дает вам полный список дат с метриками.

Затем вам нужно самостоятельно объединить две копии этого списка с датами, расположенными на расстоянии одного дня, для формирования дельт.

Обратите внимание, что если некоторые значения идентификаторов не появляются в начале диапазона дат, ониЯ не вернусь.

С этим, как руководством, я думаю, вы сможете начать.

0 голосов
/ 02 января 2012

Я не являюсь пользователем Vertica, но если вы не хотите использовать их встроенную поддержку для заполнения GAP, здесь вы можете найти более общее решение только для SQL.

...