优化数据库中大表的查询(SQL)
Posted
技术标签:
【中文标题】优化数据库中大表的查询(SQL)【英文标题】:Optimize the query for a large table in database (SQL) 【发布时间】:2020-04-27 23:14:24 【问题描述】:我正在尝试优化大型事件表(超过 1000 万行)上的 sql 查询以进行日期范围搜索。我已经在这张表上有唯一的索引(盖子、做、测量、日期)。下面的查询试图在日期列中每 2 秒间隔获取三种测量类型(千瓦、电流和电压)的事件:
SELECT *, FLOOR(UNIX_TIMESTAMP(date)/2) AS timekey
from events
WHERE lid = 1
and did = 1
and measurement IN ("Voltage")
group by timekey
UNION
SELECT *, FLOOR(UNIX_TIMESTAMP(date)/2) AS timekey
from events
WHERE lid = 1
and did = 1
and measurement IN ("Current")
group by timekey
UNION
SELECT *, FLOOR(UNIX_TIMESTAMP(date)/2) AS timekey
from events
WHERE lid = 1
and did = 1
and measurement IN ("Kilowatts")
group by timekey
这是我要查找的表。
=============================================================
id | lid | did | measurement | date
=============================================================
1 | 1 | 1 | Kilowatts | 2020-04-27 00:00:00
=============================================================
2 | 1 | 1 | Current | 2020-04-27 00:00:00
=============================================================
3 | 1 | 1 | Voltage | 2020-04-27 00:00:00
=============================================================
4 | 1 | 1 | Kilowatts | 2020-04-27 00:00:01
=============================================================
5 | 1 | 1 | Current | 2020-04-27 00:00:01
=============================================================
6 | 1 | 1 | Voltage | 2020-04-27 00:00:01
=============================================================
7 | 1 | 1 | Kilowatts | 2020-04-27 00:00:02
=============================================================
8 | 1 | 1 | Current | 2020-04-27 00:00:02
=============================================================
9 | 1 | 1 | Voltage | 2020-04-27 00:00:02
预期结果是检索日期等于 2020-04-27 00:00:00 和 2020-04-27 00:00:02 的所有数据。上面提供的查询按预期工作。但我使用 UNION 来查找桌子上的不同测量值,我相信这可能不是最佳方法。
任何 SQL 专家都可以帮助我调整我必须提高性能的查询吗?
【问题讨论】:
您有一个GROUP BY
和一个SELECT *
。这应该会失败——在一般的 SQL 和更新版本的 mysql 中。您的查询没有意义。
我使用的是 SQL 5.7 版。为什么 Group by with select * 在更新的版本中会失败?为什么没有意义?
。 .因为您在 SELECT
中有未在 GROUP BY
中的未聚合列。
另外,分组问题。您有显示日期、内容和地点的组件,但您希望按电流、电压、千瓦显示什么值。如果每秒都有条目出现,您是否希望 min、max、avg、所有这些每次测量都寻找峰值或其他东西?缺少其余列的上下文以及分组依据如何呈现您的最终结果。请编辑您现有的帖子并添加一些额外的示例数据,以说明要汇总的内容,以及要显示的预期结果。仅存在记录这一事实是一回事,但此后没有上下文。
如果输入表中缺少特定的一秒会发生什么?
【参考方案1】:
对于每个测量,您每秒都有一条记录,并且您希望每两秒选择一条记录。
你可以试试:
select *
from events
where
lid = 1
and did = 1
and measurement IN ('Voltage', 'Current')
and extract(second from date) % 2 = 0
这将选择具有偶数第二部分的记录。
或者,如果您总是每秒有一条记录,另一种选择是row_number()
(这需要 MySQL 8.0):
select *
from (
select
e.*,
row_number() over(partition by measurement order by date) rn
from events
where
lid = 1
and did = 1
and measurement IN ('Voltage', 'Current')
) t
where rn % 2 = 1
不过,这比上一个查询准确度稍差。
【讨论】:
谢谢,第一种方法适用于 1、2 秒间隔,因为操作提取(从日期开始的第二个)% 2 = 0。如果我尝试应用 30 秒间隔、15 分钟、半小时怎么办, 6 小时等。我尝试(从日期开始的分钟数)% 15 = 0,但它不像我预期的那样工作。 30 秒间隔为:extract(second from date) % 30 = 0
。对于超过一分钟的间隔,您可以使用unix_timestamp()
- 15 分钟,类似unix_timestamp(date) % (60 * 15) = 0
,等等。
查询是这样的吗? select * from events where lid = 1 and did = 9999 and measurement IN ("Voltage", "Current", "Kilowatts") and unix_timestamp(date) % (60*15) = 0
但是这个查询没有返回结果给我?我无法每隔 15 分钟获取一次数据
row_number
方法假定每秒都有一个读数。这种数据可能有点不稳定。【参考方案2】:
您的查询实际上是三个查询合二为一。幸运的是,他们都根据相似的列选择数据行。如果你想让这个查询快速运行,你可以添加以下索引:
create index ix1 on events (lid, did, measurement);
【讨论】:
是的,我已经创建了索引,我正在尝试寻找一种提高索引速度的方法【参考方案3】:除了上述建议之外,更改PRIMARY KEY
会给您带来更多性能:
PRIMARY KEY(lid, did, date, measurement)
然后扔id
。
警告,如果两个读数以完全相同的“秒”进入,则可能会出现问题。如果一个读数在时钟滴答之后进入,而下一个读数在下一个滴答之前进入,则很容易发生这种情况。
【讨论】:
以上是关于优化数据库中大表的查询(SQL)的主要内容,如果未能解决你的问题,请参考以下文章
如果输入一条查询一张表的sql语句,但数据库执行缓慢,如何并采取啥样的方法对数据库进行优化?