本节摘要:不是所有数据都长得像表格,也不是所有关系都适合写连接。本节讲三件收纳工具:JSON 函数把半结构化数据放进关系表并保留可查询性,图形对象让多跳关系查询免掉层层自连接,机器学习服务把模型算力搬进引擎。三件工具的共同纪律是"扩展而非替代"——用它们收编边界数据,别推翻已经建好的关系模型。
JSON 的正确姿势是"主键与常用查询列保持真列,易变与个性化字段进 JSON 列"。订单的收货地址、商品快照、个性化参数这类"结构因单而异"的数据,拆成几十个可空列是噩梦,整体存文本又不可查——JSON 列正好:存进去是文本,用函数表达式建计算列与索引后,等值与范围查询照样走索引。解析函数分两组读:ISJSON 验合法性,JSON_VALUE 取标量、JSON_QUERY 取子对象、OPENJSON 把数组展开成行集;写侧用 JSON_MODIFY 原位改值。2016 版引入这套函数时没有原生 JSON 类型,2024 版本线补上了原生 json 类型——写法在演进,"常用列真列化加尾部 JSON"的设计原则不变。
-- JSON 列:存储、计算列、索引、查询一条龙 CREATE TABLE dbo.订单扩展 ( OrderId BIGINT PRIMARY KEY, ExtJson NVARCHAR(MAX), -- 柔性区 DeliveryCity AS NVARCHAR(32), PERSISTED)) AS 免解析城市列 -- 计算列提取常用字段 ); CREATE INDEX IX_订单扩展_城市 ON dbo.订单扩展 (DeliveryCity); -- 走索引的城市过滤 SELECT OrderId, JSON_VALUE(ExtJson, '$.delivery.slot') AS 配送时段, j.[key] AS 属性名, j.[value] AS 属性值 FROM dbo.订单扩展 CROSS APPLY OPENJSON(ExtJson, '$.tags') j -- 数组展开成行集 WHERE DeliveryCity = N'杭州';
图形对象解决的是另一类痛:多跳关系。社交网络"朋友的朋友买了什么"、权限体系"这个角色经由哪些组继承了哪些权限",在关系模型里是层层自连接,跳数一多查询既难写又难优化。图形表把节点(如 Person、Product)与边(如 Friend、Bought)做成带 MATCH 语法的专用表,两跳三跳的查询写成一条 MATCH 路径。它的边界也要说清:图形对象的优化器成熟度与生态工具仍弱于关系表,全局图计算(社区发现、路径最优化)也不是它的强项——两三跳的关系探索用图形,深度的图分析交给专用图平台,数据同步走 ETL。
-- 图形表:人员节点、关注边、两跳查询 CREATE TABLE dbo.人员 (PersonId BIGINT PRIMARY KEY, Name NVARCHAR(64)) AS NODE; CREATE TABLE dbo.关注 (Since DATE) AS EDGE; ALTER TABLE dbo.关注 ADD CONSTRAINT EC_关注 CONNECTION (dbo.人员 TO dbo.人员); SELECT f2.Name AS 朋友的朋友 FROM dbo.人员 a, dbo.关注 e1 ON MATCH (a-(e1)->b), dbo.关注 e2 ON MATCH (b-(e2)->f2) WHERE a.Name = N'老周' AND f2.Name <> a.Name; -- 递归公用表表达式也能写,但跳数一深,MATCH 的可读性与优化质量都占优
把数据搬到外部集群训练,再搬回结果——搬运本身就是成本与风险。机器学习服务的思路相反:在引擎内跑外部脚本(Python 或 R),数据以帧形式直接传给运行时,结果集流回 T-SQL。安全边界靠三条制度守:外部脚本执行权限单列(默认连管理员都没开);运行时以最低权限的独立工作账户运行,碰不到实例敏感资源;脚本与数据的交换通道受控。适用场景是"数据在本库、计算密集但单机可承受"的评分与统计——比如每夜给全量客户打流失分、对订单流做异常检测。边界同样清楚:大规模训练、GPU 需求、分布式计算,都该去专用的机器学习平台,库内服务只做"离数据最近的那一段"。
-- 库内评分的最小骨架(先由管理员启用外部脚本) EXEC sys.sp_configure 'external scripts enabled', 1; RECONFIGURE; EXECUTE sp_execute_external_script @language = N'Python', @script = N' import pandas result = pandas.DataFrame({ "OrderId": InputDataSet["OrderId"], "Score": (InputDataSet["Recency"] < 7).astype(int) })', @input_data_1 = N'SELECT OrderId, Recency FROM dbo.活跃订单', @output_data_1_name = N'result' WITH RESULT SETS ((OrderId BIGINT, Score INT)); -- 生产化路径:把这段评分封装成存储过程,挂到夜间代理作业
一次应用归档:营销团队每月要"七天未复购客户"名单,原流程是导出 CSV 到分析机跑脚本再导回名单,一来一回两天。改为库内 Python 评分后,夜间作业十分钟出分,名单表直接对接推送系统。省下的不只是时间——导出环节的数据安全审批整个免了,因为数据从未离开库。这正是枢纽哲学(数据不动)在 AI 场景的兑现。
本库的柔性数据收编了,库外的数据湖与异构库呢?下一节的 PolyBase 给出"不搬运、直接问"的答案。