Разделить слова с большой буквы в 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)