文章目录
1、需求
有一个excel,读取到datatable后,需要通过id,获取name,并替换到excel的对应列。
那么可能查询需求有10行,返回只有9行。
为了替换的1对1匹配,查询和结果的key(行号)一致就很主要了。
2、落地
将
11[A]''AA'' [T] 22[A]''BB'' [T] 33[A]''CC''
// 仿 [{Row:'11',Id='AA'},{Row:'22',Id='BB'},{Row:'33',Id='CC'}]
转换为
(SELECT Top 1 11 as ROW, Name From TableA Where Id='AA') UNION ALL
(SELECT Top 1 22 as ROW, Name From TableA Where Id='BB' ) UNION ALL
(SELECT Top 1 33 as ROW, Name From TableA Where Id='CC' )
不能使用STRING_SPLIT,有点打脑壳。
但是查了要改到前端,所以必须锁定数据和查询的1对1关系。
3、实现
sql
-- =============================================
ALTER FUNCTION [dbo].[F_OptimizeOr]
( @WhereStr NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS Begin
DECLARE @Delimiter1 NVARCHAR(3)='[A]'
DECLARE @Delimiter2 NVARCHAR(3)='[T]'
DECLARE @StartIndex INT= 1; -- 子字符串起始索引
DECLARE @EndIndex INT; -- 子字符串结束索引
DECLARE @Substring NVARCHAR(MAX) -- 提取的子字符串
DECLARE @Result NVARCHAR(MAX)='' --结果
DECLARE @DoCount INT= 1 --循环次数
WHILE @StartIndex > 0
BEGIN
IF (@DoCount%2=1)--奇次
-- 找到下一个分隔符的位置
SET @EndIndex = CHARINDEX(@Delimiter1, @WhereStr, @StartIndex);--[A]到[T]止
ELSE SET @EndIndex = CHARINDEX(@Delimiter2, @WhereStr, @StartIndex);--[T]到[A]止
-- 如果未找到分隔符,则设置结束位置为字符串长度加一
IF (@EndIndex = 0)
SET @EndIndex = LEN(@WhereStr) + 1;
-- 提取当前子字符串
SET @Substring = SUBSTRING(
@WhereStr,
@StartIndex,
@EndIndex - @StartIndex
);
-- 更新起始索引至下一位置之后
SET @StartIndex = @EndIndex + LEN(@Delimiter2);
-- 将子字符串插入结果表中
IF(@EndIndex>LEN(@WhereStr))
BEGIN
SET @Result+= ' Id='+@Substring+' ) ';
BREAK;--中止循环
END
IF (@DoCount%2=1)--奇次--[A]到[T]止
SET @Result+= ' (SELECT Top 1 '+ @Substring +' as ROW, Name From TableA Where ';
ELSE --偶次--[T]到[A]止
SET @Result+= ' Id='+@Substring+' ) UNION ALL';
Set @DoCount+=1 --循环次数
END
RETURN @Result
END
GO
4、总结
问题主要在于前端拼接SQL后,传输的不安全(注入)、长字符不稳定(语句过长可能...)。
而逻辑放在SQL又过于难实现,毕竟逻辑确实是SQL的短板。
本次记录主要是split的挨个、循环截取,将11[A]''AA'' [T] 22[A]''BB'' [T] 33[A]''CC''
转换为 11 ''AA'' 22 'BB'' 33 ''CC'' ,并穿插入SQL的巧思。