Возвращение индекса n-го элемента в строке с помощью tsql - PullRequest
1 голос
/ 07 февраля 2011

Как в SQL Server я могу вернуть индекс n-го элемента в строке.Например, у меня есть строка: a, b, c, d, e Теперь я хочу получить все элементы до третьей комы, поэтому a, b, c .Для этого мне нужен индекс третьей комы.Charindex здесь не подходит, потому что я не могу передать число комы, для которой я хочу индекс.Есть ли встроенная функция, которая может это сделать?

Обновление , @Andriy M.

Эй, ссылка, на которую Вы указали мне, также полезна.Ниже я немного изменил показанный sql, чтобы получить то, что я хочу:

CREATE FUNCTION [dbo].[nthindexsub](@String varchar(8000), @Delimiter char(1), @DelimitersCount int)       
 returns varchar(8000)
 as       
 begin       
     declare @Substring varchar(8000)
     declare @temptable TABLE (itemindex int identity(1,1), items varchar(8000))
     declare @idx int       
     declare @slice varchar(8000)     

     select @idx = 1       
         if len(@String)<1 or @String is null  return @Substring 

     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

     select @Substring = COALESCE(@Substring + ',', '') + items from @temptable where itemindex <= @DelimitersCount  
     return @Substring;
 end

использование:

select dbo.nthindexsub('a,b,c,d,e', ',', 3)

результат: a, b, c

Ответы [ 3 ]

2 голосов
/ 07 февраля 2011

Я не уверен, почему вы просто хотите индекс? почему бы просто не разбить их на строки, быстрее и лучше работать со строками. Вот простой пример разбиения TSQL:

DECLARE @List varchar(500)
       ,@SplitOn char(1)
SELECT @List='aaa,bb,c,dd,eeee'
      ,@SplitOn=','
;WITH NumbersCTE AS
(
    SELECT 1 AS Number
    UNION ALL
    SELECT Number+1
        FROM NumbersCTE
        WHERE Number<LEN(@List)
)
, AllSplit AS
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY number) AS RowNumber
            ,LTRIM(RTRIM(SUBSTRING(ListValue, number+1, CHARINDEX(@SplitOn, ListValue, number+1)-number - 1))) AS ListValue
        FROM (
                 SELECT @SplitOn + @List + @SplitOn AS ListValue
             ) AS InnerQuery
            INNER JOIN NumbersCTE n ON n.Number < LEN(InnerQuery.ListValue)
        WHERE SUBSTRING(ListValue, number, 1) = @SplitOn
)
SELECT
    ListValue
    FROM AllSplit
    WHERE RowNumber<=3

OUTPUT

ListValue
----------------
aaa
bb
c

Есть много способов разбить строку в SQL Server 2005. В этой статье рассматриваются преимущества и недостатки практически каждого метода:

"Массивы и списки в SQL Server 2005 и более поздних версиях, когда параметры табличных значений не обрезают его", Эрланд Соммарског

Вам необходимо создать функцию разделения. Вот как можно использовать функцию разделения:

SELECT
    *
    FROM YourTable                               y
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value

Я предпочитаю подход с использованием таблицы чисел для разделения строки в TSQL , но существует множество способов разделения строк в SQL Server (в предыдущем примере я использовал CTE), см. Предыдущую ссылку, в которой объясняется плюсы и минусы каждого.

Чтобы метод таблицы чисел сработал, необходимо выполнить настройку единой таблицы, которая создаст таблицу Numbers, содержащую строки от 1 до 10 000:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

После настройки таблицы Numbers создайте функцию разделения:

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(

    ----------------
    --SINGLE QUERY-- --this will not return empty rows
    ----------------
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''

);
GO 

Теперь вы можете легко разбить строку CSV на таблицу и присоединиться к ней:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,')

ВЫВОД:

ListValue
-----------------------
1
2
3
4
5
6777

(6 row(s) affected)

Вы можете использовать строку CSV следующим образом, а не временную таблицу:

SELECT 
    y.* 
    FROM YourTable  y
        INNER JOIN dbo.FN_ListToTable(',',@MyList) x ON y.pk=x.ListValue
    Where y.aaa='5'
1 голос
/ 07 февраля 2011

Это можно сделать в TSQL, НО TSQL не очень хорош (или не предназначен для) манипуляции со строками и поэтому может быть медленным, если вы часто его вызываете.лучше создать хранимую процедуру CLR .

0 голосов
/ 07 февраля 2011

, если вы хотите получить только первые три элемента, попробуйте вложить CHARINDEX следующим образом:

DECLARE @YourTable table (csv varchar(50))
INSERT @YourTable VALUES ('aaa,bb,c,dd,eeee')
INSERT @YourTable VALUES ('aaa,bb,c,dd')
INSERT @YourTable VALUES ('aaa,bb,c')
INSERT @YourTable VALUES ('aaa,bb')
INSERT @YourTable VALUES ('aaa')
INSERT @YourTable VALUES (null)

SELECT 
    csv
        ,CHARINDEX(',',csv+',',CHARINDEX(',',csv+',',CHARINDEX(',',csv+',',0)+1)+1) AS ThirdComma
    FROM @YourTable
    WHERE CHARINDEX(',',csv,0)>0 
        AND CHARINDEX(',',csv+',',CHARINDEX(',',csv+',',CHARINDEX(',',csv+',',0)+1)+1)>0

OUTPUT:

csv                                                
-------------------------------------------------- -----------
aaa,bb,c,dd,eeee                                   9
aaa,bb,c,dd                                        9
aaa,bb,c                                           9

это вернет только строки, где есть три элемента.если вы хотите, чтобы их было один, два и / или три, попробуйте это:

DECLARE @YourTable table (csv varchar(50))
INSERT @YourTable VALUES ('aaa,bb,c,dd,eeee')
INSERT @YourTable VALUES ('aaa,bb,c,dd')
INSERT @YourTable VALUES ('aaa,bb,c')
INSERT @YourTable VALUES ('aaa,bb')
INSERT @YourTable VALUES ('aaa')
INSERT @YourTable VALUES (null)

;WITH FindThird AS
(
    SELECT csv, CHARINDEX(',',csv+',',CHARINDEX(',',csv+',',CHARINDEX(',',csv+',',0)+1)+1) AS ThirdComma
    FROM @YourTable
)
SELECT 
    csv
        ,CASE 
             WHEN ThirdComma IS NULL OR ThirdComma=0 THEN LEN(csv)+1
             ELSE ThirdComma
         END AS ThirdComma
    FROM FindThird

ВЫХОД:

csv                 ThirdComma
------------------- -----------
aaa,bb,c,dd,eeee    9
aaa,bb,c,dd         9
aaa,bb,c            9
aaa,bb              7
aaa                 4
NULL                NULL
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...