SQL Server中如何避免Foreach实现RxCui与Ndc的标签关联映射?
嘿,我明白你的痛点——不想用foreach挨个调用存储过程,又怕一次性传所有RxCui和Ndc会生成全量的无效关联。下面有两个靠谱的方案,帮你高效解决这个问题:
方案1:直接传入完整JSON到存储过程,SQL端解析处理
这种方法把整个DrugLabels对象序列成JSON直接传给存储过程,让SQL Server自己解析每个标签的RxCui和Ndc组,只建立同标签内的关联,完全不用C#端循环调用。
第一步:修改存储过程,支持JSON参数
CREATE PROCEDURE MapRxCuiToNdcByJson @drugLabelsJson NVARCHAR(MAX) AS BEGIN SET NOCOUNT ON; -- 解析JSON,拆分出每个标签下的RxCui和Ndc配对 WITH LabelAssociations AS ( SELECT rxcui.value AS RxCuiCode, ndc.value AS NdcCode FROM OPENJSON(@drugLabelsJson, '$.results') -- 遍历每个DrugLabel CROSS APPLY OPENJSON(JSON_VALUE([value], '$.openfda.rxcui')) AS rxcui -- 遍历当前标签的RxCui列表 CROSS APPLY OPENJSON(JSON_VALUE([value], '$.openfda.product_ndc')) AS ndc -- 遍历当前标签的Ndc列表 ) -- 关联到已有的RxCui/Ndc表,插入关联(自动跳过已存在的配对) MERGE INTO [Common].[RxCuiNdc] T USING ( SELECT r.RxCuiId, n.NdcId FROM LabelAssociations la INNER JOIN Common.RxCui r ON r.Code = la.RxCuiCode INNER JOIN Common.Ndc n ON n.Code = la.NdcCode ) S ON T.RxCuiId = S.RxCuiId AND T.NdcId = S.NdcId WHEN NOT MATCHED THEN INSERT (RxCuiId, NdcId) VALUES (S.RxCuiId, S.NdcId); END
第二步:C#端调用,直接传序列化后的JSON
// 把整个DrugLabels对象序列化为JSON字符串 var json = JsonSerializer.Serialize(drugLabels); // 调用存储过程(记得替换你的连接字符串) using (var conn = new SqlConnection("YourDatabaseConnectionString")) { await conn.OpenAsync(); using (var cmd = new SqlCommand("MapRxCuiToNdcByJson", conn)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@drugLabelsJson", SqlDbType.NVarChar, -1).Value = json; await cmd.ExecuteNonQueryAsync(); } }
方案2:用表值参数(TVP)批量传配对数据
如果你不想在SQL里处理JSON,可以在C#端先整理好所有需要关联的(RxCuiCode, NdcCode)配对,再用表值参数一次性传给存储过程——比传逗号分隔的字符串靠谱多了,不会因为代码里带逗号导致拆分错误。
第一步:创建SQL表值类型
CREATE TYPE RxCuiNdcPairType AS TABLE ( RxCuiCode VARCHAR(256) NOT NULL, NdcCode VARCHAR(256) NOT NULL );
第二步:修改存储过程接收表值参数
CREATE PROCEDURE MapRxCuiToNdcByTVP @rxCuiNdcPairs RxCuiNdcPairType READONLY AS BEGIN SET NOCOUNT ON; MERGE INTO [Common].[RxCuiNdc] T USING ( SELECT r.RxCuiId, n.NdcId FROM @rxCuiNdcPairs lp INNER JOIN Common.RxCui r ON r.Code = lp.RxCuiCode INNER JOIN Common.Ndc n ON n.Code = lp.NdcCode ) S ON T.RxCuiId = S.RxCuiId AND T.NdcId = S.NdcId WHEN NOT MATCHED THEN INSERT (RxCuiId, NdcId) VALUES (S.RxCuiId, S.NdcId); END
第三步:C#端准备数据并调用
// 准备存储配对的数据表 var pairTable = new DataTable(); pairTable.Columns.Add("RxCuiCode", typeof(string)); pairTable.Columns.Add("NdcCode", typeof(string)); // 遍历每个标签,把内部的RxCui和Ndc交叉添加到数据表 foreach (var label in drugLabels.results) { foreach (var rxcui in label.openfda.rxcui) { foreach (var ndc in label.openfda.product_ndc) { pairTable.Rows.Add(rxcui, ndc); } } } // 调用存储过程 using (var conn = new SqlConnection("YourDatabaseConnectionString")) { await conn.OpenAsync(); using (var cmd = new SqlCommand("MapRxCuiToNdcByTVP", conn)) { cmd.CommandType = CommandType.StoredProcedure; // 配置表值参数 var tvpParam = new SqlParameter("@rxCuiNdcPairs", SqlDbType.Structured) { TypeName = "RxCuiNdcPairType", Value = pairTable }; cmd.Parameters.Add(tvpParam); await cmd.ExecuteNonQueryAsync(); } }
方案对比
- 方案1:完全消除C#端的循环,一次数据库调用搞定,适合数据量较大的场景,SQL端处理逻辑更集中。
- 方案2:C#端做数据整理,SQL端逻辑更简单,表值参数的方式比字符串拆分更稳定,不会出现特殊字符导致的错误。
两种方案都不会生成全量交叉的无效关联,只会建立每个标签内部RxCui和Ndc的对应关系,完美解决你的问题~
内容的提问来源于stack exchange,提问作者joey0xx
相关产品推荐
相关产品推荐

