查询最新和时间间隔的数据

时间:2017-06-16 15:31:25

标签: sql postgresql ecto

我的数据集与股票价格非常相似 - 我每5分钟得到一个平均价格,符号,公司名称。

我需要做的是能够有效:

  • 获取所有符号的最新价格
  • 为符号
  • 获取时间间隔价格(即日期N每1小时)

目前,这是我到目前为止所做的:

Stock Table
ID | name | symbol
Unique between name and symbol (name/symbol can be unique on their own)
Indexed on name, symbol (irrelevant here, but indexed for text search)

Stock Ticks Table
ID | stock_id | price | updated_at
All columns non null

对于问题1(获取给定符号的最新价格),我已经遇到了一些麻烦 - 这与其他问题基本相似:

获取每篇帖子的最新评论,或者基本上是每组最大的查询。事情是我的数据会变得非常大(每5分钟一次),所以我认为这是预优化的好例子。我应该添加current_price列(或current_prices表)吗?或者是group by / distinct?如何有效地编写查询?

对于问题2(获取时间间隔价格)我老实说有点困扰如何为此编写查询。请注意,棘手的部分是数据中可能存在漏洞,例如,如果查询是:

get every price per day from June 1 to June 10

并说6月3日没有数据,那么它应该尝试找到最近的时间(过去或现在)

我在Phoenix / Ecto上写这篇文章,所以如果你能用ORM编写它,那将是一个加号但不是必需的。

1 个答案:

答案 0 :(得分:1)

假设PostgreSQL v9.6(你没有指定)。

股票表定义

CREATE TABLE stock ( id serial NOT NULL PRIMARY KEY, 
                     name text NOT NULL UNIQUE, 
                     symbol text NOT NULL UNIQUE );

和价格表定义

CREATE TABLE pricing ( id int NOT NULL REFERENCES stock (id), 
                       updated_at TIMESTAMP(0) NOT NULL, 
                       price NUMERIC( 10, 2 ) NOT NULL, 
                       PRIMARY KEY (id, updated_at) );

并按日期加速价格查询而不使用stock_ids

CREATE INDEX ON pricing (updated_at);

示例stock值为(1,'Queen','BEE'),(2,'Team Fox','FOX')。 示例pricing值为

 (1, '2017-06-17 13:24:59', 12.34), 
 (1, '2017-06-01 18:00:00', 6.10), 
 (1, '2017-06-02 17:00:00', 6.20), 
 (1, '2017-06-03 17:00:00', 6.30), 
 (2, '2017-06-02 15:00:00', 100.00), 
 (2, '2017-06-03 15:30:00', 777.00);

获取所有符号的最新价格

SELECT s.*, 
       (SELECT price 
        FROM pricing 
        WHERE id = s.id 
        ORDER BY updated_at DESC 
        LIMIT 1) "latest_price" 
FROM stock s 
WHERE EXISTS (SELECT id FROM pricing p WHERE p.id = s.id);

将使用NULL条件排除latest_price的{​​{1}}值。保留它以获得尚未知道价格的股票的空值。

的替代方案
  

获取最新价格

WHERE EXISTS

  

从6月1日到6月10日每天获得每个价格

首先使用

生成相应的日期
SELECT * 
FROM stock s 
JOIN LATERAL 
(SELECT price 
 FROM pricing p 
 WHERE p.id = s.id 
 ORDER BY updated_at DESC 
 LIMIT 1) latest_price ON true ;

产生

WITH RECURSIVE dates (d) AS ( 
SELECT '2017-06-01 20:00'::timestamp 
UNION ALL 
SELECT d + interval '24 hours' 
FROM dates 
WHERE d < '2017-06-04 20:00'::timestamp 
) 
SELECT d FROM dates ;

您可以调整以下参数

  • 开始日期( d --------------------- 2017-06-01 20:00:00 2017-06-02 20:00:00 2017-06-03 20:00:00 2017-06-04 20:00:00
  • 结束日期('2017-06-01 20:00'::timestamp
  • 时间步长('2017-06-04 20:00'::timestamp

使用日期

interval '24 hours'

得到例如

WITH RECURSIVE dates (d) AS ( 
  SELECT '2017-06-01 20:00'::timestamp 
  UNION ALL 
  SELECT d + interval '24 hours' 
  FROM dates 
  WHERE d < '2017-06-04 20:00'::timestamp 
) 
SELECT symbol, CAST( d AS date ) "day", price, updated_at  
FROM stock s 
CROSS JOIN dates  
JOIN LATERAL ( 
  SELECT updated_at, price  
  FROM pricing p 
  WHERE p.id = s.id AND p.updated_at <= d 
  ORDER BY p.updated_at DESC 
  LIMIT 1 
) latest_prices ON true 
ORDER BY 1, 2, 4, 3 ;
-- orders same id = name blocks; 
-- use 2, 1, 4, 3 to get same date blocks