在由“。”分隔的字符串中获取第n个单词。

时间:2018-05-10 07:15:20

标签: sql sql-server

已经有查询来获取第一个用“。”分隔的单词,我需要一个显示第三个单词的新列

SELECT   
    MyColumn, 
    LEFT(MyColumn, CHARINDEX('.', MyColumn) - 1) AS [1st Sequence]
FROM    
    dbo.MyTable

样品#1:

2345.Z2Z2.A12151.AB4R

查询应返回A12151

样本#2:

STR4.35S982F.X24

查询应返回X24

编辑:

感谢大家分享您的疑问。尽管WITH CTE有效,但我正在考虑一个更简单的查询,因为它将用作我的下拉列表中的数据源,并且在另一个下拉列表更改时将始终刷新。

我认为这可以通过SUBSTRING来完成。请参阅下面我发现并测试的查询,其中提取了第二个单词。我正在修改它以提取第三个单词。如果有人能帮助我,我会非常感激:

SELECT MyColumn,
SUBSTRING(MyColumn, CHARINDEX('.', MyColumn) + 1 , CHARINDEX('.', MyColumn, CHARINDEX('.', MyColumn)+1) - (CHARINDEX('.', MyColumn) + 1))
FROM dbo.MyTable

9 个答案:

答案 0 :(得分:2)

我建议使用Common Table Expressions

;WITH CTE AS
(
  SELECT 1 AS RowNo, LEFT(MyColumn, CHARINDEX('.', MyColumn)-1) AS Word,
    RIGHT(MyColumn, LEN(MyColumn) - CHARINDEX('.', MyColumn)) AS Remainder
  FROM Dummy
  WHERE CHARINDEX('.', MyColumn) >0
  UNION ALL
  SELECT RowNo +1, LEFT(Remainder, CHARINDEX('.', Remainder)-1) AS Word,
    RIGHT(Remainder, LEN(Remainder) - CHARINDEX('.', Remainder)) AS Remainder
  FROM CTE
  WHERE CHARINDEX('.', Remainder) >0
  UNION ALL
  SELECT RowNo+ 1, Remainder As Word, NULL As Remainder
  FROM CTE
  WHERE CHARINDEX('.', Remainder) = 0
)
SELECT Word
FROM CTE
WHERE RowNo = 3

结果:

  

X24

     

A12151

SQLFiddle

有关详细信息,请参阅:Recursive Queries Using Common Table Expressions

[EDIT#2]

假设您希望将每个单词放入不同的列,则必须将最后的SELECT语句更改为:

SELECT ID, [1], [2], [3], [4]
FROM (
  SELECT RowNo, Word, ROW_NUMBER() OVER(PARTITION BY RowNo ORDER BY RowNo) AS ID
  FROM CTE
) AS src
PIVOT (MAX(Word) FOR RowNo IN([1], [2], [3], [4])) AS pvt

结果:

ID  1       2           3       4

1   2345    35S982F     A12151  AB4R

2   STR4    Z2Z2        X24     (null)

答案 1 :(得分:2)

一种方法是使用Jeff Moden的DelmimitedSplit8k。这个分割器的优点是它返回每个项目的序数位置,这对您的目标至关重要。它也是许多其他人不返回的东西(包括SQL Server的STRING_SPLIT)。这变得非常简单:

WITH VTE AS (
    SELECT *
    FROM (VALUES('2345.Z2Z2.A12151.AB4R'),('STR4.35S982F.X24'))V(S))    
SELECT DS.Item
FROM VTE
     CROSS APPLY dbo.DelimitedSplit8K(VTE.S,'.') DS
WHERE DS.ItemNumber = 3;

答案 2 :(得分:1)

使用PARSENAME可以解决这个问题,但如果字符串中有三个以上的点,则不支持限制。

使用样本数据执行查询:

DECLARE @SampleTable TABLE (TextValue VARCHAR (2000));

INSERT INTO @SampleTable (TextValue)
VALUES ('2345.Z2Z2.A12151.AB4R'), ('STR4.35S982F.X24'), ('123.345'), ('12345'), (NULL);

SELECT  CASE (LEN(TextValue) - LEN(REPLACE(TextValue, '.', '')))
        WHEN 3 THEN PARSENAME(TextValue, 2)
        WHEN 2 THEN PARSENAME(TextValue, 1)
        ELSE TextValue
        END AS TextValue
FROM @SampleTable

答案 3 :(得分:1)

当您没有SQL2016并且不允许创建函数或存储过程时,可以将其强制转换为xml,然后选择第n个节点。

declare @someText nvarchar(max) = 'this is some very interesting text'

select CAST( '<p>'+Replace(rtrim(ltrim(@someText)),' ','</p><p>')+'</p>' AS XML).value('(/p)[5]','nvarchar(MAX)')

result -> interesting

答案 4 :(得分:0)

请试试这个 -

;WITH CTE AS 
(
    SELECT *
    FROM (VALUES('2345.Z2Z2.A12151.AB4R'),('STR4.35S982F.X24'))V(S)
)
,CTE1 AS
(
    SELECT * , 
    REVERSE(SUBSTRING(REVERSE(S),CHARINDEX('.',REVERSE(S),0)
    ,(CHARINDEX('.', REVERSE(S), CHARINDEX('.',REVERSE(S),0)+1) - CHARINDEX('.',REVERSE(S),0)))) N FROM CTE   
)
SELECT S,SUBSTRING(N,0,LEN(N)) N
FROM CTE1

输出

S                     N
--------------------- ---------------------
2345.Z2Z2.A12151.AB4R A12151
STR4.35S982F.X24      35S982F

(2 rows affected)

答案 5 :(得分:0)

首先,在SQL Server 2016及更高版本中运行的快速而肮脏的解决方案是通过将分隔符替换为","并使用JSON_VALUE仅提取第N个值将字符串转换为JSON数组:

select JSON_VALUE('["' + REPLACE('2345.Z2Z2.A12151.AB4R','.','","') + '"]','$[2]')

返回

A12151

这很脏,因为它执行(可能很重)字符串替换。如果该函数应用于很多行,则可以相加。

更好的解决方案是在SQL中拆分字符串。问题是

  

这将用作我的下拉列表的数据源,并将在另一个下拉列表更改时始终刷新

解析用于显示目的是客户端数据访问代码的工作,而不是数据库的工作。首先,字段应仅包含单个值。如果2345.Z2Z2.A12151.AB4R中的四个值很重要,则应将它们存储在4个不同的字段中,以便于查询和索引。

也就是说,的情况,人们希望存储Value Objects - 数据项的值不应被视为单独的entites。许多ORM都支持Value Objects,如EF Core 2.0和NHibernate。

使用值对象,字符串中的四个值可以映射到单个属性,并在UI中的数据绑定表达式中使用。即使没有ORM支持,实现Value对象也很容易。它可以像使用构造函数创建一个类一样简单,该构造函数接受一个字符串和ToString()覆盖,返回要序列化的字符串,例如:

class MyComplexInvoice
{
    public string A {get;set;}
    public string B {get;set;}
    public string C {get;set;}
    public string D {get;set;}

    public MyComplexInvoice(string input)
    {
      var items=inmput.Split('.');
      A=items[0];
      ...
    }

    public override string ToString()
    {
        return $"{A}.{B}.{C}.{D}";
    }        

}

更好的实现方法是使用Parse()TryParse()方法,例如大多数内置类型中的方法,例如:

class MyComplexInvoice
{
    public string A {get;set;}
    public string B {get;set;}
    public string C {get;set;}
    public string D {get;set;}

    public MyComplexInvoice(string a,string string c,string d)
    {
      A=a ?? throw ArgumentNullException(nameof(a));
      ...
    }

    public static bool TryParse(string input,out MyComplexInvoice inv)
    {
        inv=null;
        if (String.IsNullOrWhitespace(input) return false;

        var items=inmput.Split('.');

        if (items.Length!=4) return false;
        // More validations ....

        inv=new MyComplexInvoice(items[0],items[1],items[2],items[3]);

        return true;            
    }

    public override string ToString()
    {
        return $"{A}.{B}.{C}.{D}";
    }        

}

答案 6 :(得分:0)

在T-SQL中,您可以使用窗口函数:

declare @nthPos int = 2

select MyColumn, 
    (
        select [value] 
        from (
            select row_number() over (order by (select null)) as 'ID', [value] 
            from string_split(MyColumn, '.')
        ) x 
        where ID = @nthPos
    ) as 'nthValue'
from dbo.MyTable

答案 7 :(得分:0)

对于一个讨厌的日志文件,我需要这个确切的东西,其中包含需要解析的数据。我写了两个版本。一个带有Tally表,另一个没有。据我了解,Tally表版本应该更快。不知道这是不是真的,但这就是我最后得到的结果。对于Tally表,我必须承认下面的许多代码来自另一篇文章。如果找到它,我会引用它。

(SQL SERVER)

CREATE FUNCTION dbo.fn_SplitGetNth (@pSTR VARCHAR(MAX),@pOccurance int,@pDlm VARCHAR(MAX)=',')
RETURNS VARCHAR(MAX)
BEGIN
  DECLARE @vRslt VARCHAR(MAX)

 SET @pSTR=@pDlm + @pSTR + @pDlm;
 SELECT @vrslt=Word FROM
    (
  SELECT SUBSTRING(@pstr,id+1,CHARINDEX(@pDlm,@pSTR,id+1)-id-1)[Word],ROW_NUMBER() OVER (ORDER BY id)[Position]
    FROM Tally where id<LEN(@pstr)
    AND SUBSTRING(@pSTR,ID,1)=@pDlm 
    ) a
    WHERE a.Position=@pOccurance

  RETURN @vRslt;
end

Usage:
SELECT dbo.fn_SplitGetNth('STR4.35S982F.X24',3,'.')

结果:

COLUMN1
-------
X24

没有理货单:

CREATE FUNCTION dbo.fnSplitStringX
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1),
    @nthValue int
) 
RETURNS NVARCHAR(120) 
 AS
BEGIN 
    DECLARE @start INT, @end INT, @cnt INT=0;

    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1
        SET @cnt=@cnt+1
        -- INSERT INTO @output (splitdata)  
        IF (@cnt=@nthValue)
        BEGIN
              RETURN SUBSTRING(@string, @start, @end - @start) 
        END
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)
        
    END 
    RETURN ''
END

答案 8 :(得分:0)

CREATE FUNCTION TextSplit (@Testo VARCHAR(MAX), @Delimiter VARCHAR(128), @Position INT)
RETURNS varchar(max)
AS
BEGIN

        DECLARE @Xml XML
        DECLARE @Output TABLE (ID int IDENTITY(1,1),SplitData varchar(max))
        DECLARE @Result varchar(max)

        SET @Xml = CAST(('<a>'+REPLACE(@Testo,@delimiter,'</a><a>')+'</a>') AS XML)

        INSERT INTO @Output (SplitData)
        SELECT ltrim(rtrim(A.value('.', 'VARCHAR(MAX)')))  FROM @Xml.nodes('a') AS FN(a)
        SET @Result = (SELECT SplitData FROM @Output WHERE ID = @Position)

    RETURN(@Result);
END