「Developers, Developers, Developers…」

— Steve Ballmer,2000 年 PDC,談 .Net

第 4 章的 AI 能力固然是開發者故事的一大塊,但 SQL Server 2025 給開發者的禮物遠不止於此。本章將帶你看遍 JSON、T-SQL、Change Event Streaming(CES)與內建 REST API 等亮點。

十年來最強的開發者版本#

SQL Server 2016 為開發者帶來 Query Store、JSON 支援、Temporal Tables、PolyBase、In-Memory OLTP 強化、Dynamic Data Masking 等。SQL Server 2016 SP1 更讓 Standard Edition 取得近乎與 Enterprise 一致的 T-SQL 介面。

SQL Server 2025 的開發者亮點:

Figure 5-1: SQL Server 2025 為開發者打造

功能說明
Data API Builder(DAB)在資料庫前端提供 REST 與 GraphQL 介面
JSON原生 json 資料型別、JSON 索引、新 T-SQL 函式
T-SQL正規表示式(RegEx)、新函式
Change Event Streaming(CES)從交易日誌串流變更事件,CDC 替代方案
REST APIsp_invoke_external_rest_endpoint 直接從引擎呼叫 REST
GitHub Copilot for SQLmssql 擴充套件深度整合 GitHub Copilot
mssql-pythonMicrosoft 自製的全新 Python driver,預計與 SQL Server 2025 GA 同期

Data API Builder(DAB)#

DAB 並非 SQL Server 2025 的新概念(前身為 Project Hawaii,2023 年春開放公開預覽),但在 SQL Server 2025 上潛力極大:

  • 在 SQL Server 之上提供 REST 或 GraphQL API
  • 取代 ODBC、ADO.Net 等 TDS 連線的「plumbing code」
  • 可獨立部署,或整合 Azure Static Web Apps、Container Apps、AKS、Web App for Containers
  • 文件:https://aka.ms/dab

Developer Edition 的雙風味#

過去若正式環境使用 Standard Edition,無法以 Developer Edition 完整測試其限制(Developer Edition 等同 Enterprise)。

SQL Server 2025 提供兩種風味

  • 模擬 Enterprise Edition
  • 模擬 Standard Edition

開發者不再需要為了「測試 Standard 限制」而消耗一份正式授權。

JSON#

SQL Server 2016 開始支援 JSON,但僅以 nvarchar 文字儲存:

  • 文件大時資料庫膨脹
  • 任何更新都要重寫整段文字
  • 必須先用 ISJSON() 驗證
  • 索引只能建在文字型別欄位上

Microsoft Mechanics 主持人 Jeremy Chapman 形容 SQL Server 2025 的新 JSON:「relational database 裡的 NoSQL database」。

原生 json 資料型別#

優點:

  • 更快讀取:文件已經解析
  • 更快寫入:可只更新單一值,不必重寫整份文件
  • 更省空間:壓縮最佳化(MVP Daniel Hutmacher 的測試顯示節省約 50%)
  • 既有程式碼相容
  • 內部使用 LOB 結構,每筆最大 2GB
  • 強制有效 JSON:插入無效 JSON 會直接報錯(Msg 13609
  • 支援 modify 直接更新內部單一值

JSON 索引#

  • 採用類似 XML 索引的結構(非傳統 B-tree)
  • 可用 CREATE JSON INDEX 建立在整欄或某個 JSON 路徑(path
  • JSON_PATHJSON_VALUEJSON_CONTAINS 等可受惠

新 T-SQL 函式#

  • JSON_ARRAYAGG:把多列彙總成單一 JSON 陣列
  • JSON_OBJECTAGG:以 key/value 對輸出 JSON
  • JSON_CONTAINS:路徑搜尋(含巢狀陣列)

範例:使用 json 資料型別#

USE orders;

-- 取出單一 JSON 值
SELECT o.order_id,
       JSON_VALUE(o.order_info, '$.AccountNumber') AS account_number
FROM dbo.Orders o;

-- 把所有列彙總成單一 JSON 陣列
SELECT JSON_ARRAYAGG(o.order_info)
FROM dbo.Orders o;

-- 以 key/value 對輸出
SELECT JSON_OBJECTAGG(o.order_id : o.order_info)
FROM dbo.Orders o;

-- 直接修改 JSON 內部值,不必整列重寫
UPDATE dbo.Orders
   SET order_info.modify('$.Quantity', 2)
WHERE order_id = 1;

Figure 5-2: 取出單一 JSON 值並以結果集回傳

Figure 5-3: 把所有列以 JSON 形式輸出

Figure 5-4: 將所有列彙總為單一 JSON 文件

Figure 5-5: 以 key/value 對輸出 JSON

範例:JSON 索引#

USE contactsdb;

-- 巢狀陣列搜尋(無索引時是 scan)
SELECT JSON_VALUE(jdoc, '$.name') AS name,
       JSON_QUERY(jdoc, '$.tags') AS tags
FROM contacts
WHERE JSON_CONTAINS(jdoc, 'fitness', '$.tags[*]') = 1;

-- 建立 JSON 索引
DROP INDEX IF EXISTS [ji_contacts] ON contacts;

CREATE JSON INDEX ji_contacts ON contacts(jdoc) FOR ('$');

Figure 5-6: 從資料表中擷取 JSON 值

Figure 5-7: 不使用 JSON 索引時於資料表搜尋值

Figure 5-8: 使用 JSON 索引的執行計畫

JSON 索引支援壓縮等選項。完整語法見 https://learn.microsoft.com/sql/t-sql/statements/create-json-index-transact-sql

T-SQL 強化#

正規表示式(Regular Expressions, RegEx)#

SQL Server 2025(dbcompat 170)終於支援 RegEx,遵循 POSIX 標準。

函式對應傳統做法
REGEXP_LIKE取代 LIKE
REGEXP_REPLACE強化版 REPLACE
REGEXP_SUBSTR強化版 SUBSTRING
REGEXP_INSTR結合 CHARINDEX + PATINDEX
REGEXP_COUNT結合 LEN + REPLACE
REGEXP_MATCHES回傳所有符合的 substring(資料表)
REGEXP_SPLIT_TO_TABLE以 RegEx 切字成資料表

範例:CHECK constraint 用 RegEx 驗證 email 與電話#

CHECK (REGEXP_LIKE(Email, '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'))

CHECK (REGEXP_LIKE(PhoneNumber,
    '^(?:\+\d{1,3}[ -]?)?(?:\([2-9]\d{2}\)[ -]?\d{3}-\d{4}|[2-9]\d{2}[ -]?\d{3}-\d{4})$'))

複雜的 email/phone 驗證,傳統 LIKE 幾乎做不到,現在一行 RegEx 搞定。

範例:複雜搜尋#

-- 找 .org 結尾、local part 含 .will + 至少 3 個英文字母的 email
SELECT [Name], Email
FROM dbo.EMPLOYEES
WHERE REGEXP_LIKE(LOWER(Email), '^[^@]*\.will[a-z]{3,}@[a-z0-9.-]+\.org$');

其他 T-SQL 強化#

函式 / 功能說明
PRODUCT()數值乘積彙總,取代 EXP(SUM(LOG(...))) 的繞遠路;支援 batch mode、忽略 NULL、OVER 子句
BASE64_ENCODE / BASE64_DECODE內建 Base64 編碼(依 RFC 4648 §4)
EDIT_DISTANCEEDIT_DISTANCE_SIMILARITYFuzzy string 比對(編輯距離)
JARO_WINKLER_DISTANCEJARO_WINKLER_SIMILARITYFuzzy 比對的 de facto 標準(GA 後可能仍需 PREVIEW_FEATURES
CURRENT_DATEANSI 標準的當天日期函式
SUBSTRINGlength 改為選填(符合 ANSI)
UNISTR處理多重 Unicode 跳脫序列,方便插入 emoji 等字元
||ANSI 字串串接運算子,等同 +CONCAT
DATEADD第二個 number 參數支援 bigint
-- PRODUCT
SELECT GroupId, PRODUCT(Value) AS ProductOfValues
FROM MyTable
GROUP BY GroupId;

-- UNISTR:插入 emoji
SELECT UNISTR(N'Hello \\+1F603');

適用情境:複利、機率、良率、幾何平均等。把運算放在 T-SQL,比在應用程式做更快。

Change Event Streaming(CES)#

CDC、CT 與 CES 的差異#

功能取得變更方式傳輸主要痛點
Change Tracking(CT)系統表記錄哪些列變更(無資料歷程)應用程式查詢
Change Data Capture(CDC)LogReader Agent job 寫到系統表應用程式查詢仰賴 Agent、清理 job、I/O 雙倍、寫入產生額外 log
Change Event Streaming(CES)背景 worker 直接讀 transaction log推送(push) 到 Azure Event HubPREVIEW_FEATURES

運作方式#

CES 重用 Synapse Link(SQL Server 2022)的引擎元件。啟用後可看到 wait_type = SYNAPSE_LINK_COMMIT 的背景 worker。worker 採池化(pool)設計,多資料庫啟用 CES 不會耗盡 worker pool。

  • per-database 為單位
  • 目前唯一支援的目的端是 Azure Event Hub
  • Synapse Link 是把變更落到 Azure Storage Landing Zone;CES 則直接 stream HTTP 到 Event Hub
  • 訊息採 CloudEvents 標準(CNCF 規格)
  • 兩種傳輸協定:AMQP(Event Hubs 標準)或 Kafka(Apache Kafka on Event Hubs 相容端點)

訊息格式#

  • INSERT → 新列
  • UPDATE → 舊列 + 新列
  • DELETE → 被刪的列
  • 訊息自描述 schema——包含資料庫、資料表、欄位定義,可以建構通用處理系統

限制與注意事項#

重要限制:

  • 必須啟用 PREVIEW_FEATURES
  • CES 會延後 log truncation——log record 無法在訊息成功送達 Event Hub 前截斷;但如果應用消費端慢,不會影響 truncation
  • 不能與下列共存:Fabric Mirroring、Transactional Replication、CDC、Synapse Link
  • Change Tracking(CT)可與 CES 共存
  • 啟用 CES 的表 不能 TRUNCATE
  • DDL 可變更,但不會發出 DDL 訊息,後續訊息會反映 schema 變更
  • 沒有初始 snapshot——如需要,由開發者自行處理

範例:Contoso 出貨問題(CES + Azure Function + AI Agent)#

這個範例需要:

  • Azure 訂閱(不能用免費)
  • Azure Event Hub、Azure Function App、Azure AI Foundry 中的 AI Agent
  • SQL Server 2025 Developer/Enterprise Edition(混合模式驗證 + sysadmin 登入)
  • VSCode 用以執行 Azure Function

流程#

flowchart LR
    A["SQL Server 2025\nOrders 資料表"] -- "INSERT" --> B["CES"]
    B -- "CloudEvents" --> C["Azure Event Hub"]
    C --> D["Azure Function"]
    D -- "判定預估出貨日 > 30 天" --> E["Azure AI Agent\n(gpt-4.5-preview)"]
    E -- "建議的解決方案" --> D
    D -- "更新客戶/物流" --> F["後續處理"]

Figure 5-9: 使用 CES 解決出貨問題

啟用 CES#

-- 1. 建立資料庫、啟用 PREVIEW_FEATURES、建立 DATABASE SCOPED CREDENTIAL
USE [ContosoOrders];

-- 2. 啟用 CES
EXEC sys.sp_enable_event_stream;

-- 3. 建立 stream group 並加入資料表(指向 Azure Event Hub)

-- 4. 檢查設定
EXEC sys.sp_help_change_feed;
EXEC sys.sp_help_change_feed_table @source_schema = 'dbo', @source_name = 'Orders';

監視訊息#

SELECT * FROM sys.dm_change_feed_log_scan_sessions
ORDER BY start_time DESC;

Figure 5-10: 在 Data Explorer 中檢視來自 CES 的測試訊息

看到 error_count > 0 時,查 sys.dm_change_feed_errors 取得錯誤細節。

Azure Function 程式片段(C#)#

public async Task Run(
    [EventHubTrigger("<AEH>", Connection = "<AEHspace>_<policy>_EVENTHUB")]
    EventData[] events)
{
    // 解析 CES 訊息
    if (DateTime.TryParse(currentRow["SalesDate"]?.ToString(), out DateTime salesDate)
     && DateTime.TryParse(currentRow["EstimatedShipDate"]?.ToString(), out DateTime estShip))
    {
        if ((estShip - salesDate).TotalDays > 30)
        {
            await CheckShipment(currentRow);   // 呼叫 AI Agent
        }
    }
}

AI Agent 的 system instruction:「分析 Contoso 訂單的出貨資訊,評估是否需要尋找替代方案以加速延遲的出貨。」

下達一筆 75 天才出貨的訂單,AI Agent 會自動回傳處理建議。

Figure 5-11: 出貨案例 AI 協助的結果

CES 將是開發者建構事件驅動架構的未來;Fabric Mirroring 偏向多數使用者的預設選擇。後續路線圖包括 Kafka 等更多目的端。

REST API:sp_invoke_external_rest_endpoint#

起源:Project Solaria#

Figure 5-12: Project Solaria 私人預覽

2022 年的 Project Solaria 原本針對 Azure Functions,後來擴展為「在 SQL Server 引擎內透過 HTTPS 呼叫任意 REST 端點」。命名靈感來自 Asimov 的小說《The Naked Sun》中的 Solaria 行星——當時在 SQL 中支援 REST 被視為「異端傳統」。

安全性#

安全前提:

  • 必須先啟用 external rest endpoint enabled 伺服器設定
  • 使用者需 EXECUTE ANY EXTERNAL ENDPOINT 權限(預設只有 sysadmin)
  • 必須 HTTPS——所有流量以 TLS 加密

預設是「關」的;不需要額外的「關閉開關」。

語法#

EXECUTE @returnValue = sp_invoke_external_rest_endpoint
    [ @url = ] N'url'
  [ , [ @payload = ] N'request_payload' ]
  [ , [ @headers = ] N'http_headers_as_json_array' ]
  [ , [ @method = ] 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' ]
  [ , [ @timeout = ] seconds ]
  [ , [ @credential = ] credential ]
  [ , @response OUTPUT ]
  [ , [ @retry_count = ] # of retries if there are errors ]

重要參數#

  • URL:HTTPS endpoint
  • Method:預設 POST
  • Headers:metadata;SQL Server 一律自動注入 content-type: application/json; charset=utf-8accept: application/jsonuser-agent: <EDITION>/<VERSION>
  • Payload:JSON / XML / 文字
  • ResponseOUTPUT 參數(NVARCHAR(MAX)
  • CredentialDATABASE SCOPED CREDENTIAL,可為 API key 或 Managed Identity;用 IDENTITY = 'HTTPEndpointHeaders' 把值塞進 headers

引擎行為#

引擎細節:

  • 同步呼叫;T-SQL 須等待 REST 服務回應
  • SQL worker thread 以 preemptive 模式執行,避免阻塞 SQLOS scheduler
  • 並發上限:150 個 worker thread 或 10% 的 max worker threads——超過會出 Msg 10928
  • wait_type = HTTP_EXTERNAL_CONNECTION

回傳值#

  • 0:HTTPS 成功(HTTP 2xx)
  • 非 0:HTTP 狀態碼
  • HTTPS 連線本身失敗:拋例外
  • Msg 11558:REST 回傳的 JSON 無法被解析

社群應用範例#

作者場景
Anthony Nocentino(Pure Storage MVP)Pure Storage REST API + SQL snapshot 備份目錄
Anthony Nocentino + Andy YunPure Storage I/O 指標 + SQL Server 端到端效能監控
Andrew Pruski從 SQL Server 內存取 Kubernetes API

範例:用 REST API 完成 RAG 故事#

Figure 5-13: 為 RAG 加入向量搜尋

延續第 4 章的向量搜尋,再呼叫 chat completion 模型分析結果。

-- STEP 1: 把向量搜尋結果存入暫存表
DROP TABLE IF EXISTS #ProcResult;
CREATE TABLE #ProcResult
(
    ProductName NVARCHAR(200) NULL,
    ProductDescription NVARCHAR(MAX) NULL,
    StockLevel INT NULL
);

INSERT INTO #ProcResult (ProductName, ProductDescription, StockLevel)
EXEC find_relevant_products_vector_search
    @prompt = N'Show me stuff for extreme outdoor sports',
    @stock = 100,
    @top = 20;

-- STEP 2: 把結果格式化成 JSON
DECLARE @resultSetJson NVARCHAR(MAX);
SELECT @resultSetJson =
(
    SELECT ProductName, ProductDescription
    FROM #ProcResult
    FOR JSON PATH, INCLUDE_NULL_VALUES
);

-- STEP 3: 組裝 chat completion 的 payload
DECLARE @escapedJson NVARCHAR(MAX) = STRING_ESCAPE(@resultSetJson, 'json');
DECLARE @url NVARCHAR(MAX) = N'https://<azureai>/openai/deployments/gpt-4o/chat/completions?api-version=2025-01-01-preview';
DECLARE @credential NVARCHAR(4000) = N'https://<azureai>.openai.azure.com';
DECLARE @userPrompt NVARCHAR(MAX) = N'Are these good products for someone who is an extreme sports enthusiast. Anything missing?';

DECLARE @payload NVARCHAR(MAX) =
N'{
  "messages": [
    { "role": "system", "content": "You are a helpful assistant that analyzes small SQL result sets about products from an outdoor sports company. Be concise, structured, and actionable." },
    { "role": "user",   "content": "' + @userPrompt + '"},
    { "role": "user",   "content": "' + @escapedJson + '"}
  ],
  "temperature": 0.7
}';

-- STEP 4: 呼叫 chat completion 模型
DECLARE @statusCode INT, @response NVARCHAR(MAX);
EXEC @statusCode = sp_invoke_external_rest_endpoint
    @url        = @url,
    @method     = N'POST',
    @payload    = @payload,
    @credential = @credential,
    @response   = @response OUTPUT,
    @timeout    = 120;

-- STEP 5: 取出回應內容
SELECT c.content
FROM OPENJSON(@response, '$.result.choices')
WITH ( content NVARCHAR(MAX) '$.message.content' ) AS c;

Figure 5-14: 於 Azure AI Foundry 取得 AI 模型的端點

常見錯誤:

  • HTTP 429:「Requests to the OpenAI API have been throttled. Please retry after some time.」需要增加資源或加上延遲
  • HTTP 400:payload 格式錯誤,@response 會帶詳細錯誤

模型回應仍是 JSON 結構,需用 OPENJSON 取出 $.message.content

模型實際回傳的內容會分析產品適合度、缺口(缺少安全護具、特殊配件、服飾、其他極限運動裝備、維修工具)並給出強化建議——全部在 T-SQL 與 SQL Server 安全邊界內完成。

現代的 SQL 開發者#

SQL 開發者回來了。把半結構化文件交給原生 JSON、把 RegEx 留在 T-SQL、用 CES 建構事件驅動架構,再用 sp_invoke_external_rest_endpoint 串接地端或雲端 REST 服務(含 chat completion 模型);結合第 4 章的向量搜尋,便能打造現代資料應用平台。

  • 開發者最新動態:https://aka.ms/sqldev
  • Azure SQL Devs’ Corner:https://devblogs.microsoft.com/azure-sql

下一章將進入 Azure Arc,把 SQL Server 與雲端連結起來。