繁体   English   中英

我如何从大 mysql 表中获取每天的最后一行

[英]How I can get last row of each day from big mysql table

我有超过 1000 万行的表。 我可以通过查询获得每天的第一行

select *
from history_average_crypto_stats
where `asset_id` = 1 
  and `created_at` >= DATE_SUB(NOW(), INTERVAL 1 YEAR) 
group by date(`created_at`) 
order by `created_at` asc

但我需要最后并尝试这个查询

select history_average_crypto_stats.*
from history_average_crypto_stats
join (
  select MAX(`id`) id
  from history_average_crypto_stats
  where `asset_id` = 1 
    and `created_at` >= DATE_SUB(NOW(), INTERVAL 1 YEAR) 
  group by date(`created_at`)
) last ON history_average_crypto_stats.`id` = last.`id`

但它需要超过24秒......

我需要不到 1 秒。 表结构

CREATE TABLE `history_average_crypto_stats` (
  `id` bigint(20) UNSIGNED NOT NULL,
  `asset_id` bigint(20) UNSIGNED NOT NULL,
  `price` double(20,6) NOT NULL,
  `volume` bigint(20) NOT NULL,
  `circulating_supply` bigint(20) NOT NULL,
  `total_supply` bigint(20) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

ALTER TABLE `history_average_crypto_stats`
  ADD PRIMARY KEY (`id`),
  ADD KEY `history_average_pairs_currency_id_asset_id_foreign` (`created_at`,`asset_id`) USING BTREE,
  ADD KEY `asset_id` (`asset_id`);

ALTER TABLE `history_average_crypto_stats`
  MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;

一种选择使用相关子查询:

select h.*
from history_average_crypto_stats h
where h.created_at = (
    select max(h1.created_at)
    from history_average_crypto_stats h1
    where h1.created_at >= date(h.created_at) and h1.created_at < date(h.created_at) + interval 1 day
)

这使用列created_at来标识每天的最后一条记录。 目前尚不清楚您为什么使用id代替(除非有重复的created_at )。

为了性能,请考虑(created_at)上的索引。

如果您也想对asset_id进行asset_id ,就像在您的原始代码中一样,那么:

select h.*
from history_average_crypto_stats h
where h.asset_id = 1 and h.created_at = (
    select max(h1.created_at)
    from history_average_crypto_stats h1
    where h1.asset_id = h.asset_id
      and h1.created_at >= date(h.created_at) 
      and h1.created_at < date(h.created_at) + interval 1 day
)

然后在(asset_id, created_at)上建立索引 - 我怀疑这比(created_at, asset_id)上的原始索引更适合您的查询,因为created_at上的范围条件。

如果你运行的是 MySQL 8.0,你也可以尝试窗口函数,看看性能是否更好:

select *
from (
    select h.*, row_number() over(partition by date(created_at) order by created_at desc) rn
    from history_average_crypto_stats 
    where asset_id = 1
) h
where rn = 1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM