为大型数据集中的每个项目选择最新条目

时间:2012-05-18 14:17:20

标签: sql performance postgresql

所以,我有一个大约有150万行的表,看起来有点像这样:

name   | time       | data1 | data2  
--------------------------------------
 93-15 | 1337348782 |   11  | 60.791 
 92-02 | 1337348783 |   11  | 62.584 
 92-02 | 1337348056 |   11  | 63.281
 93-15 | 1337348068 |    8  | 65.849
 92-02 | 1337348117 |   11  | 63.271 
 93-15 | 1337348129 |    8  | 65.849 
 92-02 | 1337348176 |   10  | 63.258 
 93-15 | 1337348188 |    8  | 65.849 
 92-02 | 1337348238 |   10  | 63.245 
 93-15 | 1337348248 |    8  | 65.849  

...这些对应于需要监控的事物的历史状态更新。现在,我想做的是找到每个单位的当前状态。

在stackoverflow上找到类似的问题并不难,并从调查结果中推断出来,我提出了这个问题:

SELECT * FROM vehicles v
  JOIN ( SELECT  MAX(time) as max, name
    FROM vehicles
    GROUP BY name)
  m_v
ON (v.time = m_v.max AND v.name = m_v.name);

但看到我有大约150万行(并且正在计数),是否有一种不同的方法可以加快查询速度?

1 个答案:

答案 0 :(得分:6)

WITH
  sequenced_data
AS
(
  SELECT
    ROW_NUMBER() OVER (PARTITION BY name ORDER BY time DESC) AS sequence_id,
    *
  FROM
    vehicles
)
SELECT
  *
FROM
  sequenced_data
WHERE
  sequence_id = 1

(name, time)上的覆盖索引也会有所帮助。


编辑:关于它是如何工作的说明等。

PostgreSQL具有窗口或分析功能。这些通常采用some_function() OVER (PARTITION BY some_fields ORDER BY some_fields)形式。

在这种情况下,我使用了ROW_NUMBER() OVER (PARTITION BY name ORDER BY time DESC)

ROW_NUMBER()为一组数据创建唯一的行号。 1 to n记录的n

PARTITION BY name表示此函数独立应用于不同的名称。每个name都是它自己的组/窗口/分区,ROW_NUMBER()的结果再次从1开始为每个组/窗口/分区重新开始。

ORDER BY time DESC获取一个组/窗口/分区中的所有记录,并在应用time函数之前按ROW_NUMBER()字段对其进行排序,其中值最高。

因此,对于您的示例数据,您得到了这个......

 name  | time       | data1 | data2  | row_number
--------------------------------------------------

 92-02 | 1337348783 |   11  | 62.584 | 1
 92-02 | 1337348238 |   10  | 63.245 | 2
 92-02 | 1337348176 |   10  | 63.258 | 3
 92-02 | 1337348117 |   11  | 63.271 | 4
 92-02 | 1337348056 |   11  | 63.281 | 5

 93-15 | 1337348782 |   11  | 60.791 | 1
 93-15 | 1337348248 |    8  | 65.849 | 2
 93-15 | 1337348188 |    8  | 65.849 | 3
 93-15 | 1337348129 |    8  | 65.849 | 4
 93-15 | 1337348068 |    8  | 65.849 | 5

由于排序为time DESC,因此每个time组/窗口/分区中值最高的name字段的row_number始终为1 (name, time) }。

ROW_NUMBER()上设置索引可以确保数据处于友好的顺序,从而使优化器更容易。这意味着time实际上并未应用于所有记录;一旦找到最高价值的ROW_NUMBER() = 1记录,并指定name,它就会知道它可以停止并转到下一个{{1}}。