SQL - 从数组中获取特定元素
SQL - Get specific element from an array
假设我有 2 个看起来像数组的变量:
declare @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
我需要查找 @code
是否包含 10490
(例如),如果包含,我需要在 @value
变量中找到相应的值(通过其索引)将是 Digital
,因为 10490
是 @code
数组中的第 4 个元素,而 @value
数组的第 4 个元素是 Digital
(注意 [= 的第二个元素14=] 数组为 NULL。
免责声明:
@code
数组将始终包含唯一值。例如,不可能有超过 1 个 10490
。
@code
数组总是以','开始和结束。
如果您从 @code
变量中去掉第一个和最后一个逗号,@code
和 @value
中的元素数量将始终相同。
我不能使用函数或存储过程,所以一切都需要作为 1 个查询的一部分完成。
这里有两种可能。在你的情况下,我什至会尝试将它合并到一个 WHILE
循环中。
SQL服务器2016及以上
(兼容级别 130 及以上)您可以使用内置函数 STRING_SPLIT
DECLARE @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
DECLARE @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
DECLARE @valuetosearch nvarchar(200) = '10490'
SELECT value FROM
(
SELECT value ,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'idx'
FROM STRING_SPLIT ( @value , '~' )
) AS x2
WHERE x2.idx =
(
SELECT idx-1 FROM
(
SELECT value ,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'idx'
FROM STRING_SPLIT ( @code , ',' )
) AS x1
WHERE x1.[value] = @valuetosearch
)
对于 SQL 服务器的早期版本:
DECLARE @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
DECLARE @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
DECLARE @valuetosearch nvarchar(200) = '10490'
DECLARE @codetbl AS TABLE (idx int IDENTITY(1,1)
,code nvarchar(200))
DECLARE @valuetbl AS TABLE (idx int IDENTITY(1,1)
,value nvarchar(200))
DECLARE @name nvarchar(200)
DECLARE @pos int
WHILE CHARINDEX(',', @code) > 0
BEGIN
SELECT @pos = CHARINDEX(',', @code)
SELECT @name = SUBSTRING(@code, 1, @pos-1)
INSERT INTO @codetbl
SELECT @name
SELECT @code = SUBSTRING(@code, @pos+1, LEN(@code)-@pos)
END
INSERT INTO @codetbl
SELECT @code
WHILE CHARINDEX('~', @value) > 0
BEGIN
SELECT @pos = CHARINDEX('~', @value)
SELECT @name = SUBSTRING(@value, 1, @pos-1)
INSERT INTO @valuetbl
SELECT @name
SELECT @value = SUBSTRING(@value, @pos+1, LEN(@value)-@pos)
END
INSERT INTO @valuetbl
SELECT @value
SELECT value FROM @valuetbl
WHERE idx = (SELECT idx-1 FROM @codetbl WHERE code = @valuetosearch)
当找不到@tofind 时,您可能需要添加一些代码
declare @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
declare @tofind nvarchar(200) = '10490';
--select left(@code,CHARINDEX(@tofind,@code))
--select len(left(@code,CHARINDEX(@tofind,@code))) - LEN( REPLACE( left(@code,CHARINDEX(@tofind,@code)) , ',', ''))
declare @nth int;
set @nth = len(left(@code,CHARINDEX(@tofind,@code))) - LEN( REPLACE( left(@code,CHARINDEX(@tofind,@code)) , ',', ''))
declare @SplitOn nvarchar = '~';
declare @RowData nvarchar(200) = @value + '~';
declare @Cnt int = 1
While (Charindex(@SplitOn,@RowData)>0) and @Cnt < @nth
Begin
Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
Set @Cnt = @Cnt + 1
End
Select --Data = ltrim(rtrim(@RowData)),
Case when ltrim(rtrim(@RowData)) = '' then null else
LEFT(ltrim(rtrim(@RowData)) , CHARINDEX('~',ltrim(rtrim(@RowData))) -1)
end as Result
我想你知道,这是一个非常糟糕的设计......如果你能改变它,你真的应该。但这可以解决:
declare @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
--查询会将两个字符串转换为可拆分的 XML
-- query('/x[text()]')
将删除空条目(前导和尾随逗号)
--(...假设 @code
中永远不会有空条目)
--然后它将从两个
中读取一个派生的编号列表
-- 最后它将加入两个列表的 PartIndex
WITH Casted AS
(
SELECT CAST('<x>' + REPLACE(@code,',','</x><x>') + '</x>' AS XML).query('/x[text()]') AS CodeXml
,CAST('<x>' + REPLACE(@value,'~','</x><x>') + '</x>' AS XML) AS ValueXml
)
,CodeDerived AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartIndex
,x.value('text()[1]','nvarchar(max)') AS CodePart
FROM Casted
CROSS APPLY CodeXml.nodes('/x') A(x)
)
,ValueDerived AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartIndex
,x.value('text()[1]','nvarchar(max)') AS ValuePart
FROM Casted
CROSS APPLY ValueXml.nodes('/x') A(x)
)
SELECT cd.PartIndex
,CodePart
,ValuePart
FROM CodeDerived cd
INNER JOIN ValueDerived vd ON cd.PartIndex=vd.PartIndex
结果
inx CodePart ValuePart
1 10501 True
2 10203 NULL
3 10491 100000006
4 10490 Digital
5 10091 0
6 10253 0
7 10008 NULL
8 10020 1388.76
9 10570 Completed
10 10499 True
只需添加一个简单的 WHERE
即可将其减少到您需要的值。
免责声明:不能保证 ROW_NUMBER
和 ORDER BY (SELECT NULL)
的编号将永远 return 正确的顺序,但为了更好的机会,您需要 SQL 服务器 2016+。
这应该很简单。如果性能很重要,我建议使用 DelimitedSplit8K 拆分字符串。这是一个简单的高性能解决方案:
DECLARE @searchFor INT = 10490;
SELECT code = s1.item, s2.item
FROM dbo.DelimitedSplit8K(@code,',') s1
JOIN dbo.DelimitedSplit8K(@value,'~') s2 ON s2.ItemNumber = s1.ItemNumber-1
WHERE s1.Item = @searchFor;
结果:
code item
---------- ------------
10490 Digital
假设我有 2 个看起来像数组的变量:
declare @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
我需要查找 @code
是否包含 10490
(例如),如果包含,我需要在 @value
变量中找到相应的值(通过其索引)将是 Digital
,因为 10490
是 @code
数组中的第 4 个元素,而 @value
数组的第 4 个元素是 Digital
(注意 [= 的第二个元素14=] 数组为 NULL。
免责声明:
@code
数组将始终包含唯一值。例如,不可能有超过 1 个 10490
。
@code
数组总是以','开始和结束。
如果您从 @code
变量中去掉第一个和最后一个逗号,@code
和 @value
中的元素数量将始终相同。
我不能使用函数或存储过程,所以一切都需要作为 1 个查询的一部分完成。
这里有两种可能。在你的情况下,我什至会尝试将它合并到一个 WHILE
循环中。
SQL服务器2016及以上
(兼容级别 130 及以上)您可以使用内置函数 STRING_SPLIT
DECLARE @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
DECLARE @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
DECLARE @valuetosearch nvarchar(200) = '10490'
SELECT value FROM
(
SELECT value ,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'idx'
FROM STRING_SPLIT ( @value , '~' )
) AS x2
WHERE x2.idx =
(
SELECT idx-1 FROM
(
SELECT value ,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS 'idx'
FROM STRING_SPLIT ( @code , ',' )
) AS x1
WHERE x1.[value] = @valuetosearch
)
对于 SQL 服务器的早期版本:
DECLARE @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
DECLARE @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
DECLARE @valuetosearch nvarchar(200) = '10490'
DECLARE @codetbl AS TABLE (idx int IDENTITY(1,1)
,code nvarchar(200))
DECLARE @valuetbl AS TABLE (idx int IDENTITY(1,1)
,value nvarchar(200))
DECLARE @name nvarchar(200)
DECLARE @pos int
WHILE CHARINDEX(',', @code) > 0
BEGIN
SELECT @pos = CHARINDEX(',', @code)
SELECT @name = SUBSTRING(@code, 1, @pos-1)
INSERT INTO @codetbl
SELECT @name
SELECT @code = SUBSTRING(@code, @pos+1, LEN(@code)-@pos)
END
INSERT INTO @codetbl
SELECT @code
WHILE CHARINDEX('~', @value) > 0
BEGIN
SELECT @pos = CHARINDEX('~', @value)
SELECT @name = SUBSTRING(@value, 1, @pos-1)
INSERT INTO @valuetbl
SELECT @name
SELECT @value = SUBSTRING(@value, @pos+1, LEN(@value)-@pos)
END
INSERT INTO @valuetbl
SELECT @value
SELECT value FROM @valuetbl
WHERE idx = (SELECT idx-1 FROM @codetbl WHERE code = @valuetosearch)
当找不到@tofind 时,您可能需要添加一些代码
declare @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
declare @tofind nvarchar(200) = '10490';
--select left(@code,CHARINDEX(@tofind,@code))
--select len(left(@code,CHARINDEX(@tofind,@code))) - LEN( REPLACE( left(@code,CHARINDEX(@tofind,@code)) , ',', ''))
declare @nth int;
set @nth = len(left(@code,CHARINDEX(@tofind,@code))) - LEN( REPLACE( left(@code,CHARINDEX(@tofind,@code)) , ',', ''))
declare @SplitOn nvarchar = '~';
declare @RowData nvarchar(200) = @value + '~';
declare @Cnt int = 1
While (Charindex(@SplitOn,@RowData)>0) and @Cnt < @nth
Begin
Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
Set @Cnt = @Cnt + 1
End
Select --Data = ltrim(rtrim(@RowData)),
Case when ltrim(rtrim(@RowData)) = '' then null else
LEFT(ltrim(rtrim(@RowData)) , CHARINDEX('~',ltrim(rtrim(@RowData))) -1)
end as Result
我想你知道,这是一个非常糟糕的设计......如果你能改变它,你真的应该。但这可以解决:
declare @code nvarchar(200) =
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
declare @value nvarchar(200) =
'True~~100000006~Digital~0~0~~1388.76~Completed~True';
--查询会将两个字符串转换为可拆分的 XML
-- query('/x[text()]')
将删除空条目(前导和尾随逗号)
--(...假设 @code
中永远不会有空条目)
--然后它将从两个
中读取一个派生的编号列表
-- 最后它将加入两个列表的 PartIndex
WITH Casted AS
(
SELECT CAST('<x>' + REPLACE(@code,',','</x><x>') + '</x>' AS XML).query('/x[text()]') AS CodeXml
,CAST('<x>' + REPLACE(@value,'~','</x><x>') + '</x>' AS XML) AS ValueXml
)
,CodeDerived AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartIndex
,x.value('text()[1]','nvarchar(max)') AS CodePart
FROM Casted
CROSS APPLY CodeXml.nodes('/x') A(x)
)
,ValueDerived AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartIndex
,x.value('text()[1]','nvarchar(max)') AS ValuePart
FROM Casted
CROSS APPLY ValueXml.nodes('/x') A(x)
)
SELECT cd.PartIndex
,CodePart
,ValuePart
FROM CodeDerived cd
INNER JOIN ValueDerived vd ON cd.PartIndex=vd.PartIndex
结果
inx CodePart ValuePart
1 10501 True
2 10203 NULL
3 10491 100000006
4 10490 Digital
5 10091 0
6 10253 0
7 10008 NULL
8 10020 1388.76
9 10570 Completed
10 10499 True
只需添加一个简单的 WHERE
即可将其减少到您需要的值。
免责声明:不能保证 ROW_NUMBER
和 ORDER BY (SELECT NULL)
的编号将永远 return 正确的顺序,但为了更好的机会,您需要 SQL 服务器 2016+。
这应该很简单。如果性能很重要,我建议使用 DelimitedSplit8K 拆分字符串。这是一个简单的高性能解决方案:
DECLARE @searchFor INT = 10490;
SELECT code = s1.item, s2.item
FROM dbo.DelimitedSplit8K(@code,',') s1
JOIN dbo.DelimitedSplit8K(@value,'~') s2 ON s2.ItemNumber = s1.ItemNumber-1
WHERE s1.Item = @searchFor;
结果:
code item
---------- ------------
10490 Digital