2

不幸的是,我正在使用 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 语句,让我可以正确地对每个 id 的日常历史进行分区和排序。

编辑:假设我有一个这样的表:

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

可以参与连接。我的意图是保持calendar中的日期范围与t中的日期范围相匹配。

编辑:关于我正在寻找的内容的更多说明,具体而言:

在生成t_fill时,我想准确覆盖t中的日期范围,以及介于两者之间的任何日期。因此,正确的t_fill将在与t相同的日期开始并在相同的日期结束。 t_fill有两个属性:

1)一旦某个 id 出现在某个日期,它总是会在以后的每个日期都有一行。这是原始问题中隐含的填补空白。

2)如果在某个日期之后不再出现 id 的行,则t_fill解决方案应该愉快地生成从最后一个数据点的日期到t的结束日期具有相同度量值(和 0 增量)的行。

解决方案可能会回填较早的日期,直到t中日期范围的开始。也就是说,对于出现在 t 中的第一个日期之后的任何 id,t的第一个日期和该 id 的第一个日期之间的行将填充 metric=0 和 d_metric=0。我不喜欢这种解决方案,因为它对进入系统的每个 id 都有更高的增长因子。但是我可以通过仅在新表中选择 metric!=0 和 d_metric!=0 的行来轻松处理它。

4

4 回答 4

1

这与 Jonathan Leffler 提出的有关,但涉及老式的低级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)
于 2012-01-03T16:56:13.173 回答
0

鉴于完整的日历表,它是可行的,尽管并非微不足道。如果没有日历表,那就更难了。

您的查询需要适度准确地陈述,这通常是“如何编写查询”的任何问题的一半。我认为您正在寻找:

  • 对于日历中在 T(或其他规定范围)中表示的最小和最大日期之间的每个日期,
  • 对于 T 中表示的每个不同的 ID,
  • 查找日期或之前 T 中最新记录的给定 ID 的指标。

这为您提供了带有指标的完整日期列表。

然后,您需要自行加入该列表的两个副本,日期相隔一天以形成增量。

请注意,如果某些 ID 值未出现在日期范围的开头,它们将不会显示。

有了这个指导,我相信你应该可以开始了。

于 2012-01-03T16:00:04.860 回答
0

我不是 Vertica 用户,但如果您不想使用他们对 GAP 填充的原生支持,您可以在此处找到更通用的仅限 SQL 的解决方案。

于 2012-01-02T18:35:52.740 回答
0

如果您想使用 CTE 之类的东西,那么使用临时表怎么样?本质上,CTE 是特定查询的视图。

根据您的需要,您可以使临时表事务或会话范围。

我仍然很想知道为什么用常量插值填充间隙在这里不起作用。

于 2012-01-03T15:49:40.690 回答