SQL-从数组中获取特定元素

时间:2018-08-13 20:19:06

标签: arrays sql-server tsql sql-server-2014

假设我有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(请注意{{1}的第2个元素}数组为NULL。

免责声明@value数组将始终包含唯一值。例如,@code不能超过1。 10490数组将始终以','开头和结尾。 如果您从@code变量中删除第一个和最后一个逗号,则@code@value中的元素数将始终相同。 我无法使用函数或存储过程,因此一切都需要作为1个查询的一部分来完成。

4 个答案:

答案 0 :(得分:1)

这里有两种可能。在您的情况下,我什至会尝试将其合并为一个WHILE循环。

SQL Server 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 Server:

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)

答案 1 :(得分:1)

当找不到@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

答案 2 :(得分:1)

我想您知道,这是一个非常糟糕的设计...如果您可以更改它,那么您应该这样做。但这可以解决:

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_NUMBERORDER BY (SELECT NULL)进行的编号将返回正确的顺序,但是更好的机会是您需要SQL Server 2016+。 For more details: read this link and the other contributions there

答案 3 :(得分:0)

这应该很简单。如果性能很重要,我建议使用DelimitedSplit8K分割字符串。这是一个简单的高性能解决方案:

np.savez('test.npz',format(list(a.keys())[0])=list(a.values())[0])

结果:

SyntaxError: keyword can't be an expression