Postgres 按相似日期将数据分组
Postgres put Data into groups by similar date
我有一个桌子,我会这样称呼 data_rows
:
create table if not exists data_rows
(
id integer not null,
constraint data_rows_to_group
primary key (id),
date date not null,
group_id int,
--more fields that are not relevant
);
当我按日期对行进行排序时,如果与前一行的日期差异 >7 天(可以是另一个 time_span 但让我们保留它,我希望这些行有一个新的 group_id在 7 天)
因此,按日期排序时具有相同 group_id 的所有行的日期差异 <= 7 天。
例如:
id date group id
1 12.01.2019 0
2 15.01.2019 0
3 21.01.2019 0
4 05.02.2019 1
5 08.02.2019 1
6 20.02.2019 2
7 30.02.2019 3
8 30.02.2019 3
(特别是1和3在同一组,虽然相差>7,但在组中后面两行相差>7)
我知道如何在 python 或 c# 或类似语言中以过程方式执行此操作。
但是,如果我可以在 postgresql 服务器上执行此操作,那将非常有用,因为它包含大量数据,并且它也可以防止出现单点故障,这也将是一次重要的学习经历。
以下是我在 C# 中的做法,这样您就会明白我想要什么:
using System;
using System.Collections.Generic;
using System.Linq;
class DataRows
{
public int Id { get; set; }
public DateTime Date { get; set; }
public int GroupId { get; set; }
}
class GroupMarking
{
public DataRows[] RowsWithGroupIds(IEnumerable<DataRows> relevantDataRows, TimeSpan betweenSpan)
{
var currentGroupId = 0;
var rows = relevantDataRows.OrderBy(p => p.Date).ToArray();
rows[0].GroupId = currentGroupId;
for (var i = 1; i < rows.Length; i++)
{
if (rows[i].Date -
rows[i - 1].Date >= betweenSpan)
{
currentGroupId++;
}
rows[i].GroupId = currentGroupId;
}
return rows;
}
}
这在 postgresql 中可行吗?我知道 Postgres 中有循环。我更喜欢没有循环的解决方案,但如果没有它们就不可能。
如何在不使用过程语言的情况下在 group_id 列中创建 ID?
这是一个gaps-and-islands问题,可以通过将差异大于7天的信息转化为标志,然后对该标志求和来解决:
select id, "date", sum(flag) over (order by "date") as group_id
from (
select id, "date",
("date" - lag("date", 1, "date") over (order by "date") > 7)::int as flag
from data_rows
) t
order by "date"
表达式 "date" - lag("date", 1, "date") over (order by "date")
计算 "current" 行与前一行之间的日期差异。然后检查它是否大于 7 天,并将布尔值转换为整数 (0, 1),以便可以在其上使用外部 运行 总和。
(我把无效日期2019-02-30换成了2019-02-28)
我有一个桌子,我会这样称呼 data_rows
:
create table if not exists data_rows
(
id integer not null,
constraint data_rows_to_group
primary key (id),
date date not null,
group_id int,
--more fields that are not relevant
);
当我按日期对行进行排序时,如果与前一行的日期差异 >7 天(可以是另一个 time_span 但让我们保留它,我希望这些行有一个新的 group_id在 7 天) 因此,按日期排序时具有相同 group_id 的所有行的日期差异 <= 7 天。 例如:
id date group id
1 12.01.2019 0
2 15.01.2019 0
3 21.01.2019 0
4 05.02.2019 1
5 08.02.2019 1
6 20.02.2019 2
7 30.02.2019 3
8 30.02.2019 3
(特别是1和3在同一组,虽然相差>7,但在组中后面两行相差>7)
我知道如何在 python 或 c# 或类似语言中以过程方式执行此操作。 但是,如果我可以在 postgresql 服务器上执行此操作,那将非常有用,因为它包含大量数据,并且它也可以防止出现单点故障,这也将是一次重要的学习经历。
以下是我在 C# 中的做法,这样您就会明白我想要什么:
using System;
using System.Collections.Generic;
using System.Linq;
class DataRows
{
public int Id { get; set; }
public DateTime Date { get; set; }
public int GroupId { get; set; }
}
class GroupMarking
{
public DataRows[] RowsWithGroupIds(IEnumerable<DataRows> relevantDataRows, TimeSpan betweenSpan)
{
var currentGroupId = 0;
var rows = relevantDataRows.OrderBy(p => p.Date).ToArray();
rows[0].GroupId = currentGroupId;
for (var i = 1; i < rows.Length; i++)
{
if (rows[i].Date -
rows[i - 1].Date >= betweenSpan)
{
currentGroupId++;
}
rows[i].GroupId = currentGroupId;
}
return rows;
}
}
这在 postgresql 中可行吗?我知道 Postgres 中有循环。我更喜欢没有循环的解决方案,但如果没有它们就不可能。 如何在不使用过程语言的情况下在 group_id 列中创建 ID?
这是一个gaps-and-islands问题,可以通过将差异大于7天的信息转化为标志,然后对该标志求和来解决:
select id, "date", sum(flag) over (order by "date") as group_id
from (
select id, "date",
("date" - lag("date", 1, "date") over (order by "date") > 7)::int as flag
from data_rows
) t
order by "date"
表达式 "date" - lag("date", 1, "date") over (order by "date")
计算 "current" 行与前一行之间的日期差异。然后检查它是否大于 7 天,并将布尔值转换为整数 (0, 1),以便可以在其上使用外部 运行 总和。
(我把无效日期2019-02-30换成了2019-02-28)