Какой хороший способ обрезать все символы пробелов из строки в T-SQL без UDF и без CLR?

Функция .NET string.Trim обрезает довольно обширный набор символов пробелов. Как это точное поведение будет эмулироваться наилучшим образом T-SQL?

LTRIM и RTRIM только обрезать пробельный символ, который недостаточно.

Это было бы легко с SQL CLR, но это не всегда возможно, а также имеет довольно высокую стоимость вызова. Поэтому я хотел бы избежать SQL CLR.

Кроме того, скалярные UDF вынуждают последовательные планы и медленно запускаются. Поэтому он, вероятно, также не должен быть скалярным UDF.

Учитывая эти ограничения, что лучший способ сделать это?

Ответ 1

Этот код предоставляет шаблон, который вы можете расширить, чтобы обрабатывать пробелы по вашему выбору для измененного LTRIM.

declare @Tab as NVarChar(1) = NChar( 9 );
declare @Space as NVarChar(1) = NChar( 32 );

declare @Samples as Table ( String NVarChar(16) );
insert into @Samples ( String ) values
  ( 'Foo' ),
  ( @Tab + 'Foo' ),
  ( @Space + 'Foo' ),
  ( @Space + @Tab + 'Foo' ),
  ( @Tab + @Space + 'Foo' );
select String, Len( String ) as [Length], PatIndex( '%[^' + @Tab + @Space + ']%', String ) - 1 as [WhitespaceCount]
  from @Samples;

Функция REVERSE может использоваться для реализации модифицированной версии RTRIM.

ОБНОВЛЕНИЕ NEWER: В следующем коде используется список символов пробелов, используемых в .NET Framework 4. Он также работает вокруг функции LEN, не считая завершающих пробелов.

declare @Tab as NVarChar(1) = NChar( 9 );
declare @Space as NVarChar(1) = NChar( 32 );

declare @Samples as Table ( String NVarChar(16) );
insert into @Samples ( String ) values
  ( 'Foo' ),
  ( @Tab + 'Foo' ),
  ( @Space + 'Foo' ),
  ( @Space + @Tab + 'Foo' ),
  ( @Tab + @Space + 'Foo' ),
  ( @Tab + 'Foo' + @Space ),
  ( @Space + 'Foo' + @Tab ),
  ( @Space + @Tab + 'Foo' + @Tab + @Space ),
  ( @Tab + @Space + 'Foo' + @Space + @Tab ),
  ( 'Foo' + @Tab ),
  ( NULL ),
  ( '           ' ),
  ( @Space + NULL + @Tab + @Tab ),
  ( '' ),
  ( 'Hello world!' );

declare @WhitespacePattern as NVarChar(100) = N'%[^' +
  NChar( 0x0020 ) + NChar( 0x00A0 ) + NChar( 0x1680 ) + NChar( 0x2000 ) +
  NChar( 0x2001 ) + NChar( 0x2002 ) + NChar( 0x2003 ) + NChar( 0x2004 ) +
  NChar( 0x2005 ) + NChar( 0x2006 ) + NChar( 0x2007 ) + NChar( 0x2008 ) +
  NChar( 0x2009 ) + NChar( 0x200A ) + NChar( 0x202F ) + NChar( 0x205F ) +
  NChar( 0x3000 ) + NChar( 0x2028 ) + NChar( 0x2029 ) + NChar( 0x0009 ) +
  NChar( 0x000A ) + NChar( 0x000B ) + NChar( 0x000C ) + NChar( 0x000D ) +
  NChar( 0x0085 ) + N']%';
-- NB: The   Len   function does not count trailing spaces.
--     Use   DataLength   instead.
with AnalyzedSamples as (
  select String, DataLength( String ) / DataLength( NChar( 42 ) ) as [StringLength],
    PatIndex( @WhitespacePattern, String ) - 1 as [LeftWhitespace],
    PatIndex( @WhitespacePattern, Reverse( String ) ) - 1 as [RightWhitespace]
  from @Samples ),
  TrimmedSamples as (
  select String, StringLength, [LeftWhitespace], [RightWhitespace],
    case
      when String is NULL then NULL
      when LeftWhitespace = -1 then N''
      else Substring( String, LeftWhitespace + 1, StringLength - LeftWhitespace )
      end as [LTrim],
    case
      when String is NULL then NULL
      when RightWhitespace = -1 then N''
      else Reverse( Substring( Reverse( String ), RightWhitespace + 1, StringLength - RightWhitespace ) )
      end as [RTrim],
    case
      when String is NULL then NULL
      when LeftWhitespace = -1 then N''
      else Substring( String, LeftWhitespace + 1, StringLength - LeftWhitespace - RightWhitespace )
      end as [Trim]
    from AnalyzedSamples )
  select N'"' + String + N'"' as [String], StringLength, [LeftWhitespace], [RightWhitespace],
    N'"' + [LTrim] + N'"' as [LTrim], DataLength( [LTRIM] ) / DataLength( NChar( 42 ) ) as [LTrimLength],
    N'"' + [RTrim] + N'"' as [RTrim], DataLength( [RTRIM] ) / DataLength( NChar( 42 ) ) as [RTrimLength],
    N'"' + [Trim] + N'"' as [Trim], DataLength( [TRIM] ) / DataLength( NChar( 42 ) ) as [TrimLength]
    from TrimmedSamples;

Ответ 2

Мне будет интересно узнать, найдет ли кто-нибудь общее решение SQL.

Лучшее, что я могу придумать, - это простая функция REPLACE:

SELECT MyString = LEFT(MyString, LEN(RTRIM(REPLACE(REPLACE(REPLACE(MyString COLLATE Latin1_General_100_BIN2, NCHAR(9), ' '), NCHAR(12), ' '), NCHAR(13), ' ')))) AS RTrimmed

SELECT MyString = RIGHT(MyString, LEN(LTRIM(REPLACE(REPLACE(REPLACE(MyString COLLATE Latin1_General_100_BIN2, NCHAR(9), ' '), NCHAR(12), ' '), NCHAR(13), ' ')))) AS LTrimmed

и др.

Здесь вы можете получить список текущих пробелов:

http://unicode.org/charts/uca/chart_Whitespace.html

Или, чтобы доказать это самому себе, вы можете экспортировать список всех символов из SQL Server в нечто вроде Excel, очистить символы и импортировать их обратно. Все, что было удалено, было пропущено.