【问题标题】:Preserve order from subquery (with GROUP BY and ORDER BY)保留子查询的顺序(使用 GROUP BY 和 ORDER BY)
【发布时间】:2016-06-25 17:26:34
【问题描述】:

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

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

让我用一个例子来解释。考虑一下我的数据库中有下表:

+------------+------------+-----------+
| 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     |
+------------+------------+-------|

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

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

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

【问题讨论】:

  • SELECT latitude, longitude, count(*) from table GROUP BY latitude, longitude ORDER BY max(timestamp); /* or min(timestamp)*/

标签: sql postgresql group-by sql-order-by


【解决方案1】:
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

【讨论】:

  • 这里的问题是您可能在两个不同的时间间隔处于同一位置。例如,我每天上班,每天早上 9 点到下午 5 点将手机放在同一个全球位置。正如您所描述的,此查询并未向您显示。
  • 提示:将[...] 更改为"..."。它是 PostgreSQL,而不是 MS SQL 服务器。
  • 其实,一个非常简单的解决方案!谢谢!我知道在两个不同的时间间隔相同位置的问题,但现在这不是我的具体问题的问题。
  • @MaxSorin,Abelisto 的以下回答解决了不同时间间隔相同位置的问题:)
【解决方案2】:
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();

作为一个小奖励,它还为每个系列提供开始/结束时间戳。

【讨论】:

  • 谢谢!您的解决方案具有以两个不同的时间间隔分开相同位置的优势。在我的具体情况下,我不需要它,但确实值得指出!
  • @tjiagoM 如果需要,请随意使用它:)
【解决方案3】:

子查询不会保留顺序,但可以为 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) 可能更简单,但如果有更复杂的排序,这可能是一个更简洁的选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-21
    • 2021-11-17
    • 1970-01-01
    • 1970-01-01
    • 2014-10-12
    • 2012-12-31
    • 1970-01-01
    相关资源
    最近更新 更多