Удаление повторяющихся дублированных символов - PullRequest
9 голосов
/ 26 апреля 2011

У меня есть строка в моем сохраненном процессе, например ',,,sam,,bob,' или ',,,' из приведенной выше строки я должен удалить несколько запятых из него, он должен выглядеть так 'sam,bob,' или только если ',,,', то ''. Я должен использовать только функции сервера SQL. Я использую Sql Server 2008 и .Net 3.5

Заранее спасибо.

Ответы [ 5 ]

8 голосов
/ 26 апреля 2011

Это работает для строк, которые являются исключительно запятыми или имеют до 398 непрерывных запятых.

 SELECT 
     CASE 
         WHEN TargetString NOT LIKE '%[^,]%' 
             THEN '' /*The string is exclusively commas*/
         ELSE 
            REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(TargetString,
            REPLICATE(',',16),','), /*399/16 = 24 remainder 15*/
            REPLICATE(',',8),','),  /* 39/ 8 =  4 remainder 7*/
            REPLICATE(',',4),','),  /* 11/ 4 =  2 remainder 3*/
            REPLICATE(',',2),','),  /*  5/ 2 =  2 remainder 1*/
            REPLICATE(',',2),',')   /*  3/ 2 =  1 remainder 1*/
         END
 FROM T    

Добавьте дополнительные силы 2 вверху, если вам нужно больше, или уберите сверху, если вам нужно меньше. Комментарии каждого этапа указывают наименьшее число, с которым этот этап не будет иметь дело успешно.

Все строки комментариев в этом формате

/*  L/D    =  Q remainder R */

D:    Corresponds to the length of the string generated by `REPLICATE`
R:    Is always D-1
Q+R:  Form L for the next step

Таким образом, чтобы расширить серию вверх с помощью другой REPLICATE(',',32),',') ступени

D = 32 
R = 31
Q = 368 (399-31)
L = (368 * 32) + 31 = 11807

Так что это будет иметь дело с разделами запятых до 11 806 символов.

6 голосов
/ 26 апреля 2011

Я бы предложил UDF сделать это. Поскольку UDF, который я собираюсь предложить, не касается таблиц, производительность должна быть довольно хорошей.

CREATE Function [dbo].[CleanDuplicates](@Data VarChar(8000), @DuplicateChar VarChar(1))
Returns VarChar(8000)
WITH SCHEMABINDING
AS
Begin

    Set @Data = @DuplicateChar + @Data

    While PATINDEX('%' + @DuplicateChar + @DuplicateChar + '%',@Data) > 0
        Set @Data = REPLACE(@Data, @DuplicateChar + @DuplicateChar,@DuplicateChar)

    Return Right(@Data, Len(@Data)-1)

End

Вы можете проверить функцию следующим образом:

Select dbo.CleanDuplicates(',,,', ',')
Select dbo.CleanDuplicates(',,,sam,,bob,', ',')
2 голосов
/ 24 мая 2012

попробуйте

SELECT @Parameter AS 'BEFORE'
BEGIN
WHILE CHARINDEX(',,', @Parameter) > 0
    BEGIN
        SELECT @Parameter = REPLACE(@Parameter, ',,',',') 
    END
SELECT @Parameter AS 'AFTER'
END
1 голос
/ 07 июля 2012

Джордж Мастрос писал:


Я бы предложил UDF сделать это.Поскольку UDF, который я собираюсь предложить, не касается таблиц, производительность должна быть довольно хорошей.

Я согласен, что Scalar UDF "только для памяти" довольно быстрые.Фактически, я использовал один из скалярных UDF Джорджа, который решил проблему «начальных заглавных букв», чтобы продемонстрировать, что иногда код «на основе набора» ISN'T всегда лучший путь.

Однако Мартин Смит (еще один автор этой темы) определенно был на правильном пути.В этом случае «Set Based» - это еще путь.Конечно, любой может сделать необоснованное заявление о производительности, поэтому давайте подведем итоги с демонстрацией производительности.

Для демонстрации нам сначала понадобятся некоторые тестовые данные.Множество тестовых данных, потому что обе функции, которые мы собираемся протестировать, выполняются очень быстро.Вот код для построения тестовой таблицы с миллионами строк.

--===== Conditionally drop the test table 
     -- to make reruns in SSMS easier
     IF OBJECT_ID('tempdb..#MyHead','U') IS NOT NULL
        DROP TABLE #MyHead
GO
--===== Create and populate the test table on-the-fly.
     -- This builds a bunch of GUIDs and removes the dashes from them to 
     -- increase the chances of duplicating adjacent characters.
     -- Not to worry.  This takes less than 7 seconds to run because of
     -- the "Pseudo Cursor" created by the CROSS JOIN.
 SELECT TOP 1000000
        RowNum     = IDENTITY(INT,1,1),
        SomeString = REPLACE(CAST(NEWID() AS VARCHAR(36)),'-','')
   INTO #MyHead
   FROM sys.all_columns ac1
  CROSS JOIN sys.all_columns ac2
;
GO

Нет необходимости репостить здесь тонкую функцию Джорджа, но мне нужно опубликовать мою.Следующая функция дает тот же результат, что и у Джорджа.Он выглядит как «iTVF» (функция с оценкой встроенной таблицы) и возвращает только одно значение.Вот почему Microsoft называет их «встроенными скалярными функциями» (для краткости я называю их «iSF»).

 CREATE FUNCTION dbo.CleanDuplicatesJBM
        (@Data VARCHAR(8000), @DuplicateChar VARCHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN 
 SELECT Item =  STUFF(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
                    @DuplicateChar+@Data COLLATE LATIN1_GENERAL_BIN,
                REPLICATE(@DuplicateChar,33),@DuplicateChar),
                REPLICATE(@DuplicateChar,17),@DuplicateChar),
                REPLICATE(@DuplicateChar, 9),@DuplicateChar),
                REPLICATE(@DuplicateChar, 5),@DuplicateChar),
                REPLICATE(@DuplicateChar, 3),@DuplicateChar),
                REPLICATE(@DuplicateChar, 2),@DuplicateChar),
                REPLICATE(@DuplicateChar, 2),@DuplicateChar)
                ,1,1,'')
;
GO

Сначала давайте проверим скалярный UDF Джорджа.Пожалуйста, прочитайте комментарии о том, почему мы не используем SET STATISTICS TIME ON здесь.

/******************************************************************************
 Test George's code.
 Since Scalar Functions don't work well with SET STATISTICS TIME ON, we measure
 duration a different way.  We'll also throw away the result in a "Bit Bucket"
 variable because we're trying to measure the performance of the function 
 rather than how long it takes to display or store results.
******************************************************************************/
--===== Declare some obviously named variables
DECLARE @StartTime DATETIME,
        @BitBucket VARCHAR(36)
;
--===== Start the "Timer"
 SELECT @StartTime = GETDATE()
;
--===== Run the test on the function
 SELECT @BitBucket = [dbo].[CleanDuplicates](SomeString,'A')
   FROM #MyHead
;
--===== Display the duration in milliseconds
  PRINT DATEDIFF(ms,@StartTime,GETDATE())
;
--===== Run the test a total of 5 times
GO 5

Вот результаты этого «пятикратного» запуска ...

Beginning execution loop
15750
15516
15543
15480
15510
Batch execution completed 5 times.
(Average is 15,559 on my 10 year old, single 1.8Ghz CPU)

Теперь,мы запустим версию "iSF" ...

/******************************************************************************
 Test Jeff's code.
 Even though this uses an "iSF" (Inline Scalar Function), we'll test exactly
 the same way that we tested George's code so we're comparing apples-to-apples.
 This includes throwing away the result in a "Bit Bucket" variable because 
 we're trying to measure the performance of the function rather than how long 
 it takes to display or store results.
******************************************************************************/
--===== Declare some obviously named variables
DECLARE @StartTime DATETIME,
        @BitBucket VARCHAR(36)
;
--===== Start the "Timer"
 SELECT @StartTime = GETDATE()
;
--===== Run the test on the function
 SELECT @BitBucket = cleaned.ITEM
   FROM #MyHead
  CROSS APPLY [dbo].[CleanDuplicatesJBM](SomeString,'A') cleaned
;
--===== Display the duration in milliseconds
  PRINT DATEDIFF(ms,@StartTime,GETDATE())
;
--===== Run the test a total of 5 times
GO 5

Вот результаты этого прогона.

Beginning execution loop
6856
6810
7020
7350
6996
Batch execution completed 5 times.
(Average is 7,006 {more than twice as fast} on my 10 year old, single 1.8Ghz CPU)

Я не считаю, что код Джорджа плохой.Не за что.На самом деле, я использую Scalar UDF, когда нет решения «одного запроса».Я также констатирую и поддерживаю Джорджа, говоря, что не все решения с «одним запросом» всегда являются лучшими.

Просто не переставайте искать их, когда дело доходит до UDF.; -)

0 голосов
/ 27 апреля 2011

Ваши решения хороши, но

  1. это только запятая
  2. я ненавижу код TSQL на основе циклов; -)

поэтому я написал на основев универсальном коде на основе набора решений Marcin для замены всех объявленных видов дубликатов:

DECLARE @Duplicate NVARCHAR(100)= '#$'
DECLARE @TestString NVARCHAR(MAX)= 'test_test__f##f2$$g'
DECLARE @Replacement NVARCHAR(MAX)= ''
DECLARE @OutputString NVARCHAR(MAX)= @teststring ;
WITH    numbers
          AS ( SELECT   ROW_NUMBER() OVER ( ORDER BY o.object_id, o2.object_id ) Number
               FROM     sys.objects o
                        CROSS JOIN sys.objects o2
             ),
        chars
          AS ( SELECT   SUBSTRING(@Duplicate, 1, 1) CHAR ,
                        CAST(1 AS INT) [LEVEL]
               UNION ALL
               SELECT   SUBSTRING(@Duplicate, numbers.Number, 1) CHAR ,
                        CAST(numbers.Number AS INT) [LEVEL]
               FROM     numbers
                        JOIN chars ON chars.Level + 1 = numbers.Number
               WHERE    LEN(SUBSTRING(@Duplicate, numbers.Number, 1)) > 0
             ),
        Replicated
          AS ( SELECT   REPLICATE(CHAR, numbers.number) Repl ,
                        numbers.Number
               FROM     chars
                        CROSS JOIN numbers
             )
    SELECT  @OutputString = REPLACE(@OutputString, Repl, @Replacement)
    FROM    replicated
    WHERE   number <= LEN(@TestString)

SELECT  @OutputString

Вы можете объявить каждый тип символа в строке Duplicate и каждую строку замены в @Replacement.Дополнительный выигрыш ИМХО в том, что я ищу замену только в диапазоне максимальной длины входной строки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...