Arrays SQL-从数组中获取特定元素
假设我有两个看起来像数组的变量:Arrays SQL-从数组中获取特定元素,arrays,sql-server,tsql,sql-server-2014,Arrays,Sql Server,Tsql,Sql Server 2014,假设我有两个看起来像数组的变量: 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(例如),如果包含,我需要找到相应的值(通过其索引)在@va
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
变量中,该变量将是数字的
,因为10490
是@code
数组的第四个元素,@value
数组的第四个元素是数字的
(请注意,@value
数组的第二个元素为空)
免责声明:
@code
数组将始终包含唯一的值。例如,不可能有超过1个10490
。
@code
数组始终以“,”开头和结尾。
如果从@code
变量中去掉第一个和最后一个逗号,则@code
和@value
中的元素数将始终相同。
我无法使用函数或存储过程,因此所有操作都需要作为1个查询的一部分来完成。这里有两种可能性。在您的情况下,我甚至会尝试在循环时将其合并为一个
SQL Server 2016及以上版本
(兼容级别130及以上)您可以使用内置函数STRING\u 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 Server:
当找不到@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,';
声明@value nvarchar(200)=
'True~~10000006~数字~0~0~~1388.76~已完成~True';
声明@tofind nvarchar(200)='10490';
--选择左侧(@code,CHARINDEX(@tofind,@code))
--选择len(左(@code,CHARINDEX(@tofind,@code)))-len(替换(左(@code,CHARINDEX(@tofind,@code)),',','')
声明@nth int;
设置@nth=len(左(@code,CHARINDEX(@tofind,@code)))-len(替换(左(@code,CHARINDEX(@tofind,@code)),',','')
声明@SplitOn nvarchar='~';
声明@rowdatanvarchar(200)=@value+'~';
声明@Cnt int=1
而(Charindex(@SplitOn,@RowData)>0)和@Cnt<@nth
开始
设置@RowData=Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
设置@Cnt=@Cnt+1
终点
选择--Data=ltrim(rtrim(@RowData)),
当ltrim(rtrim(@RowData))=''然后为null else时的情况
左(ltrim(rtrim(@RowData)),CHARINDEX(“~”,ltrim(rtrim(@RowData)))-1)
结果
我想你知道,这是一个非常糟糕的设计……如果你能改变这一点,你真的应该改变。但这是可以解决的:
declare@code-nvarchar(200)=
',10501,10203,10491,10490,10091,10253,10008,10020,10570,10499,';
声明@value nvarchar(200)=
'True~~10000006~数字~0~0~~1388.76~已完成~True';
--查询将两个字符串转换为可拆分的XML
--查询('/x[text()]')
将删除空条目(前导和尾随逗号)
--(…假设@code
中永远不会有空条目)
--然后它将从这两个列表中读取派生的编号列表
--最后,它将在其PartIndex
带铸造为
(
选择CAST(“”+REPLACE(@code,“,”)+“”)作为XML。查询(“/x[text()]”)作为CodeXml
,将(“”+替换(@value,“~”,“”)+”转换为XML)转换为ValueXml
)
,代码派生为
(
选择(ORDER BY(SELECT NULL))上方的行号()作为PartIndex
,x.value('text()[1]','nvarchar(max')作为代码部分
从铸造
交叉应用CodeXml.nodes('/x')A(x)
)
,值派生为
(
选择(ORDER BY(SELECT NULL))上方的行号()作为PartIndex
,x.value('text()[1]','nvarchar(max')作为ValuePart
从铸造
交叉应用ValueXml.nodes('/x')A(x)
)
选择cd.PartIndex
,代码部分
,ValuePart
从代码派生cd
内部联接值派生自cd上的vd。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
只需在其中添加一个简单的,
即可将其减少到所需的值
免责声明:不保证使用行编号和顺序的编号(选择空)
将返回正确的序列,但为了更好地使用SQL Server 2016+。这应该非常简单。如果性能很重要,我建议使用拆分字符串。下面是一个简单、高性能的解决方案:
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
好的,当我读到你们的数组时,10490是第5个。第1个是空的,第2个是10501等等。对不起,我在免责声明中提到第一个和最后一个逗号不应计入第1个数组。这是为哪个版本的SQL Server设计的?@Tony抱歉,错过了那个版本。SQL Server 2014这将起作用。Op只需为string_split.创建一个UDF,然后使用该指令2016年内置字符串分割的ead.OP提到,他不能使用函数或存储过程。抱歉..错过了这一点!注意:string\u split()
不保证返回正确的顺序。如果您在v2016+中需要位置安全拆分方法,最好使用OPENJSON
或@Pm Duda周围的各种函数之一……您的第二种方法奏效了。我只需要看看3个建议的工作版本中哪一个更有效,因为它需要经过大量的重新记录ds.谢谢!Hi Shungo,我不会说它的设计不好。它从来没有被设计成用SQL解析。它被设计成用C#解析,这很容易做到。这是一个审计表,你可能已经猜到了,通常这就是审计表的样子。尽管如此,你的解决方案很有效,我非常感谢你的帮助p!很抱歉没有给出完整的图片,但这需要在左连接的SELECT语句中完成。类似于:LEFT JOIN audit on audit.auditid=(我需要使用TOP 1和ORDER BY here查找特定记录)。我该怎么做呢?@Tony,把我的最后一个SELECT
放在另一个cte
中,并像使用任何其他表一样使用它……嗨@Shnugo,我不是一个很强的SQL开发人员,也从来没有使用过cte,所以所有这些对我来说都是新的。我尝试过,但没有尝试过
code item
---------- ------------
10490 Digital