当组归属取决于前一行时,如何在 postgresql 中标记组?
Posted
技术标签:
【中文标题】当组归属取决于前一行时,如何在 postgresql 中标记组?【英文标题】:How to label groups in postgresql when group belonging depends on the preceding line? 【发布时间】:2015-12-16 14:05:34 【问题描述】:我想在请求中用最后一个已知值填充所有 Null 值。 当它在表中而不是在请求中时,很容易:
如果我按如下方式定义和填写表格:
CREATE TABLE test_fill_null (
date INTEGER,
value INTEGER
);
INSERT INTO test_fill_null VALUES
(1,2),
(2, NULL),
(3, 45),
(4,NULL),
(5, null);
SELECT * FROM test_fill_null ;
date | value
------+-------
1 | 2
2 |
3 | 45
4 |
5 |
那我只需要这样填写:
UPDATE test_fill_null t1
SET value = (
SELECT t2.value
FROM test_fill_null t2
WHERE t2.date <= t1.date AND value IS NOT NULL
ORDER BY t2.date DESC
LIMIT 1
);
SELECT * FROM test_fill_null;
date | value
------+-------
1 | 2
2 | 2
3 | 45
4 | 45
5 | 45
但是现在,我有一个请求,就像这个:
WITH
pre_table AS(
SELECT
id1,
id2,
tms,
CASE
WHEN tms - lag(tms) over w < interval '5 minutes' THEN NULL
ELSE id2
END as group_id
FROM
table0
window w as (partition by id1 order by tms)
)
当前一个点距离超过 5 分钟时,group_id 设置为 id2,否则为 null。通过这样做,我希望得到一组彼此跟随不到 5 分钟的点,并且每组之间的间隔超过 5 分钟。
那我不知道该怎么办了。我试过了:
SELECT distinct on (id1, id2)
t0.id1,
t0.id2,
t0.tms,
t1.group_id
FROM
pre_table t0
LEFT JOIN (
select
id1,
tms,
group_id
from pre_table t2
where t2.group_id is not null
order by tms desc
) t1
ON
t1.tms <= t0.tms AND
t1.id1 = t0.id1
WHERE
t0.id1 IS NOT NULL
ORDER BY
id1,
id2,
t1.tms DESC
但在最终结果中,我有一些连续两个点相距超过 5 分钟的组。在这种情况下,它们应该是两个不同的组。
【问题讨论】:
所以每 5 分钟有一个 NULL 值,同一组可以无限期地存在,对吗?一如既往:请您的 Postgres 版本。一开始的好测试用例结果证明不适用于您的实际问题。为您的实际问题提供测试用例会更有意义。 是的。我的版本是 9.3.10。我无法按原样提供数据。如果你愿意,我会用相同的模型制作一些假数据。 假定的基本行顺序也未定义。您是通过id1
、tms
还是id1, id2, tms
订购?
@Erwin Brandstetter:对我帮助最大的是我可以在窗口上使用计数,并且它会随着每个非空值而增加。我应该编辑我的问题的标题以反映这一点吗?
如果您能想出一个更贴近问题本质的标题,请继续!
【参考方案1】:
“选择中的选择” 通常称为“子选择”或“子查询”。在您的特定情况下,它是一个相关子查询。 LATERAL
连接(postgres 9.3 中的新功能)可以在很大程度上用更灵活的解决方案替换相关子查询:
我认为你在这里也不需要。
对于您的第一种情况,此查询可能更快更简单:
SELECT date, max(value) OVER (PARTITION BY grp) AS value
FROM (
SELECT *, count(value) OVER (ORDER BY date) AS grp
FROM test_fill_null
) sub;
count()
只计算非空值,因此grp
会随着每个非空value
递增,从而根据需要形成组。在外部SELECT
中为每个grp
选择一个 非空value
很简单。
对于您的第二种情况,我假设行的初始顺序由(id1, id2, tms)
确定,如您的一个查询所示。
SELECT id1, id2, tms
, count(step) OVER (ORDER BY id1, id2, tms) AS group_id
FROM (
SELECT *, CASE WHEN lag(tms, 1, '-infinity') OVER (PARTITION BY id1 ORDER BY id2, tms)
< tms - interval '5 min'
THEN true END AS step
FROM table0
) sub
ORDER BY id1, id2, tms;
适应您的实际订单。其中之一可能涵盖它:
PARTITION BY id1 ORDER BY id2 -- ignore tms
PARTITION BY id1 ORDER BY tms -- ignore id2
SQL Fiddle 带有扩展示例。
相关:
Select longest continuous sequence【讨论】:
我尝试了 SQL Fiddle,但我必须更正数据以反映我的情况。通过这样做,我看到 id1 不是一个好名字,我误导了你。 id1 可以被视为外键。我更正如下: INSERT INTO table0 VALUES (1,2, '2015-12-16 16:09:00+01'), (1,3, '2015-12-16 16:11:00+01') , (2,4, '2015-12-16 16:10:00+01'), (2,5, '2015-12-16 16:12:00+01'), (1,6, '2015 -12-16 16:13:01+01');由于更正您的解决方案似乎不起作用,但我会继续努力。如果我完全理解你的答案,我可能会纠正它。 实际上,对于我的第二种情况,您给了我另一种方式来获得我的“上下文请求”,而不是“标签请求”。 (没有太大不同,您只需将 'tms' 传递给不等式的另一边)。在您对第一个案例的回答的帮助下,我完成了它,所以我想要它。 [sql Fiddle)(sqlfiddle.com/#!15/af4e64/9) 你怎么看? 我的查询从 6 分钟缩短到了 5 秒,谢谢!只需使用查询的第二部分在我的小提琴中编辑您的答案,我将验证您的答案 甚至可以这样简化:sql fiddle @RemiDelassus:我添加了一个更新的查询,进一步简化了一些。在 Postgres 中,子查询通常比 CTE 快,而且我们在这里需要 CTE。【参考方案2】:在编辑我的问题时,我找到了解决方案。虽然它非常低,比我在表格中的示例要低得多。有什么改进的建议吗?
SELECT
t2.id1,
t2.id2,
t2.tms,
(
SELECT t1.group_id
FROM pre_table t1
WHERE
t1.tms <= t2.tms
AND t1.group_id IS NOT NULL
AND t2.id1 = t2.id1
ORDER BY t1.tms DESC
LIMIT 1
) as group_id
FROM
pre_table t2
ORDER BY
t2.id1
t2.id2
t2.tms
正如我所说,选择中的选择
【讨论】:
以上是关于当组归属取决于前一行时,如何在 postgresql 中标记组?的主要内容,如果未能解决你的问题,请参考以下文章
当组权限似乎正确时,如何修复“拨号 unix /var/run/docker.sock:连接:权限被拒绝”?
如何在 eloquent 中执行依赖于表的最后一行的更新和保存操作?