【问题标题】:Join three MySQL queries to one to get it faster将三个 MySQL 查询合并为一个以使其更快
【发布时间】:2021-02-04 08:20:12
【问题描述】:

我有三个 MySQL 查询,如何将它连接到一个以更快地获得结果?是否可以使用单个查询来完成它,如果它比单独的三个查询更快? (我的表中最多可以有 150 万条记录 - 从 3 个月的时间段开始测量)

  • id - 自动递增,整数
  • phase - tinyint,只能是 1,2 或 3
  • time - 日期时间,每个阶段组 1、2、3 的时间相同
  • voltage - 浮动

样本数据:

+----+---------------------+-------+---------+
| id | time                | phase | voltage |
+----+---------------------+-------+---------+
|  1 | 2021-01-31 19:55:01 |     1 |  235.77 |
|  2 | 2021-01-31 19:55:01 |     2 |  237.94 |
|  3 | 2021-01-31 19:55:01 |     3 |  224.63 |
|  4 | 2021-01-31 19:55:06 |     1 |  236.41 |
|  5 | 2021-01-31 19:55:06 |     2 |  238.17 |
|  6 | 2021-01-31 19:55:06 |     3 |  224.61 |
|  7 | 2021-01-31 19:55:11 |     1 |  236.45 |
|  8 | 2021-01-31 19:55:11 |     2 |  237.87 |
|  9 | 2021-01-31 19:55:11 |     3 |   223.4 |
+----+---------------------+-------+---------+

查询 #1:

SELECT time, voltage AS L1
FROM measurements
WHERE phase = 1
ORDER BY id 

查询 #2:

SELECT time, voltage AS L2
FROM measurements
WHERE phase = 2
ORDER BY id

查询 #3:

SELECT time, voltage AS L3
FROM measurements
WHERE phase = 3
ORDER BY id  

之后的结果应该是这样的:

+----+---------------------+--------+--------+--------+
| id | time                |   L1   |   L2   |   L3   |
+----+---------------------+--------+--------+--------+
|  1 | 2021-01-31 19:55:01 | 235.77 | 237.94 | 224.63 |
|  2 | 2021-01-31 19:55:06 | 236.41 | 238.17 | 224.61 |
|  3 | 2021-01-31 19:55:11 | 236.45 | 237.87 | 223.4  |
+----+---------------------+--------+--------+--------+

编辑:毕竟,我得到了一个查询:

SELECT 
    time,
    GROUP_CONCAT(IF(phase = 1, voltage, NULL)) AS 'L1',
    GROUP_CONCAT(IF(phase = 2, voltage, NULL)) AS 'L2',
    GROUP_CONCAT(IF(phase = 3, voltage, NULL)) AS 'L3'
FROM 
    `measurements`
GROUP BY 
    time

现在结果如下:

+---------------------+--------------------+--------------------+--------------------+
| time                | L1                 | L2                 | L3                 |
+---------------------+--------------------+--------------------+--------------------+  

| 2021-01-31 19:55:01 | 235.77000427246094 | 237.94000244140625 | 224.6300048828125  |
| 2021-01-31 19:55:06 | 236.41000366210938 | 238.1699981689453  | 224.61000061035156 |
| 2021-01-31 19:55:11 | 236.4499969482422  | 237.8699951171875  | 223.39999389648438 |
+---------------------+--------------------+--------------------+--------------------+

为什么现在电压值如此不准确,所有电压存储的值在小数点后最多为 2 个数字我没有在查询中做任何数学运算……那为什么会得到这个奇怪的值?

【问题讨论】:

  • 您可能希望在查询中使用不同的阶段条件。
  • @XnIcRaM WHERE 条件是否正确?根据直觉,我认为您希望 Q2 检查 phase=2 和 Q3 检查 phase=3?
  • 按时间戳分组。使用 case 表达式来选择不同的 L1、L2 和 L3 值。
  • 这个问题听上去像是被提问和回答的pivot
  • 这能回答你的问题吗? How can I return pivot table output in MySQL?

标签: mysql sql pivot-table


【解决方案1】:

我强烈建议稍微改变一下结构。 你会说什么:

  • measurements 与列:

    • 身份证
    • 时间
  • measurements_results 与列:

    • 身份证
    • measurement_id
    • 阶段
    • 电压

measurements_results 必须在measurement_id 上有外键。 在phase 列上,您可以放置​​常规索引。

查询将如下所示:

select m.id measurement_id, m.time, mr_l1.voltage L1, mr_l2.voltage L2, mr_l3.voltage L3
from measurements m
left join measurements_results mr_l1
    on mr_l1.measurement_id = m.id
    and mr_l1.phase = 1
left join measurements_results mr_l2
    on mr_l2.measurement_id = m.id
    and mr_l2.phase = 2
left join measurements_results mr_l3
    on mr_l3.measurement_id = m.id
    and mr_l3.phase = 3

[编辑] 左连接是为了安全,因为这种 db 结构不能确保所有 3 相始终都有电压。因此,您可以添加 coalesce(voltage, 0) 以免结果为空。

【讨论】:

  • 没有。他只会回来说性能不好。
【解决方案2】:

您可以使用group byCASE 语句以简单的方式完成:

SELECT time, max(case phase
                    when 1 then voltage
                    else 0
                  end) as L1, 
              max(case phase
                    when 2 then voltage
                    else 0
                  end) as L2, 
              max(case phase
                    when 3 then voltage
                    else 0
                  end) as L3
FROM measurements
group by time
ORDER BY time

【讨论】:

  • 我认为 ELSE 在所有 CASE 中都是多余的......缺席值和零值是不一样的。
【解决方案3】:

问题是GROUP_CONCAT() 必须将值转换为字符串。看起来它们是浮点值——因此转换很少准确。

如果你真的想转换成字符串,那么使用FORMAT() 得到你想要的格式。但是,我会使用以下方法保留原始类型:

SELECT time,
       MAX(CASE WHEN phase = 1 THEN voltage END) AS L1,
       MAX(CASE WHEN phase = 2 THEN voltage END) AS L2,
       MAX(CASE WHEN phase = 3 THEN voltage END) AS L3
FROM measurements
GROUP BY time;

注意事项:

  • 您通常不需要在标识符上使用反引号,例如 measurements
  • 我非常非常喜欢 CASE,因为它是标准 SQL,所以我替换了它。
  • 不要对列别名使用单引号。很容易将它们与字符串混淆。而且,它们也不需要。

【讨论】:

  • 同样的问题,GROUP_CONCAT()CASE 都给出了小数点后多位数的数字
  • @XnIcRaM 。 . .正如答案所说,您可以使用GROUP_CONCAT()FORMAT() 将数字转换为您想要的表示形式。
  • 我知道,但为什么会发生,这对我来说很奇怪
  • @XnIcRaM 。 . .奇怪的是在这些查询中发生了这种情况,但是您可以看到数据正常。问题的根源在于浮点格式。您可能应该使用数字/十进制。
【解决方案4】:

我会主张在输入时修复它;从长远来看,这可能会更好。但是...您还需要对数据执行哪些其他SELECTs 操作?

CREATE TABLE measurements (
    dt DATETIME NOT NULL,
    v1 DECIMAL(5,2) NOT NULL,     -- or whatever is appropriate; FLOAT is a reasonable option
    v2 DECIMAL(5,2) NOT NULL,
    v3 DECIMAL(5,2) NOT NULL,
    PRIMARY KEY (dt)
) ENGINE=InnoDB

注意事项:

  • 如果还有其他列,我们需要了解它们
  • 如果数据以 3 条记录的形式出现,则收集它们并执行单个 INSERT 或执行 1 INSERT 和 2 UPDATEs。对于后一种情况,请考虑NULL
  • “235.77000427246094”与 MySQL 决定在 DOUBLE 中执行计算的时间有关。以不同的方式进行旋转可以避免这种情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    • 2016-04-02
    • 2012-11-18
    相关资源
    最近更新 更多