在PostgreSQL中的表中添加缺少的日期
我有一个表,其中包含2002年每天的数据,但它缺少一些日期。即2002年有354项记录(而不是365项)。对于我的计算,我需要在表中包含空值的缺失数据在PostgreSQL中的表中添加缺少的日期,postgresql,datetime,Postgresql,Datetime,我有一个表,其中包含2002年每天的数据,但它缺少一些日期。即2002年有354项记录(而不是365项)。对于我的计算,我需要在表中包含空值的缺失数据 +-----+------------+------------+ | ID | rainfall | date | +-----+------------+------------+ | 100 | 110.2 | 2002-05-06 | | 101 | 56.6 | 2002-05-07 | | 10
+-----+------------+------------+
| 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中有这样做的方法吗
如果我的结果只是一个查询结果(不一定是一个更新的表),这并不重要。您必须完全重新创建表,因为索引必须更改 更好的方法是使用您喜欢的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重新排序:
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
只需对返回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列相同的值,因为它只是结果集中的一个计数器
您还可以将行号()
函数替换为提取(doy from ad.date\u col)
date
是标准SQL中的数据类型,也是PostgreSQL中的数据类型名称。PostgreSQL允许它作为标识符,但这并不是一个好主意。我使用日期作为列名
不要依赖于代理ID中没有空白。这几乎总是一个坏主意。将这样一个ID视为没有意义的唯一数字,即使它在大多数情况下似乎带有某些其他属性
在这种情况下,date
似乎是一个完美的主键,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
与发布的内容类似,但简化并忽略删减的id
填补表中第一个和最后一个日期之间的空白。如果可能存在超前/滞后间隙,则相应延长。您可以像演示的那样使用date\u trunc()
插入缺少的行
最快和最可读的方法是不存在
反半联接
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)
身份证是序列号吗?当插入日期时,是否需要全部向上移动?是的,我需要更改ID,就像我放在这里的示例表一样。在整整一年结束时,最后一个ID将是365。我确实读了你的其他帖子(回答了一个),我认为你做错了。您不应该依赖ID进行排序或分组。这是一个重要的日期。如果你想填补空白,这是可以的,它可以使查询更容易,但严格来说不是必要的。在“日期”列上创建一个唯一的索引,以确保它们不会重复。我在回答如何填补空白。是的,你是对的。我必须根据日期而不是ID进行分组。但问题是我无法根据日期进行分组。我试试看。谢谢,如果12月31日不在表中怎么办?在这种情况下,generate_series()将无法创建足够的值。@a_horse_带有_no_name Yes,这是设计的。我认为他不想要或不应该想要未来的日期。不,他想要结果中的365行。@a_nou horse_和_No_name阅读了我关于我为什么认为这是错误的问题的评论。他还可以将所有id重新编号为无意义的值(UPDATE table SET id=-id
),然后正确地重新编号(UPDATE table SET id=q.val FROM)(选择date,row_number()OVER(ORDER BY date)作为val FROM table)q其中q.date=table.date
)。也就是说,我同意@Clodoaldo的说法,这是错误的……而且效率极低。如果您想编写一个新的干净表,您可以使用一个SQL命令,在其中使用row_number()生成新的id
或从没有窗口函数的旧版本中的序列。但整个想法都不好。依靠代理键中没有间隙不是一个好主意。我为我的表运行了此操作,遇到了以下错误:错误:对表“ad”的from子句项的引用无效第13行:left join rwanda1 t on ad.date\u col=t.date提示:表“ad”有一个条目,但不能从查询的这一部分引用它。