如何在不同的字符串中找到特定的STRING位置的特定值

时间:2019-06-14 15:48:48

标签: sql tsql

我有这样的刺痛
,x,x,y,x,x,O,x,y
与此值匹配的其他字符串中的值
0〜1〜b ~~ z〜XY〜1〜7。
值“ O”可以切换其在字符串中的位置,其他字符串中的值也可以。
值“ O”位于第6位,因此预期结果将为“ XY”。
第一个字符串始终在每个值之前以“,”开头。第二个字符串首先以值开头,后跟“〜”。
“ O”是不会改变的给定值,因此我总是必须在第二个字符串中找到“ O”所在位置的给定值。

这就是我要介绍的内容:

,x,x,y,x,x,O,x,y
0〜1〜b ~~ z〜XY〜1〜7

O = XY


,x,O,y,x,x,y,x,y
0〜1〜b ~~ z〜XY〜1〜7

O = 1

谢谢。

2 个答案:

答案 0 :(得分:1)

获取DelimitedSplit8K的副本,然后您可以执行以下操作:

DECLARE @string1 VARCHAR(1000) = ',x,x,y,x,x,O,x,y',
        @string2 VARCHAR(1000) = '0~1~b~~z~XY~1~7';

DECLARE @search VARCHAR(1000) = 'O'; -- best as a variable/parameter

SELECT *
FROM dbo.delimitedSplit8K(@string2,'~') AS s
WHERE s.itemNumber = 
(
  SELECT TOP (1) s2.itemNumber -- TOP (1) until we know about dupicates
  FROM   dbo.delimitedSplit8K(@string1,',') AS s2
  WHERE  s2.item = @search
)-1;

返回:

ItemNumber           Item
-------------------- -------
6                    XY

答案 1 :(得分:1)

这是基于JSON的解决方案:

编辑:最快的方法(需要v2016)->删除了基于XQuery的解决方案

DECLARE @string1 VARCHAR(1000) = ',x,x,x,x,x,O,x,y',
        @string2 VARCHAR(1000) = '0~1~b~~z~XY~1~7';

SELECT s2.[value]
FROM OPENJSON('["' + REPLACE(@string1,',','","') + '"]') AS s1
CROSS APPLY OPENJSON('["' + REPLACE(@string2,'~','","') + '"]') AS s2 
WHERE s1.[value]='O' AND s2.[key]=s1.[key]-1;

简而言之:通过替换定界符,我们可以在JSON数组中转换定界的字符串,并使用JSON的功能来查找元素的位置,并使用此位置来获取另一个元素。

更新

我进行了一些性能测试,发现XQuery解决方案的性能很差。使用最多10个元素的小字符串是可以的,但是如果使用较长的字符串(使用100个元素进行测试),则执行起来会很慢...

测试代码(如果有兴趣的话):

DECLARE @i INT=1
       ,@CountElements INT=5 --Use higher values here
       ,@CountRows INT=500;

DECLARE @tbl TABLE(ID INT IDENTITY, i1t90 INT, s1 VARCHAR(500),s2 VARCHAR(500));

DECLARE @TemplateString VARCHAR(1000);
WHILE @i<@CountElements
BEGIN
    SET @TemplateString = CONCAT(@TemplateString,@i,'~');
    SET @i=@i+1;
END

SET @i=1;
WHILE @i<@CountRows
BEGIN
    INSERT INTO @tbl(i1t90, s1,s2) 
    SELECT
     ISNULL(NULLIF(@i%@CountElements,0),@CountElements)
    ,STUFF(REPLICATE(',x',@CountElements),(ISNULL(NULLIF(@i%@CountElements,0),@CountElements))*2,1,'O')
    ,CONCAT(@TemplateString,@i)

    SET @i=@i+1;
END

DECLARE @d DATETIME2=SYSUTCDATETIME();

SELECT t.*
      ,B.PosO
      ,A.y.value('(/y[sql:column("B.PosO")]/text())[1]','nvarchar(100)') AS FoundValue
INTO #t1
FROM @tbl t 
CROSS APPLY
(       
    SELECT (SELECT CAST('<x>' + REPLACE(s1,',','</x><x>') + '</x>' AS XML))
          ,(SELECT CAST('<y>' + REPLACE(s2,'~','</y><y>') + '</y>' AS XML))) AS A(x,y)
CROSS APPLY(SELECT A.x.value('count(/x[. << (/x[text()="O"])[1]])','int')) B(PosO);

SELECT 'XML based new', DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME());

SET @d=SYSUTCDATETIME();

SELECT *
INTO #t2
FROM @tbl
CROSS APPLY dbo.delimitedSplit8K(s2,'~') AS s
WHERE s.itemNumber = 
(
  SELECT TOP (1) s1.itemNumber -- TOP (1) until we know about dupicates
  FROM   dbo.delimitedSplit8K(s1,',') AS s1
  WHERE  s1.item = 'O'
)-1;

SELECT 'Splitter based',DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME());

SELECT * FROM #t1;
SELECT * FROM #t2;

DROP TABLE #t1;
DROP TABLE #t2;

在我的测试中,基于拆分器的方法快了大约八倍...

更新2:JSON分隔符(需要v2016 +)

此方法比基于拆分器的方法快约5倍:

SELECT t.*
      ,s2.[key] +1 AS PosO --zero based index
      ,s2.[value] AS FoundValue  
INTO #t3
FROM @tbl t
CROSS APPLY OPENJSON('["' + REPLACE(s1,',','","') + '"]') AS s1
CROSS APPLY OPENJSON('["' + REPLACE(s2,'~','","') + '"]') AS s2 
WHERE s1.[value]='O' AND s2.[key]=s1.[key]-1;