TDengine 字符串函数 GROUP_CONCAT 用户手册

GROUP_CONCAT 函数用户手册

1. 函数概述

1.1 功能说明

GROUP_CONCAT 函数是一个聚合函数,用于将分组中的多行字符串值连接成一个字符串。它在需要将分组数据汇总为单个字符串表示时非常有用,例如将同一电表在不同时间点的状态信息合并展示。

核心特点

  • 属于聚合函数,配合 GROUP BY 使用
  • 支持多列字段连接
  • 支持自定义分隔符
  • 自动跳过 NULL 值

1.2 语法

sql 复制代码
GROUP_CONCAT(expr1 [, expr2, ..., exprN, separator])

1.3 参数说明

参数 类型 说明 是否必需
expr1, expr2, ... VARCHAR/NCHAR 要连接的字符串字段或表达式 必需(至少1个)
separator VARCHAR/NCHAR 分隔符,作为最后一个参数 必需

1.4 返回值

  • 类型:VARCHAR
  • 说明 :返回连接后的字符串,最大长度不超过 TSDB_MAX_FIELD_LEN
  • 如果所有输入值均为 NULL,则返回 NULL
  • 如果输入包含 NCHAR 类型,会自动进行字符集转换

1.5 适用版本

自 TDengine v3.3.8.0 开始支持

1.6 适用范围

  • ✅ 普通表
  • ✅ 超级表
  • ✅ 支持嵌套子查询
  • ✅ 可与 GROUP BY 结合使用

2. GROUP_CONCAT 与 CONCAT 的区别

2.1 核心差异对比

对比项 GROUP_CONCAT CONCAT
函数类型 聚合函数(Aggregate) 标量函数(Scalar)
作用范围 多行数据 → 单个结果 单行数据 → 单个结果
使用场景 分组汇总,将多行合并 行内字段拼接
GROUP BY 通常配合使用 不需要
NULL 处理 自动跳过 NULL 值 任何参数为 NULL 则返回 NULL
参数个数 至少2个(字段+分隔符) 2-8个
分隔符 最后一个参数作为分隔符 无分隔符(CONCAT_WS 支持)

2.2 示例对比

CONCAT - 单行字段拼接
sql 复制代码
-- 拼接单行的多个字段
SELECT CONCAT(location, '-', tbname) AS device_info
FROM meters
LIMIT 1;

-- 输出:
-- device_info
-- ===================
-- Beijing.Chaoyang-d1001
GROUP_CONCAT - 多行数据聚合
sql 复制代码
-- 将同一位置的所有电表名连接起来
SELECT 
    location,
    GROUP_CONCAT(tbname, ',') AS all_meters
FROM meters
GROUP BY location;

-- 输出:
-- location          | all_meters
-- =======================================
-- Beijing.Chaoyang  | d1001,d1002,d1003
-- Beijing.Haidian   | d2001,d2002

2.3 适用场景选择

场景 推荐函数 说明
单行多列拼接 CONCAT 如:姓+名、城市+区域
多行汇总 GROUP_CONCAT 如:同组数据合并显示
需要分隔符的单行拼接 CONCAT_WS 带分隔符的 CONCAT
需要分隔符的多行汇总 GROUP_CONCAT 带分隔符的聚合

3. 基础使用示例

3.1 智能电表表结构

sql 复制代码
-- 创建智能电表超级表
CREATE STABLE meters (
    ts TIMESTAMP,
    current FLOAT,
    voltage INT,
    phase FLOAT
) TAGS (
    groupid INT,
    location VARCHAR(64)
);

-- 创建子表
CREATE TABLE d1001 USING meters TAGS (1, 'Beijing.Chaoyang');
CREATE TABLE d1002 USING meters TAGS (1, 'Beijing.Chaoyang');
CREATE TABLE d1003 USING meters TAGS (1, 'Beijing.Haidian');
CREATE TABLE d1004 USING meters TAGS (2, 'Shanghai.Pudong');

-- 插入测试数据
INSERT INTO d1001 VALUES
    ('2024-01-15 00:00:00', 10.2, 220, 0.95),
    ('2024-01-15 12:00:00', 15.8, 219, 0.94);

INSERT INTO d1002 VALUES
    ('2024-01-15 00:00:00', 9.8, 221, 0.96),
    ('2024-01-15 12:00:00', 14.5, 220, 0.95);

3.2 简单字符串连接

sql 复制代码
-- 连接两个字符串字段
SELECT GROUP_CONCAT(location, tbname, '-') AS device_list
FROM (
    SELECT DISTINCT location, tbname FROM meters LIMIT 3
);

预期输出

复制代码
 device_list                              |
===========================================
 Beijing.Chaoyangd1001-Beijing.Chaoyangd1002-Beijing.Haiدياند1003 |

3.3 配合 GROUP BY 使用

sql 复制代码
-- 按区域分组,列出每个区域的所有电表
SELECT 
    SUBSTRING_INDEX(location, '.', 1) AS city,
    GROUP_CONCAT(tbname, ',') AS meter_list,
    COUNT(*) AS meter_count
FROM meters
GROUP BY city;

预期输出

复制代码
 city     | meter_list           | meter_count |
====================================================
 Beijing  | d1001,d1002,d1003    | 3           |
 Shanghai | d1004                | 1           |

4. 智能电表应用场景

场景 1:按区域汇总电表设备清单

业务需求:运维人员需要快速查看每个区域部署了哪些电表设备。

sql 复制代码
-- 按位置分组,列出所有电表编号
SELECT 
    location,
    COUNT(*) AS total_meters,
    GROUP_CONCAT(tbname, ', ') AS meter_ids
FROM meters
GROUP BY location
ORDER BY total_meters DESC;

预期输出

复制代码
 location          | total_meters | meter_ids        |
========================================================
 Beijing.Chaoyang  | 2            | d1001, d1002     |
 Beijing.Haidian   | 1            | d1003            |
 Shanghai.Pudong   | 1            | d1004            |

业务价值

  • 快速了解设备分布情况
  • 便于运维人员制定巡检计划
  • 支持设备清单导出

场景 2:多维度数据展示

业务需求:将电表的多个状态信息合并为一条记录便于展示。

sql 复制代码
-- 将电表的关键指标连接成摘要信息
SELECT 
    tbname,
    location,
    GROUP_CONCAT(
        CONCAT(
            TO_CHAR(ts, 'HH24:MI'),
            ':',
            CAST(current AS VARCHAR),
            'A'
        ),
        ' | '
    ) AS hourly_current_summary
FROM meters
WHERE ts >= '2024-01-15 00:00:00' 
  AND ts < '2024-01-15 06:00:00'
GROUP BY tbname, location
LIMIT 3;

预期输出

复制代码
 tbname | location          | hourly_current_summary        |
==================================================================
 d1001  | Beijing.Chaoyang  | 00:00:10.2A | 12:00:15.8A   |
 d1002  | Beijing.Chaoyang  | 00:00:9.8A | 12:00:14.5A    |

业务价值

  • 紧凑的数据展示
  • 便于移动端查看
  • 减少数据传输量

5. 与其他数据库的兼容性

5.1 MySQL 中的 GROUP_CONCAT

MySQL 原生支持 GROUP_CONCAT 函数,是该函数的标准实现。

MySQL 语法
sql 复制代码
GROUP_CONCAT([DISTINCT] expr [,expr ...]
             [ORDER BY {unsigned_integer | col_name | expr}
                 [ASC | DESC] [,col_name ...]]
             [SEPARATOR str_val])
MySQL 特有功能
  1. DISTINCT 去重
sql 复制代码
-- MySQL 支持
SELECT dept, GROUP_CONCAT(DISTINCT name SEPARATOR ', ')
FROM employees
GROUP BY dept;
  1. ORDER BY 排序
sql 复制代码
-- MySQL 支持
SELECT dept, GROUP_CONCAT(name ORDER BY salary DESC SEPARATOR ', ')
FROM employees
GROUP BY dept;
  1. 自定义分隔符(可选)
sql 复制代码
-- MySQL 分隔符可选,默认为逗号
SELECT GROUP_CONCAT(name SEPARATOR ' | ') FROM users;
SELECT GROUP_CONCAT(name) FROM users;  -- 默认用逗号
  1. 长度限制
  • group_concat_max_len 系统变量控制
  • 默认 1024 字节,可动态调整
sql 复制代码
SET SESSION group_concat_max_len = 10000;
MySQL 示例
sql 复制代码
-- MySQL 完整示例
SELECT 
    department,
    GROUP_CONCAT(
        DISTINCT employee_name 
        ORDER BY salary DESC 
        SEPARATOR ' | '
    ) AS top_employees
FROM employees
WHERE salary > 5000
GROUP BY department;

5.2 PostgreSQL 中的 STRING_AGG

PostgreSQL 不支持 GROUP_CONCAT,但提供了功能相似的 STRING_AGG 函数(SQL标准函数)。

PostgreSQL 语法
sql 复制代码
STRING_AGG(expression, delimiter [ORDER BY ...])
PostgreSQL 特点
  1. 必须指定分隔符
sql 复制代码
-- PostgreSQL (9.0+)
SELECT dept, STRING_AGG(name, ', ' ORDER BY name)
FROM employees
GROUP BY dept;
  1. 支持 ORDER BY
sql 复制代码
SELECT STRING_AGG(name, ', ' ORDER BY salary DESC)
FROM employees;
  1. 支持 DISTINCT(PostgreSQL 9.0+)
sql 复制代码
SELECT STRING_AGG(DISTINCT city, ', ')
FROM customers;
  1. 无长度限制(除了内存限制)
PostgreSQL 替代方案
sql 复制代码
-- 使用 ARRAY_AGG + ARRAY_TO_STRING
SELECT ARRAY_TO_STRING(ARRAY_AGG(name ORDER BY name), ', ')
FROM employees;

5.3 TDengine vs MySQL vs PostgreSQL

特性 TDengine MySQL PostgreSQL
函数名 GROUP_CONCAT GROUP_CONCAT STRING_AGG
分隔符 必需(最后参数) 可选(SEPARATOR) 必需
DISTINCT ❌ 不支持 ✅ 支持 ✅ 支持
ORDER BY ❌ 不支持 ✅ 支持 ✅ 支持
长度限制 TSDB_MAX_FIELD_LEN group_concat_max_len 无限制
版本 v3.3.8.0+ 原生支持 9.0+ (STRING_AGG)
NULL 处理 跳过 跳过 跳过
多列连接 ✅ 支持 ✅ 支持 ❌ 单列(需嵌套)

5.4 跨数据库迁移建议

从 MySQL 迁移到 TDengine
sql 复制代码
-- MySQL 原始查询
SELECT GROUP_CONCAT(DISTINCT name ORDER BY name SEPARATOR '|')
FROM users;

-- TDengine 等效查询(需要调整)
SELECT GROUP_CONCAT(name, '|')
FROM (SELECT DISTINCT name FROM users ORDER BY name);

注意事项

  • ❌ TDengine 不支持 DISTINCTORDER BY 子句
  • ✅ 需要通过子查询实现去重和排序
  • ✅ 分隔符位置不同(TDengine 在最后)
从 PostgreSQL 迁移到 TDengine
sql 复制代码
-- PostgreSQL 原始查询
SELECT STRING_AGG(name, ', ' ORDER BY name)
FROM users;

-- TDengine 等效查询
SELECT GROUP_CONCAT(name, ', ')
FROM (SELECT name FROM users ORDER BY name);

注意事项

  • ✅ 函数名不同但语义相似
  • ✅ 分隔符参数位置调整
  • ✅ 排序需通过子查询实现

6. 使用注意事项

6.1 NULL 值处理

sql 复制代码
-- NULL 值会被自动跳过
CREATE TABLE test_null (ts TIMESTAMP, name VARCHAR(20));
INSERT INTO test_null VALUES 
    ('2024-01-01 00:00:00', 'A'),
    ('2024-01-01 00:00:01', NULL),
    ('2024-01-01 00:00:02', 'B');

SELECT GROUP_CONCAT(name, ',') FROM test_null;
-- 输出:A,B (NULL 被跳过)

6.2 去重需要使用子查询

sql 复制代码
-- ❌ 错误:TDengine 不支持 DISTINCT 直接用在 GROUP_CONCAT 中
SELECT GROUP_CONCAT(DISTINCT location, ',') FROM meters;

-- ✅ 正确:通过子查询去重
SELECT GROUP_CONCAT(location, ',')
FROM (SELECT DISTINCT location FROM meters);

6.3 字符集处理

sql 复制代码
-- 自动处理 NCHAR 和 VARCHAR 混合
SELECT GROUP_CONCAT(varchar_col, nchar_col, '-')
FROM mixed_charset_table;
-- 会自动进行字符集转换

6.4 结果长度限制

sql 复制代码
-- 注意:结果字符串不能超过 TSDB_MAX_FIELD_LEN
-- 对于大量数据,可能需要分批处理或使用 LIMIT
SELECT 
    location,
    GROUP_CONCAT(tbname, ',') AS meters
FROM meters
GROUP BY location
HAVING COUNT(*) < 1000;  -- 避免结果过长

6.5 分隔符必需性

sql 复制代码
-- ❌ 错误:缺少分隔符
SELECT GROUP_CONCAT(name) FROM users;

-- ✅ 正确:必须提供分隔符
SELECT GROUP_CONCAT(name, ',') FROM users;

7. 性能优化建议

7.1 限制分组大小

sql 复制代码
-- ✅ 推荐:对大表使用 WHERE 过滤
SELECT 
    location,
    GROUP_CONCAT(tbname, ',') AS meters
FROM meters
WHERE ts >= NOW - 1d  -- 限制时间范围
GROUP BY location;

7.2 避免过大的结果集

sql 复制代码
-- ✅ 使用 HAVING 限制组大小
SELECT 
    location,
    GROUP_CONCAT(tbname, ',') AS meters
FROM meters
GROUP BY location
HAVING COUNT(*) <= 100;  -- 限制每组记录数

7.3 合理使用子查询

sql 复制代码
-- ✅ 先过滤再聚合
SELECT 
    location,
    GROUP_CONCAT(tbname, '|') AS active_meters
FROM (
    SELECT DISTINCT location, tbname 
    FROM meters 
    WHERE groupid = 1
    LIMIT 1000
)
GROUP BY location;

8. 常见问题 FAQ

Q1: GROUP_CONCAT 和 CONCAT 有什么区别?

A:

  • CONCAT标量函数,用于单行内多个字段的拼接
  • GROUP_CONCAT聚合函数,用于多行数据的汇总连接
  • CONCAT 不需要 GROUP BY,GROUP_CONCAT 通常配合 GROUP BY 使用

Q2: 如何实现 MySQL 的 DISTINCT 和 ORDER BY 功能?

A: 通过子查询实现:

sql 复制代码
-- 去重 + 排序
SELECT GROUP_CONCAT(name, ',')
FROM (
    SELECT DISTINCT name 
    FROM users 
    ORDER BY name
);

Q3: 分隔符可以省略吗?

A : 不可以。TDengine 的 GROUP_CONCAT 必须提供分隔符作为最后一个参数,这与 MySQL 不同。

Q4: 如何处理结果字符串过长的情况?

A:

  1. 使用 LIMIT 限制输入行数
  2. 使用 HAVING 过滤大组
  3. 缩短分隔符长度
  4. 分批查询

Q5: 能否连接数值类型字段?

A: 需要先转换为字符串类型:

sql 复制代码
SELECT GROUP_CONCAT(CAST(id AS VARCHAR), ',')
FROM users;

Q6: COUNT(DISTINCT ...) 不支持怎么办?

A: 使用子查询实现:

sql 复制代码
-- ❌ 不支持
SELECT COUNT(DISTINCT tbname) FROM meters;

-- ✅ 使用子查询
SELECT COUNT(*) FROM (SELECT DISTINCT tbname FROM meters);

Q7: PostgreSQL 用户如何适应 TDengine?

A:

  • STRING_AGG 改为 GROUP_CONCAT
  • 分隔符参数移到最后
  • 通过子查询实现排序

Q8: 空字符串和 NULL 有什么区别?

A:

  • NULL 值会被跳过,不影响结果
  • 空字符串 '' 会被保留,参与连接

Q9: 可以连接多少个字段?

A: 理论上没有字段数量限制,但要注意:

  • 总结果长度不能超过 TSDB_MAX_FIELD_LEN
  • 字段越多,性能可能下降

9. 相关函数

函数 类型 说明 关系
CONCAT 标量函数 单行多列拼接 行级操作,不聚合
CONCAT_WS 标量函数 带分隔符的单行拼接 类似 CONCAT
GROUP_CONCAT 聚合函数 多行汇总连接 分组聚合
STRING_AGG 聚合函数(PG) PostgreSQL 等效函数 功能相似

文档版本 :v3.3.8.0
最后更新 :2024-01-15
适用场景:智能电表数据分析、设备清单汇总、多行数据展示

重要提示

  • TDengine 不支持 WITH 子句(CTE)
  • TDengine 的 COUNT 函数不支持 DISTINCT,需使用子查询实现
  • GROUP_CONCAT 必须提供分隔符参数

关于 TDengine

TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。

相关推荐
曹轲恒17 分钟前
Java中断
java·开发语言
xxxmine28 分钟前
Java并发wait(timeout)
java
豆芽脚脚33 分钟前
MongoDB 导出和导入完整指南
数据库·mongodb
烧饼Fighting40 分钟前
Mysql替换为瀚高数据库部分函数转换V4.5版本
数据库·mysql
冰冰菜的扣jio1 小时前
Redis缓存问题——一致性问题、事务、持久化
java·spring·mybatis
施棠海1 小时前
监听与回调的三个demo
java·开发语言
Tel199253080041 小时前
全新C-Components高压继电器P/N 500-214
单片机·物联网·自动化·工业自动化
微光闪现1 小时前
AI识别宠物焦虑、紧张和晕车行为,是否已经具备实际可行性?
大数据·人工智能·宠物
上善若水_厚德载物1 小时前
Centos7 Mysql 5.7 读写分离
数据库·mysql
Mr__Miss2 小时前
Redis的持久化
数据库·redis·缓存