2011-04-26 5 views
8

나는 그것에서 여러 쉼표를 삭제해야 위의 문자열에서 ',,,sam,,bob,' 또는 ',,,' 처럼 내 저장된 프로 시저에서 문자열을 반복 제거, 그것은 'sam,bob,' 또는 경우에만 ',,,' 다음 ''과 같이해야합니다. SQL Server 기능 만 사용해야합니다. SQL Server 2008 및 .Net 3.5를 사용하는 임가 중복 문자

미리 감사드립니다.

+0

데이터를 삽입/전달하기 전에이 작업을 수행 할 수 있습니까? 그렇다면 코드를 사용하여이 작업을 수행하는 것이 더 쉬울 것입니다. –

+0

예쁜 옛 저장 proc. 4k 줄 이상의 코드가 포함되어 있습니다. 그래서 삽입/통과하는 동안 질수있어. SQL Server에서 스칼라 UDF를 사용할 때 – Nash

답변

7

이 독점적으로있는 쉼표 나 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 

그래서 그래서 11,806 자까지 쉼표의 섹션을 처리 할 또 다른 REPLICATE(',',32),',') 단계

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

위쪽 시리즈를 확장하는이 형식에 있습니다.

5

나는 이것을하기 위해 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,', ',') 
+0

이 아닌 UDF를 쓰려면 열어두면 모든 행에 대해 다시 계산 될 것이므로 성능은 "꽤 좋아"않을 것입니다. – Dalex

+0

@Dalex, 귀하의 의견에 감사드립니다. 불행히도 스칼라 UDF 성능 문제가 지나치게 단순화되었습니다. 나는 쿼리가 내 것보다 몇 배 더 느리다는 것을 알면 놀랄 것입니다. 스칼라 UDF는 UDF가 테이블을 사용할 때 종종 성능이 저하됩니다. 이 UDF는 그렇지 않으므로 성능이 좋습니다. 또한 간단하고 이해하기 쉽고 코드 작성이 쉬우 며 디버깅하기 쉽습니다. –

+0

데이터 액세스를하지 않는 UDF의 경우 'WITH SCHEMABINDING' 옵션을 사용하는 것이 가장 좋습니다. UDF가 UPDATE 문 (여기에 설명 된대로) (http://blogs.msdn.com/b/sqlprogrammability/archive/)에서 사용되는 경우 이점이 어떤 방식 으로든 직접적으로 SELECT에 유익하지만 확실한 이점이 있는지 확실하지 않습니다. 2006/05/12/596424.aspx) –

0

솔루션은 잘하지만

  1. 그 것이다 콤마 만
  2. 내가 싫어 루프 기반 TSQL 코드 ;-)
그래서

내가 마르신 솔루션 설정 기반의 보편적 인 코드를 기반으로 쓴 선언 된 모든 종류의 복제본 대체 :

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 

모든 종류의 선언 할 수 있습니다. f 문자열을 중복 문자열에 넣고 대체 문자열을 @Replacement에 넣습니다. 추가 이득 이럴 내가에만 입력 문자열의 최대 길이의 범위 내에서 교체를 검색한다는 것입니다

+0

>> 2.i 싫어하는 루프 기반 TSQL 코드 ;-) 사실이라면 카운터에 대해 rCTE (재귀 CTE)를 사용하는 습관을 벗어나고 싶을 수 있습니다. rCTE는 어떤 것들에는 좋지만 다른 방법들에 비해 계산에 절대적으로 끔찍한데, 그 배경 뒤에는 임시 테이블에서 끔찍한 루프가 있기 때문입니다. 주제에 대한 기사 링크가 있습니다. http://www.sqlservercentral.com/articles/T-SQL/74118/ –

2

SELECT @Parameter AS 'BEFORE' 
BEGIN 
WHILE CHARINDEX(',,', @Parameter) > 0 
    BEGIN 
     SELECT @Parameter = REPLACE(@Parameter, ',,',',') 
    END 
SELECT @Parameter AS 'AFTER' 
END 
1

조지 Mastros 쓴 시도 :


내가 제안 이를 수행하기위한 UDF. 이 테이블을 건드리지 않는다고 제안하기 때문에 성능이 꽤 좋아야합니다.

"메모리 전용"스칼라 UDF는 매우 빠르다는 것에 동의합니다. 실제로 "Initial Caps"문제를 해결 한 George의 Scalar UDF 중 하나를 사용하여 "Set Based"코드 이 항상이 아닌 가장 좋은 방법임을 입증했습니다.

그러나 마틴 스미스 (이 스레드의 또 다른 포스터)는 올바른 방향이었습니다. 이 경우에는 "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 

여기 조지의 훌륭한 기능을 다시 게시 할 필요는 없지만 광산을 게시해야합니다. 다음 함수는 George가하는 것과 같은 결과를냅니다. 그것은 "iTVF"(Inline Table Valued Function)처럼 보이지만 그것은 단지 하나의 값을 반환합니다. 이것이 바로 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(
        @[email protected] 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 

먼저 George 's Scalar UDF를 테스트 해 봅시다. 여기에 SET 통계 시간을 사용하지 않는 이유에 대한 의견을 읽어보십시오. 여기

/****************************************************************************** 
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 

여기

/****************************************************************************** 
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 

... 우리는 "ISF"버전을 실행하는거야, 지금 ...

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) 

"오파 운드 지폐가"실행 것과 수익입니다입니다 그 실행 결과.

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) 

요점은 아닙니다. 조지의 코드가 좋지 않습니다. 전혀. 사실 "단일 쿼리"솔루션이 없을 때 스칼라 UDF를 사용합니다. 또한 모든 "단일 쿼리"솔루션이 항상 최상의 것은 아니라고 말하면서 George를 다시 언급 할 것입니다.

UDF의 경우에는 그만 두지 마십시오. ;-)

+0

ㅎ ... 네. 나는 1 년 이상 된 실에 반응을 보였다. 나는이 시간 이후에도 누군가가 팁을 사용할 수 있기를 바랍니다. –

관련 문제