【问题标题】:adding missing date in a table in PostgreSQL在 PostgreSQL 的表中添加缺失的日期
【发布时间】:2012-10-27 13:04:19
【问题描述】:

我有一个包含 2002 年每一天的数据的表,但它缺少一些日期。即,2002 年有 354 条记录(而不是 365 条)。对于我的计算,我需要在表中包含 Null 值的缺失数据

+-----+------------+------------+
| ID  |  rainfall  | date       |
+-----+------------+------------+
| 100 |  110.2     | 2002-05-06 |
| 101 |  56.6      | 2002-05-07 |
| 102 |  65.6      | 2002-05-09 |
| 103 |  75.9      | 2002-05-10 |
+-----+------------+------------+

您看到 2002-05-08 不见了。我希望我的决赛桌是这样的:

+-----+------------+------------+
| ID  |  rainfall  | date       |
+-----+------------+------------+
| 100 |  110.2     | 2002-05-06 |
| 101 |  56.6      | 2002-05-07 |
| 102 |            | 2002-05-08 |
| 103 |  65.6      | 2002-05-09 |
| 104 |  75.9      | 2002-05-10 |
+-----+------------+------------+

有没有办法在 PostgreSQL 中做到这一点?

我是否将结果作为查询结果(不一定是更新的表)并不重要

【问题讨论】:

  • ID是序列号吗?插入日期后是否需要全部向上移动?
  • 是的,我需要像我放在这里的示例表一样更改 ID。在一整年结束时,最后一个 ID 将是 365。
  • 我确实阅读了您的其他帖子(已回答),我认为您做错了。您不应依赖 ID 进行排序或分组。这是重要的“日期”。如果你想填补空白,那没关系,它可以使查询更容易,但不是绝对必要的。在“日期”列上创建唯一索引以确保它们不会重复。我正在回答如何填补空白。
  • 是的,你是对的。我必须根据日期而不是 ID 来做。但问题是我无法根据日期进行分组。我会试试看。谢谢

标签: postgresql datetime


【解决方案1】:

date 是标准 SQL 中的 reserved word 和 PostgreSQL 中数据类型的名称。 PostgreSQL 允许它作为标识符,但这并不是一个好主意。我改用thedate 作为列名。

不要依赖代理 ID 中没有空白。这几乎总是一个坏主意。将这样的 ID 视为没有意义的唯一数字,即使它似乎大部分时间都带有某些其他属性。

在这种特殊情况下,@Clodoaldo commentedthedate 似乎是一个完美的主键,而列 id 只是粗鲁 - 我删除了:

CREATE TEMP TABLE tbl (thedate date PRIMARY KEY, rainfall numeric);
INSERT INTO tbl(thedate, rainfall) VALUES
  ('2002-05-06', 110.2)
, ('2002-05-07', 56.6)
, ('2002-05-09', 65.6)
, ('2002-05-10', 75.9);

查询

查询全表:

SELECT x.thedate, t.rainfall  -- rainfall automatically NULL for missing rows
FROM (
   SELECT generate_series(min(thedate), max(thedate), '1d')::date AS thedate
   FROM   tbl
   ) x
LEFT   JOIN tbl t USING (thedate)
ORDER  BY x.thedate

类似于@a_horse_with_no_name 发布的内容,但简化并忽略了修剪后的id

填补表格中第一个和最后一个日期之间的空白。如果可能存在领先/落后差距,请相应扩展。您可以像 @Clodoaldo 演示的那样使用 date_trunc() - 但他的查询存在语法错误并且可以更简单。

插入缺失的行

最快、最易读的方法是NOT EXISTSanti-semi-join。

INSERT INTO tbl (thedate, rainfall)
SELECT x.thedate, NULL
FROM (
   SELECT generate_series(min(thedate), max(thedate), '1d')::date AS thedate
   FROM   tbl
   ) x
WHERE NOT EXISTS (SELECT 1 FROM tbl t WHERE t.thedate = x.thedate)

【讨论】:

    【解决方案2】:

    只需对返回 2002 年所有日期的查询进行外连接:

    with all_dates as (
      select date '2002-01-01' + i as date_col
      from generate_series(0, extract(doy from date '2002-12-31')::int - 1) as i
    )
    select row_number() over (order by ad.date_col) as id, 
           t.rainfall,
           ad.date_col as date
    from all_dates ad
      left join your_table t on ad.date_col = t.date
    order by ad.date_col;
    

    这不会改变你的表格,它只会产生你想要的结果。

    请注意,生成的 id 列不会包含与表中的 ID 列相同的值,因为它只是结果集中的一个计数器。

    您也可以将row_number() 函数替换为extract(doy from ad.date_col)

    【讨论】:

    • 我为我的表运行了这个,我遇到了以下错误:错误:对表“ad”的 FROM 子句条目的无效引用第 13 行:在 ad.date_col = 上左连接 rwanda1 t t.date 提示:表“ad”有一个条目,但不能从这部分查询中引用。
    【解决方案3】:

    填补空白。这不会重新排序 ID:

    insert into t (rainfall, "date") values
    select null, "date"
    from (
        select d::date as "date"
        from (
            t
            right join
            generate_series(
                (select date_trunc('year', min("date")) from t)::timestamp,
                (select max("date") from t),
                '1 day'
            ) s(d) on t."date" = s.d::date
        where t."date" is null
        ) q
    ) s
    

    【讨论】:

    • 如果表格中缺少 12 月 31 日怎么办?在这种情况下, generate_series() 不会创建足够的值。
    • @a_horse_with_no_name 是的,这是设计使然。我认为他不想要或不应该想要未来的约会。
    • 不,他希望结果中有 365 行。
    • @a_horse_with_no_name 阅读我对这个问题的评论,为什么我认为这是错误的。
    【解决方案4】:

    您必须完全重新创建您的表,因为索引必须更改。

    更好的方法是使用您喜欢的 dbi 语言,创建一个忽略 ID 的循环并将值放入具有新序列化 ID 的新表中。

    for day in (whole needed calendar)
        value = select rainfall from oldbrokentable where date = day
        insert into newcleanedtable date=day, rainfall=value, id=serialized
    

    (这不是真正的代码!只是概念性的,以适应您喜欢的脚本语言)

    【讨论】:

    • 他还可以将所有 ID 重新编号为无意义的值 (UPDATE table SET id=-id),然后正确地重新编号 (UPDATE table SET id=q.val FROM (SELECT date, row_number() OVER (ORDER BY date) AS val FROM table) q WHERE q.date=table.date)。也就是说,我同意@Clodoaldo 的观点,这是错误的。
    • .. 而且效率极低。 如果您想编写一个新的干净表,您可以使用单个 SQL 命令生成新的idrow_number() 或从旧版本中没有窗口函数的序列。但整个想法并不好。依赖代理键中没有空位并不是一个好主意。
    猜你喜欢
    • 1970-01-01
    • 2017-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 2020-01-26
    • 2020-09-16
    • 2021-04-06
    相关资源
    最近更新 更多