0

我有一个包含动态类别数据的表:

+----------+--------------+---------------+---------+
| category | string_value | integer_value | user_id |
+----------+--------------+---------------+---------+
| cat_1    | NULL         | 1             |       1 |
| cat_1    | NULL         | 3             |       2 |
| cat_2    | foo          | NULL          |       1 |
| cat_2    | bar          | NULL          |       2 |
+----------+--------------+---------------+---------+

我需要这个表的一个旋转版本,我使用 with 语句:

select
  user_id,
  max(case when category == 'cat_1' then integer_value end) as 'cat_1',
  max(case when category == 'cat_2' then string_value end) as 'cat_2',
from my_table
group by user_id

这将创建以下格式的结果:

+---------+-------+-------+
| user_id | cat_1 | cat_2 |
+---------+-------+-------+
|       1 |     1 | foo   |
|       2 |     3 | bar   |
+---------+-------+-------+

这个查询本身对于许多类别和表条目也表现良好(例如,对于 8 个类别和 240k 条目,它需要大约 20 毫秒),但如果我将这个确切的查询包装在 a 中select * from <query>,性能会显着下降(到 650 毫秒)。

此外,排序依据user_id不会显着影响性能,而任何其他字段的排序也会导致性能下降,即使相应字段的索引并且user_id存在。我猜这种方法本身对于较大的表是不可行的?但是,我很好奇是什么导致添加select * from <query部件时额外的执行时间。

背景:我尝试使用此查询来存储动态用户数据,并且我想防止在运行时更改表结构(即添加一列)。任何替代方案都会受到欢迎。我正在使用 MariaDB 10.5.5,我需要该解决方案也可以与 MySQL 5.7 和 SQL Server 2019 一起使用。

执行计划:

无周围select * from

+----+-------------+-----------+-------+---------------+------------+---------+-----+--------+---------+----------+------------+-------+    
| id | select_type | table     | type  | possible_keys | key        | key_len | ref | rows   | r_rows  | filtered | r_filtered | Extra |
|----|-------------|-----------|-------|---------------|------------|---------|-----|--------|---------|----------|------------|-------|
|  1 | SIMPLE      | user_data | index |               | user_index |         |   9 | 226067 | 1619.00 |    100.0 |      99.88 |       |
+----+-------------+-----------+-------+---------------+------------+---------+-----+--------+---------+----------+------------+-------+

与周围select * from

+----+-------------+------------+-------+---------------+------------+---------+-----+--------+-----------+----------+------------+-------+ 
| id | select_type | table      | type  | possible_keys | key        | key_len | ref | rows   | r_rows    | filtered | r_filtered | Extra |
|----|-------------|------------|-------|---------------|------------|---------|-----|--------|-----------|----------|------------|-------|
|  1 | PRIMARY     | <derived2> | ALL   |               |            |         |     | 226067 |    200.00 |    100.0 |      100.0 |       |
|  2 | DERIVED     | user_data  | index |               | user_index |       9 |     | 226067 | 242418.00 |    100.0 |      100.0 |       |
+----+-------------+------------+-------+---------------+------------+---------+-----+--------+-----------+----------+------------+-------+ 
4

1 回答 1

1

这是我对正在发生的事情的猜测。

您在 MariaDB 用于聚合的基础表上有一个索引。这意味着没有进行排序。. . 只需读取索引,它就可以开始返回行。

这是一个非常好的功能。但是当您只运行查询时,您会看到第一行的时间。

当您使用派生表时,MariaDB 必须在返回任何行之前生成所有行。因此,带有子查询的工作要做的更多。select *

这就是为什么第二个版本比第一个慢。我希望在大多数机器上返回数万行的查询需要超过 20 毫秒。

于 2020-09-07T12:48:05.203 回答