Разделить слова с большой буквы в sql

Кто-нибудь знает, как разбить слова, начинающиеся с заглавных букв, из строки?

Пример:

    DECLARE @var1 varchar(100) = 'OneTwoThreeFour'
    DECLARE @var2 varchar(100) = 'OneTwoThreeFourFive'
    DECLARE @var3 varchar(100) = 'One'

    SELECT @var1 as Col1, <?> as Col2
    SELECT @var2 as Col1, <?> as Col2
    SELECT @var3 as Col1, <?> as Col2

ожидаемый результат:

    Col1                Col2
    OneTwoThreeFour     One Two three Four
    OneTwoThreeFourFive One Two Three Four Five
    One                 One

Если это невозможно (или если слишком долго), то скалярная функция тоже подойдет.

Ответ 1

Вот созданная мной функция, похожая на "удаление неалфавитных символов". Как удалить все неалфавитные символы из строки в SQL Server?

В этом случае используется чувствительная к регистру сортировка, которая активно ищет комбинацию без пробелов/заглавных букв, а затем использует функцию STUFF для вставки пространства. Это скалярный UDF, поэтому некоторые люди сразу скажут, что он будет медленнее, чем другие решения. К этому понятию, я говорю, пожалуйста, проверьте его. Эта функция не использует никаких табличных данных и только петли столько раз, сколько необходимо, поэтому она, вероятно, даст вам очень хорошую производительность.

Create Function dbo.Split_On_Upper_Case(@Temp VarChar(1000))
Returns VarChar(1000)
AS
Begin

    Declare @KeepValues as varchar(50)
    Set @KeepValues = '%[^ ][A-Z]%'
    While PatIndex(@KeepValues collate Latin1_General_Bin, @Temp) > 0
        Set @Temp = Stuff(@Temp, PatIndex(@KeepValues collate Latin1_General_Bin, @Temp) + 1, 0, ' ')

    Return @Temp
End

Назовите его следующим образом:

Select dbo.Split_On_Upper_Case('OneTwoThreeFour')
Select dbo.Split_On_Upper_Case('OneTwoThreeFour')
Select dbo.Split_On_Upper_Case('One')
Select dbo.Split_On_Upper_Case('OneTwoThree')
Select dbo.Split_On_Upper_Case('stackOverFlow')
Select dbo.Split_On_Upper_Case('StackOverFlow')

Ответ 2

Вот функция, которую я только что создал.

Функция

CREATE FUNCTION dbo.Split_On_Upper_Case
 (
     @String VARCHAR(4000)
 )
RETURNS VARCHAR(4000)
AS
BEGIN

DECLARE @Char CHAR(1);
DECLARE @i    INT = 0;
DECLARE @OutString VARCHAR(4000) = '';


WHILE (@i <= LEN(@String))
BEGIN
    SELECT @Char = SUBSTRING(@String, @i,1)

    IF (@Char = UPPER(@Char) Collate Latin1_General_CS_AI) 
       SET @OutString = @OutString + ' ' + @Char;
    ELSE 
       SET @OutString = @OutString +  @Char;

     SET @i += 1;
END

 SET @OutString =  LTRIM(@OutString);

 RETURN @OutString;

END 

Данные тестирования

DECLARE @TABLE TABLE (Strings VARCHAR(1000))
INSERT INTO @TABLE 
VALUES ('OneTwoThree')   ,
       ('FourFiveSix')   ,
       ('SevenEightNine')

Query

SELECT dbo.Split_On_Upper_Case(Strings) AS Vals
FROM @TABLE

Набор результатов

╔══════════════════╗
║       Vals       ║
╠══════════════════╣
║ One Two Three    ║
║ Four Five Six    ║
║ Seven Eight Nine ║
╚══════════════════╝

Ответ 3

Если требуется один запрос 26 REPLACE может использоваться для проверки каждой буквы верхнего регистра, например

SELECT @var1 col1, REPLACE(
                    REPLACE(
                     REPLACE(
                      ...
                       REPLACE(@var1, 'A', ' A')
                    , ... 
                  , 'X', ' X')
                , 'Y', ' Y')
              , 'Z', ' Z') col2

Не самая красивая вещь, но она будет работать.

ИЗМЕНИТЬ
Просто добавьте еще одну функцию, чтобы сделать то же самое другим способом других ответов.

CREATE FUNCTION splitCapital (@param Varchar(MAX))
RETURNS Varchar(MAX)
BEGIN
  Declare @ret Varchar(MAX) = '';
  declare @len int = len(@param);

  WITH Base10(N) AS (
              SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 
    UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 
    UNION ALL SELECT 8 UNION ALL SELECT 9
  ), Chars(N) As (
    Select TOP(@len)
           nthChar 
         = substring(@param, u.N + t.N*10 + h.N*100 + th.N*1000 + 1, 1) 
           Collate Latin1_General_CS_AI
    FROM   Base10 u
           CROSS JOIN Base10 t
           CROSS JOIN Base10 h
           CROSS JOIN Base10 th
    WHERE  u.N + t.N*10 + h.N*100 + th.N*1000 < @len
    ORDER BY u.N + t.N*10 + h.N*100 + th.N*1000
  )
  SELECT @ret += Case nthChar 
                      When UPPER(nthChar) Then ' ' 
                      Else '' 
                 End + nthChar
  FROM   Chars

  RETURN @ret;
END

Это использует возможность TSQL для конкатенации строковой переменной, мне пришлось использовать трюк TOP N, чтобы заставить строки Chars CTE в правильном порядке

Ответ 4

Построить таблицу чисел. На SO есть несколько отличных сообщений, чтобы показать вам, как это сделать. Заполняйте его значениями до максимальной длины вашей входной строки. Выберите значения от 1 до фактической длины текущей входной строки. Перекрестно присоедините этот список чисел к входной строке. Используйте результат для SUBSTRING() каждого символа. Затем вы можете либо сравнить полученный список значений одного шахатера с предварительно заполненной табличной переменной, либо преобразовать каждый символ в целое число с помощью ASCII() и выбрать только те, которые находятся между 65 ( "A" ) и 90 ( "Z",). На этом этапе у вас есть список, который является позицией каждого символа верхнего регистра в вашей строке ввода. UNION максимальная длина вашей входной строки в конце этого списка. Вы поймете, почему всего за секунду. Теперь вы можете SUBSTRING() ввести переменную ввода, начиная с номера, заданного строкой N, и взять длину (число, заданное строкой N + 1) - (число, заданное строкой N). Вот почему вы должны UNION добавить дополнительный номер в конец. Наконец, конкатенируйте все эти подстроки вместе, разделенные пробелами, используя алгоритм по вашему выбору.

Извините, у меня нет экземпляра передо мной, чтобы попробовать код. Звучит забавно. Я думаю, что делать это с вложенными операторами SELECT будет запутано и не поддерживается; лучше расставить его как CTE, IMHO.

Ответ 5

Я использую функцию ITVF (функция таблицы). Что касается производительности, встроенная функция работает как представление

    CREATE FUNCTION [dbo].[udf_Split_Capitals_In_Str]
    (@str VARCHAR(8000))
    RETURNS TABLE AS RETURN 
    WITH Tally (n) AS
    (
        SELECT TOP (LEN (@str)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
        FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0)) a(n)
        CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
        CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
        CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d(n)

    )
    SELECT New_Str =  STUFF((
        SELECT 
        CASE 
            WHEN SUBSTRING(@str, n,1) = UPPER(SUBSTRING(@str, n,1)) Collate Latin1_General_CS_AI AND n > 1 
            THEN ' ' + SUBSTRING(@str, n,1)  
            ELSE SUBSTRING(@str, n,1) 
        END
    FROM Tally
    FOR XML PATH ('')),1,0,'')

/*How To use:*/
SELECT * FROM dbo.udf_Split_Capitals_In_Str ('HelloWorld')
/*Cross Apply Example*/
    SELECT T.* , Fixed_Name.New_Str FixedName
    FROM 
        (       
        SELECT Id= 1 , Name = 'DonaldTrump'
        UNION ALL 
        SELECT Id= 2 , Name = 'HilaryClinton'
        ) T
    CROSS APPLY dbo.udf_Split_Capitals_In_Str (T.Name) Fixed_Name

Ответ 6

Я знаю, что есть уже неплохие ответы, но если вы хотите избежать создания функции, вы также можете использовать рекурсивный CTE для этого. Это, конечно, не чистый способ сделать это, но он работает.

DECLARE
    @camelcase nvarchar(4000) = 'ThisIsCamelCased'
;

WITH
    split
AS
    (
        SELECT
              [iteration]   = 0
             ,[string]      = @camelcase

        UNION ALL

        SELECT
             [iteration]    = split.[iteration] + 1
            ,[string]       = STUFF(split.[string], pattern.[index] + 1, 0, ' ')
        FROM
            split
        CROSS APPLY
            ( SELECT [index] = PATINDEX(N'%[^ ][A-Z]%' COLLATE Latin1_General_Bin, split.[string]) )
            pattern
        WHERE
            pattern.[index] > 0
    )
SELECT TOP (1)
    [spaced] = split.[string]
FROM
    split
ORDER BY
    split.[iteration]   DESC
;

Как я уже сказал, это не очень хороший способ написать запрос, но я использую такие вещи, когда я просто пишу некоторые специальные запросы, в которых я бы не хотел добавлять новые артефакты в базу данных. Вы также можете использовать это, чтобы создать свою функцию как функцию встроенной таблицы, которая всегда приятна.

Ответ 7

Пожалуйста, попробуйте это:

    declare  @t nvarchar (100) ='IamTheTestString'  
    declare  @len int
    declare  @Counter int =0 
    declare  @Final nvarchar (100) =''
    set @len =len( @t)


    while (@Counter <= @len)
    begin 

    set @Final= @Final + Case when  ascii(substring (@t,@Counter,1))>=65 and  
    ascii(substring (@t,@Counter,1))<=90 then ' '+substring (@t,@Counter,1) else 
    substring (@t,@Counter,1) end


    set @[email protected]+1
    end

    print ltrim(@Final)