Другой вариант - обработать это через SQLCLR. В .NET уже есть метод, который делает это: TextInfo.ToTitleCase (in System.Globalization
). Этот метод будет в верхнем регистре первой буквы каждого слова, а в нижнем регистре остальные буквы. В отличие от других предложений, здесь также пропускаются слова в верхнем регистре, считая их аббревиатурами. Конечно, если это необходимо, было бы достаточно просто обновить любое из предложений T-SQL, чтобы сделать это.
Одним из преимуществ метода .NET является то, что он может использовать буквы верхнего регистра, которые являются дополнительными символами. Например: DESERET SMALL LETTER OW имеет отображение в верхнем регистре DESERET CAPITAL LETTER OW (оба отображаются в виде блоков, когда я вставляю их сюда) , но UPPER()
функция не изменяет версию нижнего регистра на верхний регистр, даже когда Сортировка по умолчанию для текущей базы данных установлена на Latin1_General_100_CI_AS_SC
. Кажется, это согласуется с документацией MSDN, в которой нет перечня UPPER
и LOWER
в таблице функций, которые ведут себя по-разному при использовании параметров _SC
Collation: Collation и Unicode: Дополнительные символы .
SELECT N'DESERET SMALL LETTER OW' AS [Label], NCHAR(0xD801)+NCHAR(0xDC35) AS [Thing]
UNION ALL
SELECT N'DESERET CAPITAL LETTER OW' AS [Label], NCHAR(0xD801)+NCHAR(0xDC0D) AS [Thing]
UNION ALL
SELECT N'SmallButShouldBeCapital' AS [Label], UPPER(NCHAR(0xD801)+NCHAR(0xDC35)) AS [Thing]
Возвращает (увеличено, чтобы вы могли видеть дополнительный символ):
Вы можете увидеть полный (и текущий) список символов в нижнем регистре и изменить его в верхний регистр, используя следующую функцию поиска на Unicode.org (вы можете увидеть дополнительные символы, прокручивая вниз, пока не дойдете до «DESERET») раздел, или просто нажмите Control-Fи найдите это слово):
http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AChanges_When_Titlecased%3DYes%3A%5D
Хотя, честно говоря, это не является огромным преимуществом, так как сомнительно, что кто-то на самом деле использует какой-либо из дополнительных символов, которые могут быть в названии. В любом случае, вот код SQLCLR:
using System.Data.SqlTypes;
using System.Globalization;
using Microsoft.SqlServer.Server;
public class TitleCasing
{
[return: SqlFacet(MaxSize = 4000)]
[Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlString TitleCase([SqlFacet(MaxSize = 4000)] SqlString InputString)
{
TextInfo _TxtInf = new CultureInfo(InputString.LCID).TextInfo;
return new SqlString (_TxtInf.ToTitleCase(InputString.Value));
}
}
Вот предложение @ MikaelEriksson - слегка измененное для обработки NVARCHAR
данных, а также для пропуска слов в верхнем регистре (чтобы более точно соответствовать поведению метода .NET) - вместе с тестом этой реализации T-SQL и реализация SQLCLR:
SET NOCOUNT ON;
DECLARE @a NVARCHAR(50);
SET @a = N'qWeRtY kEyBoArD TEST<>&''"X one&TWO '
+ NCHAR(0xD801)+NCHAR(0xDC28)
+ N'pPLe '
+ NCHAR(0x24D0) -- ⓐ Circled "a"
+ NCHAR(0xFF24) -- D Full-width "D"
+ N'D u'
+ NCHAR(0x0308) -- ̈ (combining diaeresis / umlaut)
+ N'vU'
+ NCHAR(0x0308) -- ̈ (combining diaeresis / umlaut)
+ N'lA';
SELECT @a AS [Original];
SELECT STUFF((
SELECT N' '
+ IIF(UPPER(T3.V) <> T3.V COLLATE Latin1_General_100_BIN2,
UPPER(LEFT(T3.V COLLATE Latin1_General_100_CI_AS_SC, 1))
+ LOWER(STUFF(T3.V COLLATE Latin1_General_100_CI_AS_SC, 1, 1, N'')),
T3.V)
FROM (SELECT CAST(REPLACE((SELECT @a AS N'*' FOR XML PATH('')), N' ', N'<X/>')
AS XML).query('.')) AS T1(X)
CROSS APPLY T1.X.nodes('text()') AS T2(X)
CROSS APPLY (SELECT T2.X.value('.', 'NVARCHAR(70)')) AS T3(V)
FOR XML PATH(''), TYPE
).value('text()[1]', 'NVARCHAR(70)') COLLATE Latin1_General_100_CI_AS_SC, 1, 1, N'')
AS [Capitalize first letter only];
SELECT dbo.TitleCase(@a) AS [ToTitleCase];
Другое различие в поведении состоит в том, что эта конкретная реализация T-SQL разделяется только на пробелы, тогда как ToTitleCase()
метод рассматривает большинство не-букв как разделители слов (отсюда и разница в обработке части «one & TWO»).
Обе реализации правильно обрабатывают комбинируемые последовательности. Каждая из акцентированных букв в «üvÜlA» состоит из базовой буквы и сочетания диареза / умляута (две точки над каждой буквой), и они правильно преобразуются в другой случай в обоих тестах.
Наконец, один неожиданный недостаток версии SQLCLR заключается в том, что при создании различных тестов я обнаружил ошибку в коде .NET, связанную с обработкой кружковых букв (о которой теперь сообщалось в Microsoft Connect - UPDATE: Connect был переехал /dev/null
- буквально - так что мне может понадобиться повторить это, если проблема все еще существует). Библиотека .NET рассматривает Обведенные буквы как разделители слов, поэтому она не превращает «ⓐDD» в «Ⓐdd», как следует.
FYI
Готовая функция SQLCLR, инкапсулирующая TextInfo.ToTitleCase
упомянутый выше метод, теперь доступна в бесплатной версии SQL # (которую я написал) как String_ToTitleCase и String_ToTitleCase4k .
😺