mysql - SQL - 从大数据集中返回最新的多条记录

标签 mysql database laravel mariadb groupwise-maximum

背景

我有一个 stock_price存储大约 1000 只股票的历史日内股票价格的表。虽然定期清除旧数据,但该表定期有 5M+ 记录。结构松散:

| id     | stock_id | value | change |  created_at         |
|--------|----------|-------|--------|---------------------|
| 12345  | 1        | 50    | 2.12   | 2020-05-05 17:39:00 |
| 12346  | 2        | 25    | 1.23   | 2020-05-05 17:39:00 |

我经常需要为 API 端点一次获取大约 20 只股票的最新股票价格。这个的原始实现对每只股票执行一个查询:
select * from stock_prices where stock_id = 1 order by created_at desc limit 1

第 1 部分:低效查询

20 多个查询效率低下,但它有效。代码 (Laravel 6) 已更新为使用正确的关系 ( stock hasMany stock_prices ),进而生成如下查询:
select
  *
from
  `stock_prices`
where
  `stock_prices`.`stock_id` in (1, 2, 3, 4, 5)
order by
  `id` desc

虽然这可以节省查询,但运行需要 1-2 秒。运行 explain显示它仍然必须在任何给定时间查询 50k+ 行,即使使用外键索引也是如此。我的下一个想法是添加一个 limit到查询只返回等于我要求的股票数量的行数。查询现在是:
select
  *
from
  `stock_prices`
where
  `stock_prices`.`stock_id` in (1, 2, 3, 4, 5)
order by
  `id` desc
limit
  5

第 2 部分:查询有时会遗漏记录

性能是惊人的 - 毫秒级处理。 然而 ,它可能不会返回一个/多个股票的价格。自 limit已添加,如果任何股票在下一个股票之前有多个价格(行),它将“消耗”一个行计数。

这是一个非常真实的场景,因为有些股票每分钟提取一次数据,其他每 15 分钟提取一次,等等。因此,由于 limit,上面的查询有时会出现。将为一只股票提取多行,随后不会为其他股票返回数据:
| id   | stock_id | value | change | created_at     |
|------|----------|-------|--------|----------------|
| 5000 | 1        | 50    | 0.5    | 5/5/2020 17:00 |
| 5001 | 1        | 51    | 1      | 5/5/2020 17:01 |
| 6001 | 2        | 25    | 2.2    | 5/5/2020 17:00 |
| 6002 | 3        | 35    | 3.2    | 5/5/2020 17:00 |
| 6003 | 4        | 10    | 1.3    | 5/5/2020 17:00 |

在这个场景中,你可以看到 stock_id1有更频繁的数据间隔,所以当查询运行时,它返回该 ID 的两条记录,然后继续向下列表。在达到 5 条记录后,它停止了,这意味着 stock id5没有返回任何数据,尽管它确实存在。可以想象,当没有数据返回时,这会破坏应用程序中的内容。

第 3 部分:尝试解决
  • 最明显的答案似乎是添加一个 GROUP BY stock_id作为一种要求我获得与每只股票预期相同数量的结果的方式。不幸的是,这让我回到了第 1 部分,其中该查询在运行时需要 1-2 秒,因为它最终必须遍历与之前没有限制的相同的 50k+ 行。这让我没有好过。
  • 接下来的想法是随意制作LIMIT大于它需要的大小,以便它可以捕获所有行。这不是一个可预测的解决方案,因为查询可能是数千只股票的任意组合,每只股票都有不同的可用数据间隔。最极端的例子是每天和每分钟拉动的股票,这意味着在第二只股票出现之前,一个股票可能有接近 350 多行。乘以一次查询中的股票数量 - 比如说 50,这仍然需要查询 15k+ 行。可行,但并不理想,并且可能无法扩展。

  • 第 4 部分:建议?

    让一个 API 调用发起可能超过 50 个 DB 查询只是为了获取股票价格数据是一种糟糕的做法吗?是否有 LIMIT 的一些阈值?我应该使用它来最大限度地减少失败的机会,让自己感到舒服吗?是否有其他 SQL 方法可以让我返回所需的行而不必查询大量表?

    任何帮助表示赞赏。

    最佳答案

    最快的方法是union all :

    (select * from stock_prices where stock_id = 1 order by created_at desc limit 1)
    union all
    (select * from stock_prices where stock_id = 2 order by created_at desc limit 1)
    union all
    (select * from stock_prices where stock_id = 3 order by created_at desc limit 1)
    union all
    (select * from stock_prices where stock_id = 4 order by created_at desc limit 1)
    union all
    (select * from stock_prices where stock_id = 5 order by created_at desc limit 1)
    

    这可以使用 stock_prices(stock_id, created_at [desc]) 上的索引.不幸的是,当您使用 in ,索引无法有效使用。

    关于mysql - SQL - 从大数据集中返回最新的多条记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61649768/

    相关文章:

    java - 如何使用Java比较2个数据库表

    Laravel 中的 MySQL 子查询,如何连接查询并将第二个查询的结果作为新列添加到第一个查询?

    c# - 如何设置列表项值属性以更正数据库表行中的 ID?

    mysql - 如何在 laravel.. 中实现 Select 查询?

    mysql - 使用 ORDER BY 和 LIMIT MYSQL 更新多个不同的表

    php - 从数组中找出特定的键?

    database - 查询 Excel 表格

    php - Laravel 4 中邮件的更多 SMTP 帐户

    Laravel 4 - 从集合中获取属性数组

    mysql - 我可以使用 gorm 将 MySQL 中的现有字段增加 1 吗?不使用原始 SQL