源表 FROMTABLE 記錄任務明細:
| TASK_ID | EMPLOYEE_NAME | STEPWORKTIME |
|---|---|---|
| 6262 | 張三 | 2 |
| 6262 | 李四 | 2 |
| 6262 | 王二 | 1.5 |
| 6265 | 成某 | 2 |
| 6265 | 趙某 | 2 |
| 6265 | 錢某 | 1.5 |
目標表 TOTABLE 需聚合為:
| TASK_ID | EMPLOYEES (逗號分隔) | WORKLOAD (總工時) |
|---|---|---|
| 6262 | 張三,李四,王二 | 5.5 |
| 6265 | 成某,趙某,錢某 | 5.5 |
-- 方案 1: LISTAGG (最簡潔,需 DB2 10.1+ 或啟用兼容性向量)
INSERT INTO TOTABLE (ID, NAME, SUM)
SELECT TASK_ID,
LISTAGG(EMPLOYEE_NAME, ',') WITHIN GROUP (ORDER BY EMPLOYEE_NAME),
SUM(STEPWORKTIME)
FROM FROMTABLE
GROUP BY TASK_ID;
-- 方案 2: XMLAGG + REPLACE (通用,DB2 9.7+)
INSERT INTO TOTABLE (ID, NAME, SUM)
SELECT TASK_ID,
REPLACE(REPLACE(XMLSERIALIZE(XMLAGG(XMLTEXT(CONCAT(',', EMPLOYEE_NAME))) AS VARCHAR(10000)), '<', ''), '>', ''),
SUM(STEPWORKTIME)
FROM FROMTABLE
GROUP BY TASK_ID;
適用於不支持聚合函數的老版本,或需在循環中執行複雜業務邏輯的場景。
CREATE TABLE FROMTABLE (ID VARCHAR(10), NAME VARCHAR(200), STEPWORKTIME DECIMAL(5,2));
CREATE TABLE TOTABLE (ID VARCHAR(100), NAME VARCHAR(5000), SUM DECIMAL(10,2));
CREATE PROCEDURE ADMINISTRATOR.PROC_CONCAT_NAME ()
LANGUAGE SQL
BEGIN
-- 外層游標:按 TASK_ID 分組計數
DECLARE C1 CURSOR FOR
SELECT ID, COUNT(*) AS CNT
FROM FROMTABLE
GROUP BY ID
ORDER BY ID;
DECLARE V_ID VARCHAR(100);
DECLARE V_CNT INT;
DECLARE V_IDX INT;
DECLARE V_NAMES VARCHAR(5000);
DECLARE V_SUM DECIMAL(10,2);
DECLARE AT_END INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET AT_END = 1;
OPEN C1;
OUTER_LOOP: LOOP
FETCH C1 INTO V_ID, V_CNT;
IF AT_END = 1 THEN LEAVE OUTER_LOOP; END IF;
-- 內層:按 ROW_NUMBER 逐行提取拼接
SET V_IDX = 1;
SET V_NAMES = '';
SET V_SUM = 0;
INNER_LOOP: LOOP
IF V_IDX > V_CNT THEN LEAVE INNER_LOOP; END IF;
-- 子查詢定位第 V_IDX 行 (同一 TASK_ID 內)
SET V_NAMES = COALESCE(V_NAMES || ',', '') || (
SELECT NAME FROM (
SELECT ROW_NUMBER() OVER() AS RN, NAME
FROM FROMTABLE WHERE ID = V_ID
) T WHERE RN = V_IDX
);
SET V_SUM = V_SUM + (
SELECT STEPWORKTIME FROM (
SELECT ROW_NUMBER() OVER() AS RN, STEPWORKTIME
FROM FROMTABLE WHERE ID = V_ID
) T WHERE RN = V_IDX
);
SET V_IDX = V_IDX + 1;
END LOOP INNER_LOOP;
INSERT INTO TOTABLE (ID, NAME, SUM) VALUES (V_ID, V_NAMES, V_SUM);
END LOOP OUTER_LOOP;
CLOSE C1;
END
ROW_NUMBER() OVER() 在子查詢中為同一 TASK_ID 內行生成遞增序號,實現「按位置取值」COALESCE(V_NAMES || ',', '') 避免首個前綴逗號DECIMAL 保留小數 (原文用 INT 導致 1.5 截斷為 1)CALL ADMINISTRATOR.PROC_CONCAT_NAME();
SELECT * FROM TOTABLE;
-- 預期輸出:
-- 6262 | 張三,李四,王二 | 5.5
-- 6265 | 成某,趙某,錢某 | 5.5
| 方案 | 適用版本 | 性能 | 可維護性 | 推薦度 |
|---|---|---|---|---|
| LISTAGG | 10.1+ / 兼容向量 | 極高 (單掃描) | ★★★★★ | 首選 |
| XMLAGG | 9.7+ | 高 | ★★★★☆ | 備選 |
| 游標循環 | 所有版本 | 低 (多掃描+上下文切換) | ★★☆☆☆ | 特殊邏輯/老版本 |
行轉列聚合是經典場景,**現代 DB2 首選 LISTAGG/XMLAGG 集合函數**,單語句完成、優化器最優、無上下文切換開銷。游標循環僅作為教學演示或極舊版本兜底。遷移老代碼時,務必識別此類模式並重構為集合操作。