这个问题似乎与处理 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
这可能是使用横向连接的好地方(这至少需要 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)
.
你能试试
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()
功能的更多信息: