【问题标题】:Postgres: getting latest rows for an array of keysPostgres:获取键数组的最新行
【发布时间】:2014-11-17 08:26:33
【问题描述】:

我有一个简单的事件日志表:

uid | event_id | event_data
----+----------+------------
  1 |  1       | whatever
  2 |  2       |
  1 |  3       |
  4 |  4       |
  4    5       |

如果我需要给定用户的最新事件,那很明显:

SELECT * FROM events WHERE uid=needed_uid ORDER BY event_id DESC LIMIT 1

但是,假设我需要数组中每个用户 ID 的最新事件。例如,对于上面的表格和用户{1, 4},我期望事件{3, 5}。在不使用 pgSQL 循环的情况下,这是否可以在纯 SQL 中实现?

【问题讨论】:

  • 你真的想要一个 array 作为结果吗?还是输入数组中元素顺序的整行?这将提出一个更有趣的问题,如何保留给定的顺序...
  • @ErwinBrandstetter 我需要这些行,但顺序并不重要。
  • 无论如何,我添加了另一个答案,有更好的表现潜力。

标签: sql postgresql greatest-n-per-group


【解决方案1】:

Postgres 特定的解决方案是使用distinct on,这通常比使用窗口函数的解决方案更快:

select distinct on (uid) uid, event_id, event_data
from events 
where uid in (1,4)
order by uid, event_id DESC

【讨论】:

  • 为什么是order by uid, event_id,而不仅仅是event_id
  • @bereal:因为distinct on 要求order by 以为distinct on 指定的列开头。尝试在没有它的情况下运行它;)
  • 我可以用上面的答案出错。看来(uid)后面的,需要去掉。
【解决方案2】:

试试下面的查询:

select DesiredColumnList 
from 
(
    select *, row_number() over ( partition by uid order by event_id desc) rn
    from yourtable
) t
where rn = 1

Row_Number 将为event_id desc 的每个行顺序分配从1 开始的唯一编号,partition by 将确保应为每组uid 进行编号。

【讨论】:

    【解决方案3】:

    也许这会有所帮助:

    SELECT uid,
           event_id
      FROM(SELECT uid,
                  event_id,
                  ROW_NUMBER() OVER (PARTITION BY uid ORDER BY event_ID DESC) rank
             FROM events
          )
     WHERE uid IN (1, 4)
       AND rank = 1
    

    【讨论】:

      【解决方案4】:

      数组元素的原始顺序返回行:

      Postgres 9.4 或更新版本

      SELECT e.*
      FROM   unnest('{1, 4}'::int[]) WITH ORDINALITY a(uid, ord)  -- input array here
      CROSS  JOIN LATERAL (
         SELECT * FROM events e
         WHERE  e.uid = a.uid
         ORDER  BY e.event_id DESC
         LIMIT  1
         ) e
      ORDER  BY a.ord;
      

      WITH ORDINALITY的详细信息:

      @a_horse's query 有一个细微的差别:如果给定的数组有重复的元素,则此查询会返回重复的行,这可能是可取的,也可能是不可取的。如果不是,请在unnest() 之后和加入大表之前添加一个DISTINCT 步骤。

      主要好处是优化了索引使用。见:

      Postgres 9.3 或更早版本

      使用隐式JOIN LATERAL

      SELECT e.*
      FROM  (SELECT '{1, 4}'::int[]) a(arr)  -- input array here
           , generate_subscripts(a.arr, 1) i 
      CROSS  JOIN LATERAL (
         SELECT * FROM event e
         WHERE  e.uid = a.arr[i.i]
         ORDER  BY e.event_id DESC
         LIMIT  1
         ) e
      ORDER  BY i.i;
      

      【讨论】:

      • 你,先生,摇滚,我必须注意。
      【解决方案5】:

      在我发布问题几秒钟后,我就想到了这个问题。它效率不高,但要考虑所有选项:

      SELECT * FROM events WHERE event_id IN 
          (SELECT MAX(event_id) FROM events GROUP BY uid WHERE uid IN (1,4))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-06-14
        • 1970-01-01
        • 1970-01-01
        • 2013-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多