sql >> データベース >  >> RDS >> Sqlserver

SQLServer2016のSTRING_SPLITからの位置の戻り値

    -v2016以降-FROM OPENJSON()による解決策があります :

    DECLARE @str VARCHAR(100) = 'val1,val2,val3';
    
    SELECT *
    FROM OPENJSON('["' +  REPLACE(@str,',','","') + '"]');
    

    結果

    key value   type
    0   val1    1
    1   val2    1
    2   val3    1
    

    ドキュメントには明確に記載されています:

    OPENJSONがJSON配列を解析するとき、関数はJSONテキスト内の要素のインデックスをキーとして返します。

    あなたの場合、これは次のとおりです。

    SELECT 'z_y_x' AS splitIt
    INTO #split UNION
    SELECT 'a_b_c'
    
    DECLARE @delimiter CHAR(1)='_';
    
    SELECT * 
    FROM #split
    CROSS APPLY OPENJSON('["' +  REPLACE(splitIt,@delimiter,'","') + '"]') s
    WHERE s.[key]=1; --zero based
    

    STRING_SPLIT()の将来のバージョンを期待しましょう この情報が含まれます

    UPDATEパフォーマンステスト、人気のあるJeff-Moden-splitterと比較

    これを試してみてください:

    USE master;
    GO
    
    CREATE DATABASE dbTest;
    GO
    
    USE dbTest;
    GO
    --Jeff Moden's splitter
    CREATE FUNCTION [dbo].[DelimitedSplit8K](@pString VARCHAR(8000), @pDelimiter CHAR(1))
    RETURNS TABLE WITH SCHEMABINDING AS
     RETURN
      WITH E1(N) AS (
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                     SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                    ),                          --10E+1 or 10 rows
           E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
           E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
     cteTally(N) AS (
                     SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                    ),
    cteStart(N1) AS (
                     SELECT 1 UNION ALL
                     SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                    ),
    cteLen(N1,L1) AS(
                     SELECT s.N1,
                            ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                       FROM cteStart s
                    )
     SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
            Item       = SUBSTRING(@pString, l.N1, l.L1)
       FROM cteLen l
    ;
    GO
    --Avoid first call bias
    SELECT * FROM dbo.DelimitedSplit8K('a,b,c',',');
    GO  
    
    --Table to keep the results
    CREATE TABLE Results(ID INT IDENTITY,ResultSource VARCHAR(100),durationMS INT, RowsCount INT);
    GO
    --Table with strings to split
    CREATE TABLE dbo.DelimitedItems(ID INT IDENTITY,DelimitedNString nvarchar(4000),DelimitedString varchar(8000));
    GO
    

    -100アイテムのランダムに混合された文字列を使用して行を取得します
    -行の数(GOの後ろの数)とTOPを使用した数で遊んでみてください

    INSERT INTO DelimitedItems(DelimitedNString)
    SELECT STUFF((
                SELECT TOP 100 ','+REPLACE(v.[name],',',';') 
                FROM master..spt_values v
                WHERE LEN(v.[name])>0
                ORDER BY NewID()
                FOR XML PATH('')),1,1,'')
    --Keep it twice in varchar and nvarchar
    UPDATE DelimitedItems SET DelimitedString=DelimitedNString;
    GO 500 --create 500 differently mixed rows
    

    -テスト

    DECLARE @d DATETIME2;
    
    SET @d = SYSUTCDATETIME();
        SELECT DI.ID, DS.Item, DS.ItemNumber
        INTO #TEMP
        FROM dbo.DelimitedItems DI
             CROSS APPLY dbo.DelimitedSplit8K(DI.DelimitedNString,',') DS;
    INSERT INTO Results(ResultSource,RowsCount,durationMS)
    SELECT 'delimited8K with NVARCHAR(4000)'
          ,(SELECT COUNT(*) FROM #TEMP) AS RowCountInTemp
          ,DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME()) AS Duration_NV_ms_delimitedSplit8K
    
    SET @d = SYSUTCDATETIME();
        SELECT DI.ID, DS.Item, DS.ItemNumber
        INTO #TEMP2
        FROM dbo.DelimitedItems DI
             CROSS APPLY dbo.DelimitedSplit8K(DI.DelimitedString,',') DS;
    INSERT INTO Results(ResultSource,RowsCount,durationMS)
    SELECT 'delimited8K with VARCHAR(8000)'
          ,(SELECT COUNT(*) FROM #TEMP2) AS RowCountInTemp
          ,DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME()) AS Duration_V_ms_delimitedSplit8K
    
    SET @d = SYSUTCDATETIME();
        SELECT DI.ID, OJ.[Value] AS Item, OJ.[Key] AS ItemNumber
        INTO #TEMP3
        FROM dbo.DelimitedItems DI
             CROSS APPLY OPENJSON('["' +  REPLACE(DI.DelimitedNString,',','","') + '"]') OJ;
    INSERT INTO Results(ResultSource,RowsCount,durationMS)
    SELECT 'OPENJSON with NVARCHAR(4000)'
          ,(SELECT COUNT(*) FROM #TEMP3) AS RowCountInTemp
          ,DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME()) AS Duration_NV_ms_OPENJSON
    
    SET @d = SYSUTCDATETIME();
        SELECT DI.ID, OJ.[Value] AS Item, OJ.[Key] AS ItemNumber
        INTO #TEMP4
        FROM dbo.DelimitedItems DI
             CROSS APPLY OPENJSON('["' +  REPLACE(DI.DelimitedString,',','","') + '"]') OJ;
    INSERT INTO Results(ResultSource,RowsCount,durationMS)
    SELECT 'OPENJSON with VARCHAR(8000)'
          ,(SELECT COUNT(*) FROM #TEMP4) AS RowCountInTemp
          ,DATEDIFF(MILLISECOND,@d,SYSUTCDATETIME()) AS Duration_V_ms_OPENJSON
    GO
    SELECT * FROM Results;
    GO
    

    -クリーンアップ

    DROP TABLE #TEMP;
    DROP TABLE #TEMP2;
    DROP TABLE #TEMP3;
    DROP TABLE #TEMP4;
    
    USE master;
    GO
    DROP DATABASE dbTest;
    

    結果:

    500行200アイテム

    1220    delimited8K with NVARCHAR(4000)
     274    delimited8K with VARCHAR(8000)
     417    OPENJSON with NVARCHAR(4000)
     443    OPENJSON with VARCHAR(8000)
    

    500行に100個のアイテム

    421 delimited8K with NVARCHAR(4000)
    140 delimited8K with VARCHAR(8000)
    213 OPENJSON with NVARCHAR(4000)
    212 OPENJSON with VARCHAR(8000)
    

    5行で100アイテム

    10  delimited8K with NVARCHAR(4000)
    5   delimited8K with VARCHAR(8000)
    3   OPENJSON with NVARCHAR(4000)
    4   OPENJSON with VARCHAR(8000)
    

    500行に5つのアイテム

    32  delimited8K with NVARCHAR(4000)
    30  delimited8K with VARCHAR(8000)
    28  OPENJSON with NVARCHAR(4000)
    24  OPENJSON with VARCHAR(8000)
    

    -無制限の長さ(OPENJSONでのみ可能) )-入力中にTOP句がない場合
    -500行に約500個のアイテムが表示されます

    1329    OPENJSON with NVARCHAR(4000)
    1117    OPENJSON with VARCHAR(8000)
    

    ファシット:

    • 人気のあるスプリッター関数はNVARCHARが好きではありません
    • この関数は、8kバイトのボリューム内の文字列に制限されています
    • VARCHARに多数の項目と多数の行がある場合のみ スプリッター機能を先に進めましょう。
    • その他すべての場合OPENJSON 多かれ少なかれ速いようです...
    • OPENJSON (ほぼ)無制限のカウントを処理できます
    • OPENJSON v2016の需要
    • 誰もがSTRING_SPLITを待っています 位置で

    UPDATEテストにSTRING_SPLITを追加しました

    その間に、STRING_SPLIT()を使用して、さらに2つのテストセクションでテストを再実行します。 。この関数はパーツのインデックスを返さないため、位置としてハードコードされた値を返す必要がありました。

    テストされたすべてのケースでOPENJSON STRING_SPLITに近かった 多くの場合、より高速です:

    1000行に5つのアイテム

    250 delimited8K with NVARCHAR(4000)
    124 delimited8K with VARCHAR(8000) --this function is best with many rows in VARCHAR
    203 OPENJSON with NVARCHAR(4000)
    204 OPENJSON with VARCHAR(8000)
    235 STRING_SPLIT with NVARCHAR(4000)
    234 STRING_SPLIT with VARCHAR(8000)
    

    30行で200アイテム

    140 delimited8K with NVARCHAR(4000)
    31  delimited8K with VARCHAR(8000)
    47  OPENJSON with NVARCHAR(4000)
    31  OPENJSON with VARCHAR(8000)
    47  STRING_SPLIT with NVARCHAR(4000)
    31  STRING_SPLIT with VARCHAR(8000)
    

    10.000行に100個のアイテム

    8145    delimited8K with NVARCHAR(4000)
    2806    delimited8K with VARCHAR(8000) --fast with many rows!
    5112    OPENJSON with NVARCHAR(4000)
    4501    OPENJSON with VARCHAR(8000)
    5028    STRING_SPLIT with NVARCHAR(4000)
    5126    STRING_SPLIT with VARCHAR(8000)
    


    1. MacにSQLServerをインストールする方法

    2. ActiveRecord_Associations_CollectionProxyのRails未定義メソッド

    3. 日付範囲の重複チェック制約

    4. MariaDBでLEAST()がどのように機能するか