Обрезать пробелы (пробелы, табуляции, новые строки)


10

Я нахожусь на SQL Server 2014, и мне нужно очистить пробел от начала и конца содержимого столбца, где пробелами могут быть простые пробелы, табуляции или переводы строк (как \nи \r\n); например

'    this content    '                          should become 'this content'
'  \r\n   \t\t\t this \r\n content \t  \r\n   ' should become 'this \r\n content'

и так далее.

Я смог добиться только первого случая с

UPDATE table t SET t.column = LTRIM(RTRIM(t.column))

но для других случаев это не работает.

Ответы:


8

Для тех, кто использует SQL Server 2017 или новее

Вы можете использовать встроенную функцию TRIM . Например:

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~'
        + TRIM(NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A) FROM @Test)
        + N'~';

Обратите внимание, что по умолчанию TRIMиспользуется только удаление пробелов, поэтому для удаления вкладок и новых строк (CR + LF) необходимо указать это characters FROMпредложение.

Кроме того, я использовал NCHAR(0x09)символы табуляции в @Testпеременной, чтобы пример кода можно было скопировать и вставить и сохранить правильные символы. В противном случае вкладки преобразуются в пробелы при визуализации этой страницы.

Для тех, кто использует SQL Server 2016 или старше

Вы можете создать функцию в виде SQLCLR Scalar UDF или T-SQL Inline TVF (iTVF). TF SQL Inline TVF будет выглядеть следующим образом:

CREATE
--ALTER
FUNCTION dbo.TrimChars(@OriginalString NVARCHAR(4000), @CharsToTrim NVARCHAR(50))
RETURNS TABLE
WITH SCHEMABINDING
AS RETURN
WITH cte AS
(
  SELECT PATINDEX(N'%[^' + @CharsToTrim + N']%', @OriginalString) AS [FirstChar],
         PATINDEX(N'%[^' + @CharsToTrim + N']%', REVERSE(@OriginalString)) AS [LastChar],
        LEN(@OriginalString + N'~') - 1 AS [ActualLength]
)
SELECT cte.[ActualLength],
       [FirstChar],
       ((cte.[ActualLength] - [LastChar]) + 1) AS [LastChar],
       SUBSTRING(@OriginalString, [FirstChar],
                 ((cte.[ActualLength] - [LastChar]) - [FirstChar] + 2)) AS [FixedString]
FROM   cte;
GO

И работает так:

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~' + tc.[FixedString] + N'~' AS [proof]
FROM   dbo.TrimChars(@Test, NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A)) tc;

Возвращает:

proof
----
~this 
              content~

И вы можете использовать это в UPDATEиспользовании CROSS APPLY:

UPDATE tbl
SET    tbl.[Column] = itvf.[FixedString]
FROM   SchemaName.TableName tbl
CROSS APPLY  dbo.TrimChars(tbl.[Column],
                           NCHAR(0x09) + NCHAR(0x20) + NCHAR(0x0D) + NCHAR(0x0A)) itvf

Как упоминалось в начале, это также действительно легко с помощью SQLCLR, поскольку .NET включает Trim()метод, который выполняет именно ту операцию, которую вы хотите. Вы можете либо написать свой собственный код для вызова SqlString.Value.Trim(), либо вы можете просто установить бесплатную версию библиотеки SQL # (которую я создал, но эта функция есть в бесплатной версии) и использовать либо String_Trim (который выполняет только пробел), либо String_TrimChars, где Вы передаете символы для обрезки с обеих сторон (так же, как iTVF, показанный выше).

DECLARE @Test NVARCHAR(4000);
SET @Test = N'  
    ' + NCHAR(0x09) + N'  ' + NCHAR(0x09) + N' this 
 ' + NCHAR(0x09) + NCHAR(0x09) + N'  content' + NCHAR(0x09) + NCHAR(0x09) + N'  
' + NCHAR(0x09) + N' ' + NCHAR(0x09) + NCHAR(0x09) + N'     ';

SELECT N'~' + SQL#.String_Trim(@Test) + N'~' AS [proof];

И он возвращает точно такую ​​же строку, как показано выше в выходных данных примера iTVF. Но, будучи скалярным UDF, вы должны использовать его следующим образом UPDATE:

UPDATE tbl
SET    tbl.[Column] = SQL#.String_Trim(itvf.[Column])
FROM   SchemaName.TableName tbl

Любой из вышеперечисленных должен быть эффективным для использования через миллионы строк. Встроенные TVF оптимизируются в отличие от многократных операторов TVF и скалярных пользовательских функций T-SQL. Кроме того, скалярные пользовательские функции SQLCLR могут использоваться в параллельных планах, если они помечены как IsDeterministic=trueи не установлены ни для одного типа DataAccess Read(по умолчанию для доступа к данным пользователя и системы None), и оба эти условия true для обеих функций SQLCLR, указанных выше.


4

Возможно, вы захотите использовать TVF (таблично-значная функция), чтобы удалить оскорбительные символы из начала и конца ваших данных.

Создайте таблицу для хранения тестовых данных:

IF COALESCE(OBJECT_ID('dbo.TrimTest'), 0) <> 0
BEGIN
    DROP TABLE dbo.TrimTest;
END
CREATE TABLE dbo.TrimTest
(
    SampleData VARCHAR(50) NOT NULL
);

INSERT INTO dbo.TrimTest (SampleData)
SELECT CHAR(13) + CHAR(10) + CHAR(9) + 'this is ' + CHAR(13) + CHAR(10) + ' a test' + CHAR(13) + CHAR(10);
GO

Создайте TVF:

IF COALESCE(OBJECT_ID('dbo.StripCrLfTab'), 0) <> 0
BEGIN
    DROP FUNCTION dbo.StripCrLfTab;
END
GO
CREATE FUNCTION dbo.StripCrLfTab
(
    @val NVARCHAR(1000)
)
RETURNS @Results TABLE
(
    TrimmedVal NVARCHAR(1000) NULL
)
AS
BEGIN
    DECLARE @TrimmedVal NVARCHAR(1000);
    SET @TrimmedVal = CASE WHEN RIGHT(@val, 1) = CHAR(13) OR RIGHT(@val, 1) = CHAR(10) OR RIGHT(@val, 1) = CHAR(9)
            THEN LEFT(
                CASE WHEN LEFT(@val, 1) = CHAR(13) OR LEFT(@val, 1) = CHAR(10) OR LEFT(@val, 1) = CHAR(9)
                THEN RIGHT(@val, LEN(@val) - 1)
                ELSE @val
                END
                , LEN(@val) -1 )
            ELSE
                CASE WHEN LEFT(@val, 1) = CHAR(13) OR LEFT(@val, 1) = CHAR(10) OR LEFT(@val, 1) = CHAR(9)
                THEN RIGHT(@val, LEN(@val) - 1)
                ELSE @val
                END
            END;
    IF @TrimmedVal LIKE (CHAR(13) + '%')
        OR @TrimmedVal LIKE (CHAR(10) + '%')
        OR @TrimmedVal LIKE (CHAR(9) + '%')
        OR @TrimmedVal LIKE ('%' + CHAR(13))
        OR @TrimmedVal LIKE ('%' + CHAR(10))
        OR @TrimmedVal LIKE ('%' + CHAR(9))
        SELECT @TrimmedVal = tv.TrimmedVal
        FROM dbo.StripCrLfTab(@TrimmedVal) tv;
    INSERT INTO @Results (TrimmedVal)
    VALUES (@TrimmedVal);
    RETURN;
END;
GO

Запустите TVF, чтобы показать результаты:

SELECT tt.SampleData
    , stt.TrimmedVal
FROM dbo.TrimTest tt
CROSS APPLY dbo.StripCrLfTab(tt.SampleData) stt;

Полученные результаты:

введите описание изображения здесь

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

Вы можете использовать это в заявлении об обновлении:

UPDATE dbo.TrimTest
SET TrimTest.SampleData = stt.TrimmedVal
FROM dbo.TrimTest tt
CROSS APPLY dbo.StripCrLfTab(tt.SampleData) stt;


SELECT *
FROM dbo.TrimTest;

Результаты (как текст):

введите описание изображения здесь


Спасибо, Макс, к сожалению, мне нужно очистить большое количество строк (миллионов) в нескольких таблицах, я надеялся, что какая-то функция будет использоваться в UPDATEзапросе, например LTRIM/ RTRIM, что-то в строках UPDATE table t SET t.column = TRIM(t.column, CONCAT(CHAR(9), CHAR(10), CHAR(13)))с TRIM( expression, charlist )функцией, принимающей список символов для обрезки как и многие языки сценариев.
Джованни Ловато

Предупреждение, которое я дал об этом, «вероятно», не работает должным образом над множеством строк, может быть или не быть проблемой. Если вы делаете это только один раз, то это может не быть проблемой. Возможно, вы захотите протестировать его в непроизводственной среде, чтобы увидеть, сколько времени это займет.
Макс Вернон

Я обновлю свой ответ, чтобы показать, как вы будете использовать это в updateутверждении.
Макс Вернон

1

У меня просто была проблема с этой конкретной ситуацией, мне нужно было найти и очистить каждое поле с пробелами, но я нашел 4 типа возможных пробелов в полях базы данных (ссылка на таблицу кодов ASCII):

  • Горизонтальная вкладка (символ (9))
  • Новая Линия (char (10))
  • Вертикальная вкладка (символ (9))
  • Пространство (символ (32))

Может быть, этот запрос может помочь вам.

UPDATE @TABLE SET @COLUMN = replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')

Это также очищает пробел от середины полей, а не только от начала и до конца, как указано в вопросе.
Colin 't Hart

Да, вы правы, я буду редактировать
sami.almasagedi

-1

Вам придется проанализировать второй пример, потому что LTRIM / RTRIM только обрезают пробелы. Вы действительно хотите обрезать то, что SQL считает данными (/ r, / t и т. Д.). Если вы знаете значения, которые ищете, просто используйте REPLACE для их замены. Еще лучше написать функцию и вызвать ее.


-1

Если хотите, используйте мою элегантную функцию:

CREATE FUNCTION s_Trim
(
    @s nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
    -- Create comparators for LIKE operator
    DECLARE @whitespaces nvarchar(50) = CONCAT('[ ', CHAR(9), CHAR(10), CHAR(13), ']'); -- Concat chars that you consider as whitespaces
    DECLARE @leftComparator nvarchar(50) = @whitespaces + '%',
            @rightComparator nvarchar(50) = '%' + @whitespaces;
    -- LTRIM
    WHILE @s LIKE @leftComparator AND LEN(@s + 'x') > 1 SET @s = RIGHT(@s, LEN(@s + 'x') - 2)
    -- RTRIM
    WHILE @s LIKE @rightComparator AND LEN(@s + 'x') > 1 SET @s = LEFT(@s, LEN(@s + 'x') - 2)

    RETURN @s;
END
GO

1
Скалярные функции вряд ли изящны. Они заставляют запросы выполняться последовательно и выполняться один раз для строки (не один раз для запроса). Вместо этого вы должны взглянуть на встроенные табличные функции.
Эрик Дарлинг

-2

Использование функции для больших данных может занять много времени. У меня есть набор данных из 8 миллионов строк, выполнение функции заняло более 30 минут. replace(replace(replace(replace(@COLUMN,CHAR(9),''),CHAR(10),''),CHAR(13),''),CHAR(32),'')заняло всего 5 сек. Спасибо всем. Я вижу тебя @ sami.almasagedi и @Colin 't Hart


Как и в ответе, который вы повторяете, это не решит проблему, если пробел между первым и последним непробельным символом должен быть сохранен. Скорость полезна только тогда, когда она приводит к желаемому ответу. Также - см. Примечания в принятом ответе о том, как гарантировать, что функции не замедляют запрос, подобный этому.
RDFozz
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.