515

SQL Serverを使用して、アイテムxにアクセスできるように文字列を分割するにはどうすればよいですか?

文字列「HelloJohnSmith」を取ります。文字列をスペースで分割し、「John」を返す必要があるインデックス1のアイテムにアクセスするにはどうすればよいですか?

4

46 に答える 46

365

SQL Serverに組み込みの分割関数があるとは思わないので、UDFを除いて、私が知っている他の唯一の答えは、PARSENAME関数をハイジャックすることです。

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2) 

PARSENAMEは文字列を受け取り、ピリオド文字で分割します。2番目の引数として数値を取り、その数値は、文字列のどのセグメントを返すかを指定します(後ろから前に向かって動作します)。

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3)  --return Hello

明らかな問題は、文字列にすでにピリオドが含まれている場合です。私はまだUDFを使用することが最善の方法だと思います...他の提案はありますか?

于 2008-08-05T18:45:19.117 に答える
193

区切り文字列を解析するSQLユーザー定義関数の解決策が役立つ場合があります(Code Projectから)。

この単純なロジックを使用できます。

Declare @products varchar(200) = '1|20|3|343|44|6|8765'
Declare @individual varchar(20) = null

WHILE LEN(@products) > 0
BEGIN
    IF PATINDEX('%|%', @products) > 0
    BEGIN
        SET @individual = SUBSTRING(@products,
                                    0,
                                    PATINDEX('%|%', @products))
        SELECT @individual

        SET @products = SUBSTRING(@products,
                                  LEN(@individual + '|') + 1,
                                  LEN(@products))
    END
    ELSE
    BEGIN
        SET @individual = @products
        SET @products = NULL
        SELECT @individual
    END
END
于 2008-08-05T18:28:38.653 に答える
110

まず、関数を作成します(CTEを使用すると、共通テーブル式により一時テーブルが不要になります)

 create function dbo.SplitString 
    (
        @str nvarchar(4000), 
        @separator char(1)
    )
    returns table
    AS
    return (
        with tokens(p, a, b) AS (
            select 
                1, 
                1, 
                charindex(@separator, @str)
            union all
            select
                p + 1, 
                b + 1, 
                charindex(@separator, @str, b + 1)
            from tokens
            where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
      )
    GO

次に、このように任意のテーブルとして使用します(または、既存のストアドプロシージャ内に収まるように変更します)。

select s 
from dbo.SplitString('Hello John Smith', ' ')
where zeroBasedOccurance=1

アップデート

以前のバージョンでは、4000文字を超える入力文字列では失敗していました。このバージョンは制限を処理します:

create function dbo.SplitString 
(
    @str nvarchar(max), 
    @separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
    select 
        cast(1 as bigint), 
        cast(1 as bigint), 
        charindex(@separator, @str)
    union all
    select
        p + 1, 
        b + 1, 
        charindex(@separator, @str, b + 1)
    from tokens
    where b > 0
)
select
    p-1 ItemIndex,
    substring(
        @str, 
        a, 
        case when b > 0 then b-a ELSE LEN(@str) end) 
    AS s
from tokens
);

GO

使用法は同じままです。

于 2008-08-05T18:57:03.137 に答える
70

ここでのソリューションのほとんどは、while ループまたは再帰 CTE を使用しています。スペース以外の区切り文字を使用できる場合は、セットベースのアプローチが優れていると約束します。

CREATE FUNCTION [dbo].[SplitString]
    (
        @List NVARCHAR(MAX),
        @Delim VARCHAR(255)
    )
    RETURNS TABLE
    AS
        RETURN ( SELECT [Value], idx = RANK() OVER (ORDER BY n) FROM 
          ( 
            SELECT n = Number, 
              [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
              CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
            FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
              FROM sys.all_objects) AS x
              WHERE Number <= LEN(@List)
              AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim
          ) AS y
        );

使用例:

SELECT Value FROM dbo.SplitString('foo,bar,blat,foo,splunge',',')
  WHERE idx = 3;

結果:

----
blat

必要な を引数として関数に追加することもできidxますが、それは読者の課題として残しておきます。

出力が元のリストの順序でレンダリングされるという保証がないため、SQL Server 2016 に追加されたネイティブ関数だけでこれを行うことはできません。つまり、結果を渡すと、その順序になる可能性があります、. ここで組み込み関数を改善するためにコミュニティの助けを求めました:STRING_SPLIT3,6,11,3,6

十分な定性的なフィードバックがあれば、実際にこれらの機能強化のいくつかを行うことを検討するかもしれません:

分割関数の詳細、while ループと再帰 CTE がスケーリングしない理由 (およびその証明)、およびアプリケーション層からの文字列を分割する場合のより良い代替手段:

ただし、SQL Server 2016 以降では、以下を確認する必要がありSTRING_SPLIT()ますSTRING_AGG()

于 2013-11-12T17:16:08.703 に答える
38

Number テーブルを利用して、文字列の解析を行うことができます。

物理数値テーブルを作成します。

    create table dbo.Numbers (N int primary key);
    insert into dbo.Numbers
        select top 1000 row_number() over(order by number) from master..spt_values
    go

1000000 行のテスト テーブルを作成する

    create table #yak (i int identity(1,1) primary key, array varchar(50))

    insert into #yak(array)
        select 'a,b,c' from dbo.Numbers n cross join dbo.Numbers nn
    go

関数を作成する

    create function [dbo].[ufn_ParseArray]
        (   @Input      nvarchar(4000), 
            @Delimiter  char(1) = ',',
            @BaseIdent  int
        )
    returns table as
    return  
        (   select  row_number() over (order by n asc) + (@BaseIdent - 1) [i],
                    substring(@Input, n, charindex(@Delimiter, @Input + @Delimiter, n) - n) s
            from    dbo.Numbers
            where   n <= convert(int, len(@Input)) and
                    substring(@Delimiter + @Input, n, 1) = @Delimiter
        )
    go

使用法 (私のラップトップでは 40 秒で 300 万行を出力します)

    select * 
    from #yak 
    cross apply dbo.ufn_ParseArray(array, ',', 1)

掃除

    drop table dbo.Numbers;
    drop function  [dbo].[ufn_ParseArray]

ここでのパフォーマンスは驚くべきものではありませんが、100 万行のテーブルを超える関数を呼び出すことは最善の考えではありません。多くの行に分割された文字列を実行する場合、私は関数を避けます。

于 2008-10-27T16:48:51.943 に答える
37

この質問は、文字列の分割方法に関するものではなく、n 番目の要素を取得する方法に関するものです。

CTEここでのすべての回答は、再帰、 s 、 multiple CHARINDEXREVERSEおよびPATINDEX、発明関数、CLR メソッドの呼び出し、数値テーブル、 sを使用して、何らかの文字列分割を行っていCROSS APPLYます...ほとんどの回答は、多くのコード行をカバーしています。

しかし、n番目の要素を取得するためのアプローチだけが本当に必要な場合、これは実際のワンライナー、UDFなし、サブセレクトでさえも行うことができます...そして、追加の利点として:タイプセーフ

スペースで区切られたパート 2 を取得します。

DECLARE @input NVARCHAR(100)=N'part1 part2 part3';
SELECT CAST(N'<x>' + REPLACE(@input,N' ',N'</x><x>') + N'</x>' AS XML).value('/x[2]','nvarchar(max)')

もちろん、区切り文字と位置に変数を使用できますsql:column(クエリの値から直接位置を取得するために使用します)。

DECLARE @dlmt NVARCHAR(10)=N' ';
DECLARE @pos INT = 2;
SELECT CAST(N'<x>' + REPLACE(@input,@dlmt,N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)')

文字列に禁止文字(特に の 1 つ&><) が含まれる可能性がある場合でも、この方法で行うことができます。最初に文字列で使用FOR XML PATHして、禁止されているすべての文字を適切なエスケープシーケンスに暗黙的に置き換えます。

さらに、区切り文字がセミコロンである場合は、非常に特殊なケースです。この場合、最初に区切り文字を「#DLMT#」に置き換え、最後にこれを XML タグに置き換えます。

SET @input=N'Some <, > and &;Other äöü@€;One more';
SET @dlmt=N';';
SELECT CAST(N'<x>' + REPLACE((SELECT REPLACE(@input,@dlmt,'#DLMT#') AS [*] FOR XML PATH('')),N'#DLMT#',N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)');

SQL Server 2016+ の更新

残念ながら、開発者は でパーツのインデックスを返すのを忘れていましたSTRING_SPLIT。しかし、SQL-Server 2016+ を使用するとJSON_VALUEOPENJSON.

JSON_VALUEインデックスの配列として位置を渡すことができます。

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

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

のような文字列に1,2,3は、角かっこ以外は何も必要ありません: [1,2,3].
のような単語の文字列は、 であるthis is an example必要があります["this","is","an","example"]
これらは非常に簡単な文字列操作です。試してみてください:

DECLARE @str VARCHAR(100)='Hello John Smith';
DECLARE @position INT = 2;

--We can build the json-path '$[1]' using CONCAT
SELECT JSON_VALUE('["' + REPLACE(@str,' ','","') + '"]',CONCAT('$[',@position-1,']'));

--位置セーフな文字列スプリッター (ゼロベース) については、これを参照してください。

SELECT  JsonArray.[key] AS [Position]
       ,JsonArray.[value] AS [Part]
FROM OPENJSON('["' + REPLACE(@str,' ','","') + '"]') JsonArray

この投稿では、さまざまなアプローチをテストし、それOPENJSONが非常に高速であることを発見しました。有名な「delimitedSplit8k()」メソッドよりもはるかに高速です...

UPDATE 2 - タイプセーフな値を取得する

doubled を使用するだけで、配列内で配列[[]]を使用できます。これにより、型付きWITH-clauseが可能になります。

DECLARE  @SomeDelimitedString VARCHAR(100)='part1|1|20190920';

DECLARE @JsonArray NVARCHAR(MAX)=CONCAT('[["',REPLACE(@SomeDelimitedString,'|','","'),'"]]');

SELECT @SomeDelimitedString          AS TheOriginal
      ,@JsonArray                    AS TransformedToJSON
      ,ValuesFromTheArray.*
FROM OPENJSON(@JsonArray)
WITH(TheFirstFragment  VARCHAR(100) '$[0]'
    ,TheSecondFragment INT          '$[1]'
    ,TheThirdFragment  DATE         '$[2]') ValuesFromTheArray
于 2016-07-08T20:41:19.780 に答える
22

これがそれを行うUDFです。区切られた値のテーブルが返されます。すべてのシナリオを試したわけではありませんが、例は正常に機能します。


CREATE FUNCTION SplitString 
(
    -- Add the parameters for the function here
    @myString varchar(500),
    @deliminator varchar(10)
)
RETURNS 
@ReturnTable TABLE 
(
    -- Add the column definitions for the TABLE variable here
    [id] [int] IDENTITY(1,1) NOT NULL,
    [part] [varchar](50) NULL
)
AS
BEGIN
        Declare @iSpaces int
        Declare @part varchar(50)

        --initialize spaces
        Select @iSpaces = charindex(@deliminator,@myString,0)
        While @iSpaces > 0

        Begin
            Select @part = substring(@myString,0,charindex(@deliminator,@myString,0))

            Insert Into @ReturnTable(part)
            Select @part

    Select @myString = substring(@mystring,charindex(@deliminator,@myString,0)+ len(@deliminator),len(@myString) - charindex(' ',@myString,0))


            Select @iSpaces = charindex(@deliminator,@myString,0)
        end

        If len(@myString) > 0
            Insert Into @ReturnTable
            Select @myString

    RETURN 
END
GO

あなたはそれをこのように呼ぶでしょう:


Select * From SplitString('Hello John Smith',' ')

編集:次のようにlen>1のデリマーを処理するようにソリューションを更新しました:


select * From SplitString('Hello**John**Smith','**')
于 2008-08-05T18:39:58.007 に答える
16

ここに私は解決の簡単な方法を投稿します

CREATE FUNCTION [dbo].[split](
          @delimited NVARCHAR(MAX),
          @delimiter NVARCHAR(100)
        ) RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
        AS
        BEGIN
          DECLARE @xml XML
          SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'

          INSERT INTO @t(val)
          SELECT  r.value('.','varchar(MAX)') as item
          FROM  @xml.nodes('/t') as records(r)
          RETURN
        END


このような機能を実行します

  select * from dbo.split('Hello John Smith',' ')
于 2013-01-30T09:41:17.203 に答える
10

私の意見では、皆さんはそれをあまりにも複雑にしています。CLR UDFを作成して、それで完了します。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;

public partial class UserDefinedFunctions {
  [SqlFunction]
  public static SqlString SearchString(string Search) {
    List<string> SearchWords = new List<string>();
    foreach (string s in Search.Split(new char[] { ' ' })) {
      if (!s.ToLower().Equals("or") && !s.ToLower().Equals("and")) {
        SearchWords.Add(s);
      }
    }

    return new SqlString(string.Join(" OR ", SearchWords.ToArray()));
  }
};
于 2012-07-19T21:46:38.117 に答える
10

stringandvalues()ステートメントの使用はどうですか?

DECLARE @str varchar(max)
SET @str = 'Hello John Smith'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited TABLE(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, '''),(''')
SET @str = 'SELECT * FROM (VALUES(''' + @str + ''')) AS V(A)' 

INSERT INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

結果セットが達成されました。

id  item
1   Hello
2   John
3   Smith
于 2013-03-01T16:26:59.797 に答える
9

私はフレデリックの答えを使用しますが、これは SQL Server 2005 では機能しませんでした

私はそれを変更し、使用selectunion allていますが、動作します

DECLARE @str varchar(max)
SET @str = 'Hello John Smith how are you'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited table(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, ''' UNION ALL SELECT ''')
SET @str = ' SELECT  ''' + @str + '''  ' 

INSERT INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

結果セットは次のとおりです。

id  item
1   Hello
2   John
3   Smith
4   how
5   are
6   you
于 2013-08-13T15:11:27.963 に答える
8

このパターンは正常に機能し、一般化できます

Convert(xml,'<n>'+Replace(FIELD,'.','</n><n>')+'</n>').value('(/n[INDEX])','TYPE')
                          ^^^^^                                   ^^^^^     ^^^^

FIELDINDEX、およびTYPEに注意してください。

次のような識別子を持ついくつかのテーブルを見てみましょう

sys.message.1234.warning.A45
sys.message.1235.error.O98
....

次に、書くことができます

SELECT Source         = q.value('(/n[1])', 'varchar(10)'),
       RecordType     = q.value('(/n[2])', 'varchar(20)'),
       RecordNumber   = q.value('(/n[3])', 'int'),
       Status         = q.value('(/n[4])', 'varchar(5)')
FROM   (
         SELECT   q = Convert(xml,'<n>'+Replace(fieldName,'.','</n><n>')+'</n>')
         FROM     some_TABLE
       ) Q

すべての部品を分割して鋳造します。

于 2014-11-11T14:31:37.597 に答える
6

ネットで解決策を探していましたが、以下がうまくいきます。 参照

そして、次のように関数を呼び出します:

SELECT * FROM dbo.split('ram shyam hari gopal',' ')

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[Split](@String VARCHAR(8000), @Delimiter CHAR(1))       
RETURNS @temptable TABLE (items VARCHAR(8000))       
AS       
BEGIN       
    DECLARE @idx INT       
    DECLARE @slice VARCHAR(8000)        
    SELECT @idx = 1       
    IF len(@String)<1 OR @String IS NULL  RETURN       
    WHILE @idx!= 0       
    BEGIN       
        SET @idx = charindex(@Delimiter,@String)       
        IF @idx!=0       
            SET @slice = LEFT(@String,@idx - 1)       
        ELSE       
            SET @slice = @String       
        IF(len(@slice)>0)  
            INSERT INTO @temptable(Items) VALUES(@slice)       
        SET @String = RIGHT(@String,len(@String) - @idx)       
        IF len(@String) = 0 break       
    END   
    RETURN       
END
于 2011-11-20T06:40:26.403 に答える
5

これを試して:

CREATE function [SplitWordList]
(
 @list varchar(8000)
)
returns @t table 
(
 Word varchar(50) not null,
 Position int identity(1,1) not null
)
as begin
  declare 
    @pos int,
    @lpos int,
    @item varchar(100),
    @ignore varchar(100),
    @dl int,
    @a1 int,
    @a2 int,
    @z1 int,
    @z2 int,
    @n1 int,
    @n2 int,
    @c varchar(1),
    @a smallint
  select 
    @a1 = ascii('a'),
    @a2 = ascii('A'),
    @z1 = ascii('z'),
    @z2 = ascii('Z'),
    @n1 = ascii('0'),
    @n2 = ascii('9')
  set @ignore = '''"'
  set @pos = 1
  set @dl = datalength(@list)
  set @lpos = 1
  set @item = ''
  while (@pos <= @dl) begin
    set @c = substring(@list, @pos, 1)
    if (@ignore not like '%' + @c + '%') begin
      set @a = ascii(@c)
      if ((@a >= @a1) and (@a <= @z1))  
        or ((@a >= @a2) and (@a <= @z2))
        or ((@a >= @n1) and (@a <= @n2))
      begin
        set @item = @item + @c
      end else if (@item > '') begin
        insert into @t values (@item)
        set @item = ''
      end
    end 
    set @pos = @pos + 1
  end
  if (@item > '') begin
    insert into @t values (@item)
  end
  return
end

次のようにテストします。

select * from SplitWordList('Hello John Smith')
于 2008-08-05T18:41:34.367 に答える
5

次の例では、再帰的な CTE を使用しています

更新18.09.2013

CREATE FUNCTION dbo.SplitStrings_CTE(@List nvarchar(max), @Delimiter nvarchar(1))
RETURNS @returns TABLE (val nvarchar(max), [level] int, PRIMARY KEY CLUSTERED([level]))
AS
BEGIN
;WITH cte AS
 (
  SELECT SUBSTRING(@List, 0, CHARINDEX(@Delimiter,  @List + @Delimiter)) AS val,
         CAST(STUFF(@List + @Delimiter, 1, CHARINDEX(@Delimiter, @List + @Delimiter), '') AS nvarchar(max)) AS stval, 
         1 AS [level]
  UNION ALL
  SELECT SUBSTRING(stval, 0, CHARINDEX(@Delimiter, stval)),
         CAST(STUFF(stval, 1, CHARINDEX(@Delimiter, stval), '') AS nvarchar(max)),
         [level] + 1
  FROM cte
  WHERE stval != ''
  )
  INSERT @returns
  SELECT REPLACE(val, ' ','' ) AS val, [level]
  FROM cte
  WHERE val > ''
  RETURN
END

SQLFiddle のデモ

于 2013-03-14T10:18:50.557 に答える
2

関数を必要とせずに SQL で文字列を分割できます。

DECLARE @bla varchar(MAX)
SET @bla = 'BED40DFC-F468-46DD-8017-00EF2FA3E4A4,64B59FC5-3F4D-4B0E-9A48-01F3D4F220B0,A611A108-97CA-42F3-A2E1-057165339719,E72D95EA-578F-45FC-88E5-075F66FD726C'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'varchar(36)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE(@bla, ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol);

任意の文字列をサポートする必要がある場合 (xml 特殊文字を使用)

DECLARE @bla NVARCHAR(MAX)
SET @bla = '<html>unsafe & safe Utf8CharsDon''tGetEncoded ÄöÜ - "Conex"<html>,Barnes & Noble,abc,def,ghi'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'nvarchar(MAX)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE((SELECT @bla FOR XML PATH('')), ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol); 
于 2015-10-23T10:07:55.507 に答える
1

私はそれが古い質問であることを知っています、しかし私は誰かが私の解決策から利益を得ることができると思います。

select 
SUBSTRING(column_name,1,CHARINDEX(' ',column_name,1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,1
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)+1
    ,LEN(column_name))
from table_name

SQLフィドル

利点:

  • 3つのサブストリングデリミネーターすべてを''で区切ります。
  • パフォーマンスが低下するため、whileループを使用しないでください。
  • 結果のすべてのサブ文字列が1つの行に表示されるため、ピボットする必要はありません。

制限:

  • 総数を知る必要があります。スペースの(部分文字列)。

:このソリューションでは、最大N個のサブ文字列を指定できます。

制限を克服するために、次の参照を使用できます。

ただし、上記のソリューションはテーブルでは使用できません(実際には使用できませんでした)。

繰り返しますが、このソリューションが誰かを助けることができることを願っています。

更新:レコードが50000を超える場合は、パフォーマンスが低下するため、使用しないことをお勧めします。LOOPS

于 2013-01-24T06:43:52.693 に答える
1

他のほとんどすべての回答は、分割されている文字列を置き換えているため、CPU サイクルが無駄になり、不要なメモリ割り当てが実行されます。

ここで文字列分割を行うためのより良い方法について説明します: http://www.digitalruby.com/split-string-sql-server/

コードは次のとおりです。

SET NOCOUNT ON

-- You will want to change nvarchar(MAX) to nvarchar(50), varchar(50) or whatever matches exactly with the string column you will be searching against
DECLARE @SplitStringTable TABLE (Value nvarchar(MAX) NOT NULL)
DECLARE @StringToSplit nvarchar(MAX) = 'your|string|to|split|here'
DECLARE @SplitEndPos int
DECLARE @SplitValue nvarchar(MAX)
DECLARE @SplitDelim nvarchar(1) = '|'
DECLARE @SplitStartPos int = 1

SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)

WHILE @SplitEndPos > 0
BEGIN
    SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, (@SplitEndPos - @SplitStartPos))
    INSERT @SplitStringTable (Value) VALUES (@SplitValue)
    SET @SplitStartPos = @SplitEndPos + 1
    SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)
END

SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, 2147483647)
INSERT @SplitStringTable (Value) VALUES(@SplitValue)

SET NOCOUNT OFF

-- You can select or join with the values in @SplitStringTable at this point.
于 2014-08-26T16:50:04.223 に答える
0

josejuanによるxmlベースの回答に似ていますが、xmlパスを1回だけ処理すると、ピボットが適度に効率的であることがわかりました。

select ID,
    [3] as PathProvidingID,
    [4] as PathProvider,
    [5] as ComponentProvidingID,
    [6] as ComponentProviding,
    [7] as InputRecievingID,
    [8] as InputRecieving,
    [9] as RowsPassed,
    [10] as InputRecieving2
    from
    (
    select id,message,d.* from sysssislog cross apply       ( 
          SELECT Item = y.i.value('(./text())[1]', 'varchar(200)'),
              row_number() over(order by y.i) as rn
          FROM 
          ( 
             SELECT x = CONVERT(XML, '<i>' + REPLACE(Message, ':', '</i><i>') + '</i>').query('.')
          ) AS a CROSS APPLY x.nodes('i') AS y(i)
       ) d
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as tokens 
    pivot 
    ( max(item) for [rn] in ([3],[4],[5],[6],[7],[8],[9],[10]) 
    ) as data

8:30に走りました

select id,
tokens.value('(/n[3])', 'varchar(100)')as PathProvidingID,
tokens.value('(/n[4])', 'varchar(100)') as PathProvider,
tokens.value('(/n[5])', 'varchar(100)') as ComponentProvidingID,
tokens.value('(/n[6])', 'varchar(100)') as ComponentProviding,
tokens.value('(/n[7])', 'varchar(100)') as InputRecievingID,
tokens.value('(/n[8])', 'varchar(100)') as InputRecieving,
tokens.value('(/n[9])', 'varchar(100)') as RowsPassed
 from
(
    select id, Convert(xml,'<n>'+Replace(message,'.','</n><n>')+'</n>') tokens
         from sysssislog 
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as data

9:20に走りました

于 2014-12-08T03:59:24.633 に答える
0

誰かが分離されたテキストの一部だけを取得したい場合は、これを使用できます

select * fromSplitStringSep('Word1 wordr2 word3',' ')

CREATE function [dbo].[SplitStringSep] 
(
    @str nvarchar(4000), 
    @separator char(1)
)
returns table
AS
return (
    with tokens(p, a, b) AS (
        select 
        1, 
        1, 
        charindex(@separator, @str)
        union all
        select
            p + 1, 
            b + 1, 
            charindex(@separator, @str, b + 1)
        from tokens
        where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
  )
于 2015-02-13T09:14:22.343 に答える
0
CREATE FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1

        INSERT INTO @output (splitdata)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)

    END 
    RETURN 
END

そしてそれを使う

select *from dbo.fnSplitString('Querying SQL Server','')
于 2014-12-20T11:58:36.940 に答える
0

サーバーの痛みを伴う再帰CTEソリューション、テスト

MS SQL Server 2008 スキーマのセットアップ:

create table Course( Courses varchar(100) );
insert into Course values ('Hello John Smith');

クエリ 1 :

with cte as
   ( select 
        left( Courses, charindex( ' ' , Courses) ) as a_l,
        cast( substring( Courses, 
                         charindex( ' ' , Courses) + 1 , 
                         len(Courses ) ) + ' ' 
              as varchar(100) )  as a_r,
        Courses as a,
        0 as n
     from Course t
    union all
      select 
        left(a_r, charindex( ' ' , a_r) ) as a_l,
        substring( a_r, charindex( ' ' , a_r) + 1 , len(a_R ) ) as a_r,
        cte.a,
        cte.n + 1 as n
    from Course t inner join cte 
         on t.Courses = cte.a and len( a_r ) > 0

   )
select a_l, n from cte
--where N = 1

結果

|    A_L | N |
|--------|---|
| Hello  | 0 |
|  John  | 1 |
| Smith  | 2 |
于 2014-01-16T10:38:04.663 に答える
0

私はこれを発展させた、

declare @x nvarchar(Max) = 'ali.veli.deli.';
declare @item nvarchar(Max);
declare @splitter char='.';

while CHARINDEX(@splitter,@x) != 0
begin
    set @item = LEFT(@x,CHARINDEX(@splitter,@x))
    set @x    = RIGHT(@x,len(@x)-len(@item) )
     select @item as item, @x as x;
end

ドット「.」だけに注意してください。@x の末尾は常にそこにある必要があります。

于 2015-10-15T10:50:09.183 に答える
0
CREATE TABLE test(
    id int,
    adress varchar(100)
);
INSERT INTO test VALUES(1, 'Ludovic Aubert, 42 rue de la Victoire, 75009, Paris, France'),(2, 'Jose Garcia, 1 Calle de la Victoria, 56500 Barcelona, Espana');

SELECT id, value, COUNT(*) OVER (PARTITION BY id) AS n, ROW_NUMBER() OVER (PARTITION BY id ORDER BY (SELECT NULL)) AS rn, adress
FROM test
CROSS APPLY STRING_SPLIT(adress, ',')

ここに画像の説明を入力

于 2021-05-06T10:55:55.343 に答える
-1

さて、私のものはそれほど単純ではありませんが、コンマで区切られた入力変数を個々の値に分割し、それをテーブル変数に入れるために使用するコードを次に示します。これを少し変更してスペースに基づいて分割し、そのテーブル変数に対して基本的なSELECTクエリを実行して結果を取得できると確信しています。

-- Create temporary table to parse the list of accounting cycles.
DECLARE @tblAccountingCycles table
(
    AccountingCycle varchar(10)
)

DECLARE @vchAccountingCycle varchar(10)
DECLARE @intPosition int

SET @vchAccountingCycleIDs = LTRIM(RTRIM(@vchAccountingCycleIDs)) + ','
SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)

IF REPLACE(@vchAccountingCycleIDs, ',', '') <> ''
BEGIN
    WHILE @intPosition > 0
    BEGIN
        SET @vchAccountingCycle = LTRIM(RTRIM(LEFT(@vchAccountingCycleIDs, @intPosition - 1)))
        IF @vchAccountingCycle <> ''
        BEGIN
            INSERT INTO @tblAccountingCycles (AccountingCycle) VALUES (@vchAccountingCycle)
        END
        SET @vchAccountingCycleIDs = RIGHT(@vchAccountingCycleIDs, LEN(@vchAccountingCycleIDs) - @intPosition)
        SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)
    END
END

コンセプトはほとんど同じです。もう1つの方法は、SQLServer2005自体の.NET互換性を活用することです。基本的に、.NETで文字列を分割し、それをストアドプロシージャ/関数として公開する簡単なメソッドを自分で作成できます。

于 2008-08-05T18:36:15.923 に答える
-2

これが誰かを助けるかもしれない私の解決策です。上記の Jonesinator の回答の修正。

区切られた INT 値の文字列があり、返された INT のテーブルが必要な場合 (後で結合できます)。例: '1,20,3,343,44,6,8765'

UDF を作成します。

IF OBJECT_ID(N'dbo.ufn_GetIntTableFromDelimitedList', N'TF') IS NOT NULL
    DROP FUNCTION dbo.[ufn_GetIntTableFromDelimitedList];
GO

CREATE FUNCTION dbo.[ufn_GetIntTableFromDelimitedList](@String NVARCHAR(MAX),                 @Delimiter CHAR(1))

RETURNS @table TABLE 
(
    Value INT NOT NULL
)
AS 
BEGIN
DECLARE @Pattern NVARCHAR(3)
SET @Pattern = '%' + @Delimiter + '%'
DECLARE @Value NVARCHAR(MAX)

WHILE LEN(@String) > 0
    BEGIN
        IF PATINDEX(@Pattern, @String) > 0
        BEGIN
            SET @Value = SUBSTRING(@String, 0, PATINDEX(@Pattern, @String))
            INSERT INTO @table (Value) VALUES (@Value)

            SET @String = SUBSTRING(@String, LEN(@Value + @Delimiter) + 1, LEN(@String))
        END
        ELSE
        BEGIN
            -- Just the one value.
            INSERT INTO @table (Value) VALUES (@String)
            RETURN
        END
    END

RETURN
END
GO

次に、テーブルの結果を取得します。

SELECT * FROM dbo.[ufn_GetIntTableFromDelimitedList]('1,20,3,343,44,6,8765', ',')

1
20
3
343
44
6
8765

そして結合ステートメントで:

SELECT [ID], [FirstName]
FROM [User] u
JOIN dbo.[ufn_GetIntTableFromDelimitedList]('1,20,3,343,44,6,8765', ',') t ON u.[ID] = t.[Value]

1    Elvis
20   Karen
3    David
343  Simon
44   Raj
6    Mike
8765 Richard

INT の代わりに NVARCHAR のリストを返したい場合は、テーブル定義を変更するだけです。

RETURNS @table TABLE 
(
    Value NVARCHAR(MAX) NOT NULL
)
于 2013-06-19T23:42:16.843 に答える
-2

シンプルな最適化アルゴリズム:

ALTER FUNCTION [dbo].[Split]( @Text NVARCHAR(200),@Splitor CHAR(1) )
RETURNS @Result TABLE ( value NVARCHAR(50)) 
AS
BEGIN
    DECLARE @PathInd INT
    Set @Text+=@Splitor
    WHILE LEN(@Text) > 0
    BEGIN
        SET @PathInd=PATINDEX('%'+@Splitor+'%',@Text)
        INSERT INTO  @Result VALUES(SUBSTRING(@Text, 0, @PathInd))
        SET @Text= SUBSTRING(@Text, @PathInd+1, LEN(@Text))
    END
        RETURN 
END
于 2014-05-01T06:26:39.047 に答える
-2

私はしばらくの間、再帰的な cte を使用して vzczc の回答を使用してきましたが、可変長の区切り記号を処理するように更新したいと考えていました。 :

「ボブ」、「スミス」、「サニーベール」、「CA」

または、以下に示すように 6 つの部分の fqn を扱っている場合。私はこれらを監査、エラー処理などのために subject_fqn のログに広く使用しています。parsename は次の 4 つの部分のみを処理します。

[netbios_name].[machine_name].[instance].[database].[schema].[table].[column]

これが私の更新版です。元の投稿をしてくれた vzczc に感謝します。

select * from [utility].[split_string](N'"this"."string"."gets"."split"."and"."removes"."leading"."and"."trailing"."quotes"', N'"."', N'"', N'"');

select * from [utility].[split_string](N'"this"."string"."gets"."split"."but"."leaves"."leading"."and"."trailing"."quotes"', N'"."', null, null);

select * from [utility].[split_string](N'[netbios_name].[machine_name].[instance].[database].[schema].[table].[column]', N'].[', N'[', N']');

create function [utility].[split_string] ( 
  @input       [nvarchar](max) 
  , @separator [sysname] 
  , @lead      [sysname] 
  , @lag       [sysname]) 
returns @node_list table ( 
  [index]  [int] 
  , [node] [nvarchar](max)) 
  begin 
      declare @separator_length [int]= len(@separator) 
              , @lead_length    [int] = isnull(len(@lead), 0) 
              , @lag_length     [int] = isnull(len(@lag), 0); 
      -- 
      set @input = right(@input, len(@input) - @lead_length); 
      set @input = left(@input, len(@input) - @lag_length); 
      -- 
      with [splitter]([index], [starting_position], [start_location]) 
           as (select cast(@separator_length as [bigint]) 
                      , cast(1 as [bigint]) 
                      , charindex(@separator, @input) 
               union all 
               select [index] + 1 
                      , [start_location] + @separator_length 
                      , charindex(@separator, @input, [start_location] + @separator_length) 
               from   [splitter] 
               where  [start_location] > 0) 
      -- 
      insert into @node_list 
                  ([index],[node]) 
        select [index] - @separator_length                   as [index] 
               , substring(@input, [starting_position], case 
                                                            when [start_location] > 0 
                                                                then 
                                                              [start_location] - [starting_position] 
                                                            else 
                                                              len(@input) 
                                                        end) as [node] 
        from   [splitter]; 
      -- 
      return; 
  end; 
go 
于 2014-08-19T19:45:33.960 に答える