在 mySQL LEFT JOIN 中组合重复项(可能通过 CONCAT)

问题描述 投票:0回答:2

这个问题似乎与处理 LEFT JOIN 重复项的其他一些问题略有不同。这是我的情况:

我有一个与仪表相关的数据点列表。数据点是连续的,但随着时间的推移,仪表名称会发生变化,偶尔会重叠,例如,我有一个仪表表,内容如下:

编辑:出于某种原因,我的表格在编辑模式下工作正常,但在实时时却不行。我会尝试格式化为固定空间:

|MeterID| DataID|MeterName|ValidFrom |  ValidTo |
|-------|-------|---------|----------|----------|
|      1|      1|Meter A  |2010-09-21|2015-09-17|
|      2|      1|Meter B  |2015-09-15|2020-02-04|
|      3|      1|Meter C  |2016-05-02|2020-09-01|

我的数据表只有这样的东西:

|DataID|Value|Timestamp          |
|------|-----|-------------------|
|     1| 0.9 |2010-09-21 00:00:00|
|     1| ... |...                |
|     1| 3.4 |2020-09-01 00:00:00|

我要找的是这样的结果:

|Timestamp          |DataID|MeterName      |Value|
|-------------------|------|---------------|-----|
|…                  |     1|Meter A        |    …|
|2015-09-14 23:00:00|     1|Meter A        |  7.9|
|2015-09-15 00:00:00|     1|Meter A,Meter B|  3.0|
|…                  |     1|Meter A,Meter B|  6.3|
|2015-09-16 23:00:00|     1|Meter A,Meter B|  0.4|
|2015-09-17 00:00:00|     1|Meter B        |  7.5|
|…                  |     1|Meter B        |    …|
|2016-05-01 23:00:00|     1|Meter B        |  0.6|
|2016-05-02 00:00:00|     1|Meter B,Meter C|  2.0|
|…                  |     1|Meter B,Meter C|    …|
|2020-02-03 23:00:00|     1|Meter B,Meter C|  3.6|
|2020-02-04 00:00:00|     1|Meter C        |  9.7|
|…                  |     1|Meter C        |    …|

我现有的查询(下方)工作正常,但显然会导致仪表时间戳重叠的重复行。实际上,其中一些重叠可以持续数年。如果有帮助,可能永远不会超过 2 个,在任何给定点可能有 3 个重叠。此外,我们在这里处理的是大表(一次超过 1000 米,每小时数据价值 20 年) - 因此仪表表目前有 2200 行,数据表几乎有 190M 行 - 仅供您考虑考虑查询效率,因为现在根据索引的设置方式,我下面的查询非常快。

我想也许做一个子查询,通过时间戳值转置仪表名称,类似于 MSSQL 中的 PIVOT 函数,但我不确定在 MySQL (v8.0.33) 中实现该功能的最佳方法

SELECT
    d.Timestamp,
    d.DataID,
    m.MeterName,
    d.Value
FROM
    data d
LEFT JOIN
    meter m
ON
    m.DataID = d.DataID
    AND d.Timestamp >= m.ValidFrom
    AND d.Timestamp <= m.ValidTo
WHERE
    d.DataID=1
sql mysql left-join mysql-8.0
2个回答
1
投票

这可能是使用横向连接的好地方(这至少需要 MySQL 8.0.14):

SELECT d.Timestamp, d.DataID, m.MeterNames, d.Value
FROM data d
CROSS JOIN LATERAL (
    SELECT GROUP_CONCAT(m.MeterName ORDER BY m.ValidFrom) meterNames
    FROM meter m
    WHERE m.DataID = d.DataID
      AND d.Timestamp >= m.ValidFrom
      AND d.Timestamp <= m.ValidTo
) m
WHERE d.DataID = 1

在早期版本中,我们可以通过相关子查询获得相同的行为:

SELECT d.Timestamp, d.DataID, 
    (
        SELECT GROUP_CONCAT(m.MeterName ORDER BY m.ValidFrom)
        FROM meter m
        WHERE m.DataID = d.DataID
          AND d.Timestamp >= m.ValidFrom
          AND d.Timestamp <= m.ValidTom
    ) MeterNames, 
    d.Value
FROM data d
WHERE d.DataID = 1

我希望这种技术比

left join
+
group by
更有效,因为它避免了外部聚合 - 特别是如果你有一个索引在
meter (DataID, ValidFrom,ValidTom)
.


1
投票

你能试试

GROUP_CONCAT()
功能吗:

SELECT
    d.Timestamp,
    d.DataID,
    GROUP_CONCAT(m.MeterName ORDER BY m.ValidFrom ASC SEPARATOR ',') AS MeterNames,
    d.Value
FROM
    data d
LEFT JOIN
    meter m
ON
    m.DataID = d.DataID
    AND d.Timestamp >= m.ValidFrom
    AND d.Timestamp <= m.ValidTo
WHERE
    d.DataID=1
GROUP BY
    d.Timestamp, d.DataID, d.Value

让我知道你得到了什么..

您可以在此处阅读有关

GROUP_CONCAT()
功能的更多信息:

https://www.geeksforgeeks.org/mysql-group_concat-function/

© www.soinside.com 2019 - 2024. All rights reserved.