☘️ 人的一生中有三大難題:

  • 中午吃什麼?
  • 明天要不要請假?
  • 怎麼把一堆資料拼成一句話?

前兩個我還在想,第三個已經找到解答了—— STRING_AGG。



他是 SQL 裡常用來做文字串接的聚合函數,官方定義是這樣子的 :
用於把 “指定的欄位” 串成一個 “以指定分隔符分隔的字符串”,不用寫一堆複雜的迴圈或自製拼接邏輯


1
2
3
4
5
6

STRING_AGG ( expression, separator ) [ <order_clause> ]

<order_clause> ::=
WITHIN GROUP ( ORDER BY <order_by_expression_list> [ ASC | DESC ] )

說到文字串接就讓我想到,每天上班打開公司群組,就像打開一個沒人整理過的資料表,一堆廢話、貼圖、+1、讚、下午茶,語焉不詳的訊息而零散像剛剛爆炸過。有時真的很想把他們 STRING_AGG 起來丟到任何看不見的地方





🧪 實戰

「那個誰,幫我把這個資料表裡的這些串在一起」老闆手上揮舞著資料像在招魂一樣,語音剛落,作為一個專業社畜我們當然知道要求是什麼,把同一筆訂單底下的商品串在一起對吧?麻~(嗯?)

這是我們的資料

Image

1
2
3
4
5
6
7
8
9

SELECT
OrderID,
STRING_AGG(ProductName, ', ') AS ProductList
FROM
OrderDetails
GROUP BY
OrderID;

Image

ok 感覺不戳,資料寄出去趕快下班!

ヘ( ゚∀゚;)ノ
ヘ( ゚∀゚;)ノ
ヘ( ゚∀゚;)ノ

隔天…

レ(゚∀゚;)ヘ
レ(゚∀゚;)ヘ
レ(゚∀゚;)ヘ

老闆 : 「欸..我們討論一下,這個可以排序嗎」

我 : 「喔好啦 我想想」我騷騷頭

老闆 : 「👌」

1
2
3
4
5
6
7
8
9

SELECT
OrderID,
STRING_AGG(ProductName, ', ') WITHIN GROUP (ORDER BY ProductId) AS ProductList
FROM
OrderDetails
GROUP BY
OrderID;

WITHIN GROUP 派上用場,有點像 Window Function

我真神,交差,收拾收拾準備下班

老闆 : 「欸..有一件事要順便一下」

(喔? 順便什麼? 發獎金給我嗎?)

老闆打開另一個視窗,秀出另外一組資料

老闆 : 「你看… 這些簡訊發出去的號碼都是哪些人啊?」

「喔,你看這裡可以查到電話,這封簡訊就是對應這個號碼喔」

老闆 : 「不不不 我是說 哪些人」

「喔~」

他需要找出這些電話號碼對應的會員名稱有哪些人

把包包放下,喝一口水壓壓驚,再次打開電腦尻 SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

DECLARE @SmsMessageCellphones NVARCHAR(MAX);

DROP TABLE IF EXISTS #MemberData_0606;

-- 需要Cellphone List 撈取跨伺服器 Member Data
SELECT @SmsMessageCellphones = STRING_AGG(CAST(JSON_VALUE(Task_Data, '$.PhoneNumber') AS NVARCHAR(MAX)), ',')
FROM Task
WHERE Task_ValidFlag = 1
AND Task_JobId = 250
AND Task_BookingTime > '2024-05-05 00:00:00'
AND Task_BookingTime < '2024-06-07 00:00:00'
AND Task_Status = 'Switched'
AND JSON_VALUE(Task_Data, '$.SMSType') = N'會員註冊';


-- 跨伺服器撈取 Member 資料
INSERT INTO #MemberData_0606
EXECUTE WEBSTOREROLS.WebStoreDB.dbo.sp_executesql N'
SELECT MemberRegister_ShopId,
MemberRegister_CellPhone
FROM dbo.MemberRegister(NOLOCK)
WHERE MemberRegister_CellPhone IN (SELECT CAST(value AS nvarchar(MAX)) FROM STRING_SPLIT(@SmsMessageCellphones, '',''))
AND MemberRegister_ValidFlag = 1
',N'@SmsMessageCellphones NVARCHAR(MAX)', @SmsMessageCellphones = @SmsMessageCellphones;

好累,下班 (☍﹏⁰)

Image

💩 一些可能會踩到的坑

工作中,我們總是在採坑,不如說,工作本身就是一個坑
工作我們實在避不掉,但 SQL 嘛,坑還是能少踩一點是一點。我們稍微討論一下有甚麼基本可以注意的事情吧!

☑️ 空值處理

如果欄位可能會是 NULL,那這些值會被自動忽略。
想控制一下,可以這樣做:

1
2
3
4

SELECT STRING_AGG(ISNULL(Department, 'N/A'), ', ') AS AllDepartments
FROM Employees;

☑️ 長度限制

拼接起來的字串可能超過欄位或變數最大容量,
建議加上 LEN() 檢查,或依需求拆批處理,避免過長爆掉。

☑️ 應用到子查詢或 CTE

可以把 STRING_AGG 包進 CTE 或子查詢裡做更進一步邏輯處理。

例子 : 在一個產品銷售資料表中,將每個地區的銷售員名字串接為一個字串,然後再將各地區的結果串接為一個總合。

1
2
3
4
5
6
7
8
9
10
11

WITH RegionAggregates AS (
SELECT
Region,
STRING_AGG(Salesperson, ', ') AS Salespersons
FROM Sales
GROUP BY Region
)
SELECT STRING_AGG(Region + ': ' + Salespersons, '; ') AS Summary
FROM RegionAggregates;

🐛 結語

本篇主要探討如何使用這個字串串接函數以及案例的分享,關於效能方面是完全沒考慮的,因此使用上務必研究過對正式環境是否不會造成嚴重影響

就像多數職場神器一樣,用得好你是效率王,用不好你是資源炸彈擁有者。
所以請記住:測試環境跑得動 ≠ 上線不會爆STRING_AGG 雖然萬用,但遇到大量資料還是要小心別自己變成那顆炸彈 💣

最後補一句

          如果人生也能用 STRING_AGG 把快樂的記憶串起來

           那請記得在最後加上:WITHIN GROUP (ORDER BY 心情 DESC)