保留子查询的顺序(使用 GROUP BY 和 ORDER BY)

Preserve order from subquery (with GROUP BY and ORDER BY)

我正在使用智能手机从加速度计收集数据,然后将其保存在服务器的 postgresql 数据库中。基本上,每次我读取加速度计时,我都会保存智能手机当时所在的 latitude/longitude,以及它发生的时间戳。

现在,我想按照保存时的相同顺序(按时间戳排序)从数据库中读取每个不同的位置 (latitude/longitude)。我想知道每个位置重复了多少读数。

让我举个例子来解释。假设我的数据库中有以下 table:

+------------+------------+-----------+
| latitude   | longitude  | timestamp |
+------------+------------+-----------+
| 43.1784771 | -8.5956853 | 930560045 |
| 43.1784771 | -8.5956853 | 930560054 |
| 41.2784813 | -7.5956853 | 930560063 |
| 42.1786173 | -8.5951757 | 930560072 |
| 42.1786173 | -8.5951757 | 930560082 |
+------------+------------+-----------|

请注意,我的元素按时间戳排序,并且有 2 个重复位置。所以,我想查询数据库以查看重复的位置并得到以下结果:

+------------+------------+-------+
| latitude   | longitude  | count |
+------------+------------+-------+
| 43.1784771 | -8.5956853 | 2     |
| 41.2784813 | -7.5956853 | 1     |
| 42.1786173 | -8.5951757 | 2     |
+------------+------------+-------|

问题是我希望元素按原始 table 排序(按时间戳排序)。 我正在尝试以下查询,但它不起作用,因为子查询中的顺序无关紧要:

SELECT latitude, longitude, count(*)
FROM 
    (SELECT latitude, longitude, timestamp FROM table ORDER BY timestamp asc) subquery1
GROUP BY latitude, longitude

我一直在 Whosebug 中寻找答案,最接近的答案是:Is order in a subquery guaranteed to be preserved? 但是,它不适用于我的情况,因为我需要 "group by" 子句。谁能帮帮我吗?

SELECT 
latitude, 
longitude, 
count(1) as "Count", 
min(timestamp) as "Start",
max(timestamp) as "End"

FROM table 
GROUP BY latitude, longitude
ORDER BY min(timestamp) asc
create or replace function foo(
  out latitude numeric, 
  out longitude numeric,
  out cnt int,
  out start_time numeric,
  out end_time numeric
) returns setof record as $$
declare
  c record;
  p record;
  i int := 1;
begin
  select null into p;
  for c in (select * from table order by timestamp) 
  loop
    if p is null then
      start_time := c.timestamp;
    elsif p.latitude <> c.latitude and p.longitude <> c.longitude then
      latitude := p.latitude; 
      longitude := p.longitude;
      cnt := i;
      end_time := p.timestamp;
      return next;
      i := 1;
      start_time := p.timestamp;
    else
      i := i + 1;
    end if;
    p := c;
  end loop;
  if p.latitude = c.latitude and p.longitude = c.longitude then
    latitude := p.latitude; 
    longitude := p.longitude;
    cnt := i;
    end_time := p.timestamp;
    return next;
  end if;
  return;
end; $$ immutable language plpgsql;

用法:

select * from foo();

作为小福利,它还为每个系列提供 start/end 时间戳。

子查询不保留顺序,但可以为 array_agg 操作定义顺序,我们可以使用它来确定更广泛的顺序。例如试试这个:

SELECT latitude, longitude, count(*), (array_agg(timestamp order by timestamp))[1] as first_time
FROM table GROUP BY latitude, longitude;

在 OP 的情况下,min(timestamp) 可能更简单,但如果有更复杂的排序,这可能是一个更简洁的选项。