Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql SQL-从大型数据集中返回多条记录的最新记录 背景_Mysql_Database_Laravel_Mariadb_Groupwise Maximum - Fatal编程技术网

Mysql SQL-从大型数据集中返回多条记录的最新记录 背景

Mysql SQL-从大型数据集中返回多条记录的最新记录 背景,mysql,database,laravel,mariadb,groupwise-maximum,Mysql,Database,Laravel,Mariadb,Groupwise Maximum,我有一个stock\u price表,它存储了大约1000只股票的历史日内股价。虽然定期清除旧数据,但该表定期有5万条以上的记录。结构松散: | id | stock_id | value | change | created_at | |--------|----------|-------|--------|---------------------| | 12345 | 1 | 50 | 2.12 | 2020-05-05 17:39:0

我有一个
stock\u price
表,它存储了大约1000只股票的历史日内股价。虽然定期清除旧数据,但该表定期有5万条以上的记录。结构松散:

| 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\u 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部分:查询有时会丢失记录 性能是惊人的-毫秒级处理与此然而,它可能无法返回一只/多只股票的价格。由于已添加
限制
,如果任何股票在下一只股票之前有多个价格(行),它将“消耗”其中一个行计数

这是一个非常真实的场景,因为一些股票每分钟提取一次数据,其他股票每15分钟提取一次数据,等等。因此,在某些情况下,由于
限制,上述查询将为一只股票提取多行数据,随后不会为其他股票返回数据:

| 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 |
在这个场景中,您可以看到
1
stock\u id
具有更频繁的数据间隔,因此当运行查询时,它返回该id的两条记录,然后继续向下列表。在命中5条记录后,它停止了,这意味着
5
stock id
没有返回任何数据,尽管它确实存在。正如你所能想象的那样,当没有数据返回时,应用程序中的事情会被打破

第三部分:解决问题的尝试
  • 最明显的答案似乎是添加一个
    按股票分组\u id
    ,作为一种方式,要求我获得与我预期的每只股票相同数量的结果。不幸的是,这让我回到了第1部分,在该部分中,该查询在工作时需要1-2秒,因为它最终必须遍历相同的50k+行,而之前没有限制。这对我没有什么好处

  • 下一个想法是任意地使
    限制
    大于它需要的大小,以便它可以捕获所有行。这不是一个可预测的解决方案,因为查询可以是数千只股票的任意组合,每只股票都有不同的可用数据间隔。最极端的例子是每天相对于每分钟拉动的股票,这意味着在第二只股票出现之前,可能会有近350多行。将其乘以一次查询中的库存数量(比如50),这仍然需要查询15k+行。可行,但不理想,并且可能无法扩展

  • 第四部分:建议? 让一个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)
    
    这可以使用
    股票价格(股票id,在[desc]创建)上的索引
    。不幸的是,当您在
    中使用
    时,索引无法有效地使用。

    Groupwise max

    SELECT b.*
        FROM ( SELECT stock_id, MAX(created_at) AS created_at
                FROM stock_proces
                GROUP BY stock_id
             ) AS a
        JOIN stock_prices AS b  USING(stock_id, created_at)
    
    需要:

    INDEX(stock_id, created_at)
    
    如果您可以在同一秒钟内为同一股票设置两行,则将得到两行。有关备选方案,请参见下面的链接

    如果该对是唯一的,则将其设为
    主键
    ,并去掉
    id
    ;这也有助于提高性能


    更多讨论:

    哪个mysql版本?我说错了-实际上是MariaDB,尽管我知道它们的操作方式类似。该版本被列为
    10.2.31
    查看。或者是
    SELECT。。created>NOW()-间隔30分钟
    将覆盖所有记录,有多少不必要的数据?如何索引
    显示创建表{tablename}
    ?还要看“时间序列数据库”谢谢,戈登。这肯定很快。我想知道这在什么程度上开始崩溃。。。我是否可以将50多个查询链接在一起,并仍然期望合理的性能?我还想知道在数据库级别,这个查询的操作是否与50个单独的查询相同。@itwasluck3。对每一个都是索引的快速引用。不幸的是,如果没有
    union-all
    ,就无法(或者我不知道如何)表达这一点,因为有关于如何使用索引的规则。