Jak usunąć wszystkie znaki niealfabetyczne z ciągu w programie SQL Server?


Odpowiedzi:


362

Wypróbuj tę funkcję:

Create Function [dbo].[RemoveNonAlphaCharacters](@Temp VarChar(1000))
Returns VarChar(1000)
AS
Begin

    Declare @KeepValues as varchar(50)
    Set @KeepValues = '%[^a-z]%'
    While PatIndex(@KeepValues, @Temp) > 0
        Set @Temp = Stuff(@Temp, PatIndex(@KeepValues, @Temp), 1, '')

    Return @Temp
End

Nazwij to tak:

Select dbo.RemoveNonAlphaCharacters('abc1234def5678ghi90jkl')

Gdy zrozumiesz kod, zobaczysz, że stosunkowo łatwo jest go zmienić, aby usunąć również inne znaki. Możesz nawet uczynić to wystarczająco dynamicznym, aby pasował do Twojego wzorca wyszukiwania.

Mam nadzieję, że to pomoże.


9
Ten kod usuwa znaki inne niż alfa (więc liczby są również usuwane). Jeśli chcesz zostawić liczby (usunąć znaki inne niż alfanumeryczne), to ... zamień ^ az na ^ az ^ 0-9 Ten ciąg wyszukiwania pojawia się w kodzie w dwóch różnych miejscach. Pamiętaj, aby wymienić oba.
George Mastros

26
Z komentarza Jeffa: Myślę, że gdybyś chciał usunąć wszystkie nieliterowe i nieliczbowe, chciałbyś '^ a-z0-9' (w przeciwieństwie do '^ az ^ 0-9', które pozostawiłoby ^ w ciągu) .
Nawet Mien

1
+1 George. Jest to jedno z tych miejsc, w których kod oparty na zestawie i użycie wbudowanych funkcji skalarnych ma duże trudności w pokonaniu wiersza po wierszu. Ładnie wykonane. Od kilku lat używam również funkcji „Initial Caps”, która ma tę samą podstawową formę.
Jeff Moden,

6
@Lynchie Change '% [^ az]%' To '% [^ az]%' Po prostu wstaw spację po z.
George Mastros

8
Nazwa zmiennej KeepValues ​​jest w rzeczywistości przeciwieństwem tego, co ma robić. KeepValues ​​zawiera listę znaków, które należy wykluczyć ..
nee21

167

Parametryzowane wersja G Mastros ' niesamowitej odpowiedzi :

CREATE FUNCTION [dbo].[fn_StripCharacters]
(
    @String NVARCHAR(MAX), 
    @MatchExpression VARCHAR(255)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
    SET @MatchExpression =  '%['+@MatchExpression+']%'

    WHILE PatIndex(@MatchExpression, @String) > 0
        SET @String = Stuff(@String, PatIndex(@MatchExpression, @String), 1, '')

    RETURN @String

END

Tylko alfabetycznie:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', '^a-z')

Tylko numeryczne:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', '^0-9')

Tylko alfanumeryczne:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', '^a-z0-9')

Niealfanumeryczne:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', 'a-z0-9')

3
Wolę tę wersję i stworzyłem adaptację odpowiedzi G Mastrosa przed przewinięciem w dół i zagłosowaniem!
zarabiaj

Wydaje się, że wzorzec wyrażenia regularnego nie działa ze wszystkimi białymi znakami. Jeśli chcę usunąć wszystkie znaki specjalne z wyjątkiem znaków alfanumerycznych i białych znaków, spodziewam się, że użyję SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9\s')nadal usuwających białe znaki. Próbowałem też użyć, [[:blank:]]ale to psuje funkcję i nic nie jest usuwane z ciągu. Najbliższe otrzymane Ive to użycie: SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9 ')(zakodowanie spacji we wzorcu wyrażenia regularnego). Jednak to nie usuwa podziałów wierszy.
Billy McKee

2
@BillyMcKee Dodaj spację na początku zamiast na końcu wyrażenia regularnego. SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^ a-z0-9')
Mike

8

Wierz lub nie, ale w moim systemie ta brzydka funkcja sprawdza się lepiej niż elegancka G Mastros.

CREATE FUNCTION dbo.RemoveSpecialChar (@s VARCHAR(256)) 
RETURNS VARCHAR(256) 
WITH SCHEMABINDING
    BEGIN
        IF @s IS NULL
            RETURN NULL
        DECLARE @s2 VARCHAR(256) = '',
                @l INT = LEN(@s),
                @p INT = 1

        WHILE @p <= @l
            BEGIN
                DECLARE @c INT
                SET @c = ASCII(SUBSTRING(@s, @p, 1))
                IF @c BETWEEN 48 AND 57
                   OR  @c BETWEEN 65 AND 90
                   OR  @c BETWEEN 97 AND 122
                    SET @s2 = @s2 + CHAR(@c)
                SET @p = @p + 1
            END

        IF LEN(@s2) = 0
            RETURN NULL

        RETURN @s2

a co ze zwykłymi przecinkami, kropkami, spacjami itp.?
sojim

ile się różni, jeśli nie użyjesz ASCIItutaj liczby całkowitej i porównaj bezpośrednio wynik SUBSTRINGz niektórymi SET @ch=SUBSTRING(@s, @p, 1)IF @ch BETWEEN '0' AND '9' OR @ch BETWEEN 'a' AND 'z' OR @ch BETWEEN 'A' AND 'Z' ...
znakami

Dodaj WITH SCHEMABINDING do jego funkcji, tak jak ma to miejsce w Twojej funkcji. Używasz VARCHAR, jego funkcja używa NVARCHAR. Jeśli parametry, które przekazujesz do jego funkcji to VARCHAR, powinieneś użyć VARCHAR zamiast NVARCHAR w jego funkcji, w przeciwnym razie twój system będzie musiał rzutować wartości ciągów z VARCHAR na NVARCHAR, zanim będzie mógł wykonać droższą funkcję. Nawet przy tych zmianach twoja funkcja może nadal działać szybciej, ale to kilka przykładów, które widzę, w których jego funkcja może działać wolniej w twojej sytuacji.
EricI

1
Jego funkcja również używa NVARCHAR (MAX), a twoja funkcja używa VARCHAR (256). Jeśli 256 to wszystko, czego potrzebujesz, zmień jego funkcję, aby również używała VARCHAR (256), a jego funkcja będzie działać szybciej.
EricI

5

Wiedziałem, że SQL źle radzi sobie z manipulacją ciągami, ale nie sądziłem, że będzie to takie trudne. Oto prosta funkcja do usunięcia wszystkich liczb z ciągu. Byłoby na to lepsze sposoby, ale to jest początek.

CREATE FUNCTION dbo.AlphaOnly (
    @String varchar(100)
)
RETURNS varchar(100)
AS BEGIN
  RETURN (
    REPLACE(
      REPLACE(
        REPLACE(
          REPLACE(
            REPLACE(
              REPLACE(
                REPLACE(
                  REPLACE(
                    REPLACE(
                      REPLACE(
                        @String,
                      '9', ''),
                    '8', ''),
                  '7', ''),
                '6', ''),
              '5', ''),
            '4', ''),
          '3', ''),
        '2', ''),
      '1', ''),
    '0', '')
  )
END
GO

-- ==================
DECLARE @t TABLE (
    ColID       int,
    ColString   varchar(50)
)

INSERT INTO @t VALUES (1, 'abc1234567890')

SELECT ColID, ColString, dbo.AlphaOnly(ColString)
FROM @t

Wynik

ColID ColString
----- ------------- ---
    1 abc1234567890 abc

Runda 2 - Czarna lista oparta na danych

-- ============================================
-- Create a table of blacklist characters
-- ============================================
IF EXISTS (SELECT * FROM sys.tables WHERE [object_id] = OBJECT_ID('dbo.CharacterBlacklist'))
  DROP TABLE dbo.CharacterBlacklist
GO
CREATE TABLE dbo.CharacterBlacklist (
    CharID              int         IDENTITY,
    DisallowedCharacter nchar(1)    NOT NULL
)
GO
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'0')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'1')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'2')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'3')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'4')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'5')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'6')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'7')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'8')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'9')
GO

-- ====================================
IF EXISTS (SELECT * FROM sys.objects WHERE [object_id] = OBJECT_ID('dbo.StripBlacklistCharacters'))
  DROP FUNCTION dbo.StripBlacklistCharacters
GO
CREATE FUNCTION dbo.StripBlacklistCharacters (
    @String nvarchar(100)
)
RETURNS varchar(100)
AS BEGIN
  DECLARE @blacklistCt  int
  DECLARE @ct           int
  DECLARE @c            nchar(1)

  SELECT @blacklistCt = COUNT(*) FROM dbo.CharacterBlacklist

  SET @ct = 0
  WHILE @ct < @blacklistCt BEGIN
    SET @ct = @ct + 1

    SELECT @String = REPLACE(@String, DisallowedCharacter, N'')
    FROM dbo.CharacterBlacklist
    WHERE CharID = @ct
  END

  RETURN (@String)
END
GO

-- ====================================
DECLARE @s  nvarchar(24)
SET @s = N'abc1234def5678ghi90jkl'

SELECT
    @s                  AS OriginalString,
    dbo.StripBlacklistCharacters(@s)   AS ResultString

Wynik

OriginalString           ResultString
------------------------ ------------
abc1234def5678ghi90jkl   abcdefghijkl

Moje wyzwanie dla czytelników: czy możesz uczynić to bardziej wydajnym? A co z rekurencją?


prawdopodobnie mógłbyś napisać lepszą dbo.StripBlacklistCharacters () bez pętli, używając sommarskog.se/arrays-in-sql-2005.html#tblnum tabeli liczb dołączonych do twojej czarnej listy, ale jestem dziś zbyt leniwy, aby to wypróbować siebie ....
KM.

4

Jeśli jesteś podobny do mnie i nie masz dostępu do dodawania funkcji do swoich danych produkcyjnych, ale nadal chcesz wykonać tego rodzaju filtrowanie, oto czyste rozwiązanie SQL wykorzystujące tabelę PIVOT do ponownego złożenia przefiltrowanych elementów.

Uwaga : zakodowałem na stałe tabelę do 40 znaków, będziesz musiał dodać więcej, jeśli masz dłuższe ciągi do filtrowania.

SET CONCAT_NULL_YIELDS_NULL OFF;

with 
    ToBeScrubbed
as (
    select 1 as id, '*SOME 222@ !@* #* BOGUS !@*&! DATA' as ColumnToScrub
),

Scrubbed as (
    select 
        P.Number as ValueOrder,
        isnull ( substring ( t.ColumnToScrub , number , 1 ) , '' ) as ScrubbedValue,
        t.id
    from
        ToBeScrubbed t
        left join master..spt_values P
            on P.number between 1 and len(t.ColumnToScrub)
            and type ='P'
    where
        PatIndex('%[^a-z]%', substring(t.ColumnToScrub,P.number,1) ) = 0
)

SELECT
    id, 
    [1]+ [2]+ [3]+ [4]+ [5]+ [6]+ [7]+ [8] +[9] +[10]
    +  [11]+ [12]+ [13]+ [14]+ [15]+ [16]+ [17]+ [18] +[19] +[20]
    +  [21]+ [22]+ [23]+ [24]+ [25]+ [26]+ [27]+ [28] +[29] +[30]
    +  [31]+ [32]+ [33]+ [34]+ [35]+ [36]+ [37]+ [38] +[39] +[40] as ScrubbedData
FROM (
    select 
        *
    from 
        Scrubbed
    ) 
    src
    PIVOT (
        MAX(ScrubbedValue) FOR ValueOrder IN (
        [1], [2], [3], [4], [5], [6], [7], [8], [9], [10],
        [11], [12], [13], [14], [15], [16], [17], [18], [19], [20],
        [21], [22], [23], [24], [25], [26], [27], [28], [29], [30],
        [31], [32], [33], [34], [35], [36], [37], [38], [39], [40]
        )
    ) pvt

To rozwiązanie było dla mnie 2,3 razy szybsze niż użycie funkcji na zbiorze 235 tys. Wierszy. Musiałem też zrobić 2x wymiany i użyłem w sumie czterech CTE. Pracował jak mistrz.
JJS

4

Po przyjrzeniu się wszystkim przedstawionym rozwiązaniom pomyślałem, że musi istnieć czysta metoda SQL, która nie wymaga funkcji ani zapytania CTE / XML i nie wymaga trudnego utrzymania zagnieżdżonych instrukcji REPLACE. Oto moje rozwiązanie:

SELECT 
  x
  ,CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 1, 1) + '%' THEN '' ELSE SUBSTRING(x, 1, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 2, 1) + '%' THEN '' ELSE SUBSTRING(x, 2, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 3, 1) + '%' THEN '' ELSE SUBSTRING(x, 3, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 4, 1) + '%' THEN '' ELSE SUBSTRING(x, 4, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 5, 1) + '%' THEN '' ELSE SUBSTRING(x, 5, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 6, 1) + '%' THEN '' ELSE SUBSTRING(x, 6, 1) END
-- Keep adding rows until you reach the column size 
    AS stripped_column
FROM (SELECT 
        column_to_strip AS x
        ,'ABCDEFGHIJKLMNOPQRSTUVWXYZ' AS a 
      FROM my_table) a

Zaletą zrobienia tego w ten sposób jest to, że prawidłowe znaki są zawarte w jednym ciągu w zapytaniu podrzędnym, co ułatwia rekonfigurację dla innego zestawu znaków.

Wadą jest to, że musisz dodać wiersz SQL dla każdego znaku, aż do rozmiaru kolumny. Aby ułatwić to zadanie, użyłem tylko poniższego skryptu Powershell, w tym przykładzie dla VARCHAR (64):

1..64 | % {
  "    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, {0}, 1) + '%' THEN '' ELSE SUBSTRING(x, {0}, 1) END" -f $_
} | clip.exe

3
Niezręczne w ogólnym przypadku, ale łatwe i przydatne w przypadku jednorazowego zapytania z wąską kolumną.
Eric J.

3

Oto inny sposób usuwania znaków niealfabetycznych za pomocą rozszerzenia iTVF. Po pierwsze, potrzebujesz rozdzielacza ciągów opartego na wzorze. Oto jeden zaczerpnięty z artykułu Dwaina Campa :

-- PatternSplitCM will split a string based on a pattern of the form 
-- supported by LIKE and PATINDEX 
-- 
-- Created by: Chris Morris 12-Oct-2012 
CREATE FUNCTION [dbo].[PatternSplitCM]
(
       @List                VARCHAR(8000) = NULL
       ,@Pattern            VARCHAR(50)
) RETURNS TABLE WITH SCHEMABINDING 
AS 

RETURN
    WITH numbers AS (
        SELECT TOP(ISNULL(DATALENGTH(@List), 0))
            n = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
        FROM
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) e (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) f (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) g (n)
    )

    SELECT
        ItemNumber = ROW_NUMBER() OVER(ORDER BY MIN(n)),
        Item = SUBSTRING(@List,MIN(n),1+MAX(n)-MIN(n)),
        [Matched]
    FROM (
        SELECT n, y.[Matched], Grouper = n - ROW_NUMBER() OVER(ORDER BY y.[Matched],n)
        FROM numbers
        CROSS APPLY (
            SELECT [Matched] = CASE WHEN SUBSTRING(@List,n,1) LIKE @Pattern THEN 1 ELSE 0 END
        ) y
    ) d
    GROUP BY [Matched], Grouper

Teraz, gdy masz rozdzielacz oparty na wzorcu, musisz podzielić ciągi pasujące do wzorca:

[a-z]

a następnie połącz je z powrotem, aby uzyskać pożądany wynik:

SELECT *
FROM tbl t
CROSS APPLY(
    SELECT Item + ''
    FROM dbo.PatternSplitCM(t.str, '[a-z]')
    WHERE Matched = 1
    ORDER BY ItemNumber
    FOR XML PATH('')
) x (a)

PRÓBA

Wynik:

| Id |              str |              a |
|----|------------------|----------------|
|  1 |    testte d'abc |     testtedabc |
|  2 |            anr¤a |           anra |
|  3 |  gs-re-C“te d'ab |     gsreCtedab |
|  4 |         Mfe, DF |          MfeDF |
|  5 |           Rtemd |          Rtemd |
|  6 |          jadji |          jadji |
|  7 |      Cje y ret¢n |       Cjeyretn |
|  8 |        Jklbalu |        Jklbalu |
|  9 |       lene-iokd |       leneiokd |
| 10 |   liode-Pyrnie |    liodePyrnie |
| 11 |         Vs Gta |          VsGta |
| 12 |        Sƒo Paulo |        SoPaulo |
| 13 |  vAstra gAtaland | vAstragAtaland |
| 14 |  ¥uble / Bio-Bio |     ubleBioBio |
| 15 | Upln/ds VAsb-y |    UplndsVAsby |

czy jest jakaś przewaga nad innymi odpowiedziami?
S.Serpooshan

2

To rozwiązanie, zainspirowane rozwiązaniem pana Allena, wymaga Numberstabeli liczb całkowitych (którą powinieneś mieć pod ręką, jeśli chcesz wykonywać poważne operacje zapytań z dobrą wydajnością). Nie wymaga CTE. Możesz zmienić NOT IN (...)wyrażenie, aby wykluczyć określone znaki, lub zmienić je na wyrażenie IN (...)OR, LIKEaby zachować tylko określone znaki.

SELECT (
    SELECT  SUBSTRING([YourString], N, 1)
    FROM    dbo.Numbers
    WHERE   N > 0 AND N <= CONVERT(INT, LEN([YourString]))
        AND SUBSTRING([YourString], N, 1) NOT IN ('(',')',',','.')
    FOR XML PATH('')
) AS [YourStringTransformed]
FROM ...

Ciekawe rozwiązanie niepowiązanego problemu.
TaterJuice,

2

Oto rozwiązanie, które nie wymaga tworzenia funkcji ani wyświetlania wszystkich wystąpień znaków do zastąpienia. Używa rekurencyjnej instrukcji WITH w połączeniu z PATINDEX, aby znaleźć niechciane znaki. Zastąpi wszystkie niechciane znaki w kolumnie - do 100 unikalnych złych znaków zawartych w dowolnym podanym ciągu. (Np. „ABC123DEF234” zawierałoby 4 złe znaki 1, 2, 3 i 4) Limit 100 to maksymalna liczba rekursji dozwolona w instrukcji WITH, ale nie narzuca to ograniczenia liczby wierszy do przetworzenia, co jest ograniczona tylko dostępną pamięcią.
Jeśli nie chcesz DISTINCT wyników, możesz usunąć dwie opcje z kodu.

-- Create some test data:
SELECT * INTO #testData 
FROM (VALUES ('ABC DEF,K.l(p)'),('123H,J,234'),('ABCD EFG')) as t(TXT)

-- Actual query:
-- Remove non-alpha chars: '%[^A-Z]%'
-- Remove non-alphanumeric chars: '%[^A-Z0-9]%'
DECLARE @BadCharacterPattern VARCHAR(250) = '%[^A-Z]%';

WITH recurMain as (
    SELECT DISTINCT CAST(TXT AS VARCHAR(250)) AS TXT, PATINDEX(@BadCharacterPattern, TXT) AS BadCharIndex
    FROM #testData
    UNION ALL
    SELECT CAST(TXT AS VARCHAR(250)) AS TXT, PATINDEX(@BadCharacterPattern, TXT) AS BadCharIndex
    FROM (
        SELECT 
            CASE WHEN BadCharIndex > 0 
                THEN REPLACE(TXT, SUBSTRING(TXT, BadCharIndex, 1), '')
                ELSE TXT 
            END AS TXT
        FROM recurMain
        WHERE BadCharIndex > 0
    ) badCharFinder
)
SELECT DISTINCT TXT
FROM recurMain
WHERE BadCharIndex = 0;

1

Umieściłem to w obu miejscach, w których nazywa się PatIndex.

PatIndex('%[^A-Za-z0-9]%', @Temp)

dla funkcji niestandardowej powyżej RemoveNonAlphaCharacters i zmieniono jej nazwę RemoveNonAlphaNumericCharacters


1

- Najpierw utwórz jedną funkcję

CREATE FUNCTION [dbo].[GetNumericonly]
(@strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
     DECLARE @intAlpha INT
     SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric)
BEGIN
     WHILE @intAlpha > 0
   BEGIN
          SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
          SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric )
   END
END
RETURN ISNULL(@strAlphaNumeric,0)
END

Teraz wywołaj tę funkcję jak

select [dbo].[GetNumericonly]('Abhi12shek23jaiswal')

Jego wynik jak

1223

1

Z punktu widzenia wydajności użyłbym funkcji Inline:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[udf_RemoveNumericCharsFromString]
(
@List NVARCHAR(4000)
)
RETURNS TABLE 
AS RETURN

    WITH GetNums AS (
       SELECT TOP(ISNULL(DATALENGTH(@List), 0))
        n = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
        FROM
          (VALUES (0),(0),(0),(0)) d (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) e (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) f (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) g (n)
            )

    SELECT StrOut = ''+
        (SELECT Chr
         FROM GetNums
            CROSS APPLY (SELECT SUBSTRING(@List , n,1)) X(Chr)
         WHERE Chr LIKE '%[^0-9]%' 
         ORDER BY N
         FOR XML PATH (''),TYPE).value('.','NVARCHAR(MAX)')


   /*How to Use
   SELECT StrOut FROM dbo.udf_RemoveNumericCharsFromString ('vv45--9gut')
   Result: vv--gut
   */

Wiem, że ten wątek jest stary, ale najlepiej jest skorzystać z funkcji wartościowanej w tabeli. Problem z rozwiązaniem polega na tym, że zwracasz tylko liczby, ten kod:), TYPE) .value ('.', 'NVARCHAR (MAX)') nie jest potrzebny i spowolni działanie funkcji o ~ 50%
Alan Burstein

1

Oto kolejne rekurencyjne rozwiązanie CTE, oparte na odpowiedzi @Gerhard Weiss tutaj . Powinieneś móc skopiować i wkleić cały blok kodu do SSMS i tam bawić się nim. Wyniki zawierają kilka dodatkowych kolumn, które pomagają nam zrozumieć, co się dzieje. Zajęło mi trochę czasu, zanim zrozumiałem wszystko, co dzieje się zarówno z PATINDEX (RegEx), jak i rekurencyjnym CTE.

DECLARE @DefineBadCharPattern varchar(30)
SET @DefineBadCharPattern = '%[^A-z]%'  --Means anything NOT between A and z characters (according to ascii char value) is "bad"
SET @DefineBadCharPattern = '%[^a-z0-9]%'  --Means anything NOT between a and z characters or numbers 0 through 9 (according to ascii char value) are "bad"
SET @DefineBadCharPattern = '%[^ -~]%'  --Means anything NOT between space and ~ characters (all non-printable characters) is "bad"
--Change @ReplaceBadCharWith to '' to strip "bad" characters from string
--Change to some character if you want to 'see' what's being replaced. NOTE: It must be allowed accoring to @DefineBadCharPattern above
DECLARE @ReplaceBadCharWith varchar(1) = '#'  --Change this to whatever you want to replace non-printable chars with 
IF patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, @ReplaceBadCharWith) > 0
    BEGIN
        RAISERROR('@ReplaceBadCharWith value (%s) must be a character allowed by PATINDEX pattern of %s',16,1,@ReplaceBadCharWith, @DefineBadCharPattern)
        RETURN
    END
--A table of values to play with:
DECLARE @temp TABLE (OriginalString varchar(100))
INSERT @temp SELECT ' 1hello' + char(13) + char(10) + 'there' + char(30) + char(9) + char(13) + char(10)
INSERT @temp SELECT '2hello' + char(30) + 'there' + char(30)
INSERT @temp SELECT ' 3hello there'
INSERT @temp SELECT ' tab' + char(9) + ' character'
INSERT @temp SELECT 'good bye'

--Let the magic begin:
;WITH recurse AS (
    select
    OriginalString,
    OriginalString as CleanString,
    patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString) as [Position],
    substring(OriginalString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString),1) as [InvalidCharacter],
    ascii(substring(OriginalString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString),1)) as [ASCIICode]
    from @temp
   UNION ALL
    select
    OriginalString,
    CONVERT(varchar(100),REPLACE(CleanString,InvalidCharacter,@ReplaceBadCharWith)),
    patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) as [Position],
    substring(CleanString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString),1),
    ascii(substring(CleanString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString),1))
    from recurse
    where patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) > 0
)
SELECT * FROM recurse
--optionally comment out this last WHERE clause to see more of what the recursion is doing:
WHERE patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) = 0

0

Używając tabeli liczb wygenerowanych przez CTE do zbadania każdego znaku, a następnie FOR XML, aby połączyć z ciągiem zachowanych wartości, możesz ...

CREATE FUNCTION [dbo].[PatRemove](
    @pattern varchar(50),
    @expression varchar(8000) 
    )
RETURNS varchar(8000)
AS
BEGIN
    WITH 
        d(d) AS (SELECT d FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) digits(d)),
        nums(n) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM d d1, d d2, d d3, d d4),
        chars(c) AS (SELECT SUBSTRING(@expression, n, 1) FROM nums WHERE n <= LEN(@expression))
    SELECT 
        @expression = (SELECT c AS [text()] FROM chars WHERE c NOT LIKE @pattern FOR XML PATH(''));

    RETURN @expression;
END

0
DECLARE @vchVAlue NVARCHAR(255) = 'SWP, Lettering Position 1: 4 Ω, 2: 8 Ω, 3: 16 Ω, 4:  , 5:  , 6:  , Voltage Selector, Solder, 6, Step switch, : w/o fuseholder '


WHILE PATINDEX('%?%' , CAST(@vchVAlue AS VARCHAR(255))) > 0
  BEGIN
    SELECT @vchVAlue = STUFF(@vchVAlue,PATINDEX('%?%' , CAST(@vchVAlue AS VARCHAR(255))),1,' ')
  END 

SELECT @vchVAlue

0

ten sposób nie zadziałał dla mnie, ponieważ starałem się zachować arabskie litery, próbowałem zastąpić wyrażenie regularne, ale też nie zadziałało. napisałem inną metodę pracy na poziomie ASCII, ponieważ była to moja jedyna opcja i zadziałała.

 Create function [dbo].[RemoveNonAlphaCharacters] (@s varchar(4000)) returns varchar(4000)
   with schemabinding
begin
   if @s is null
      return null
   declare @s2 varchar(4000)
   set @s2 = ''
   declare @l int
   set @l = len(@s)
   declare @p int
   set @p = 1
   while @p <= @l begin
      declare @c int
      set @c = ascii(substring(@s, @p, 1))
      if @c between 48 and 57 or @c between 65 and 90 or @c between 97 and 122 or @c between 165 and 253 or @c between 32 and 33
         set @s2 = @s2 + char(@c)
      set @p = @p + 1
      end
   if len(@s2) = 0
      return null
   return @s2
   end

UDAĆ SIĘ


-1

Chociaż post jest trochę stary, chciałbym powiedzieć, co następuje. Problem, który miałem z powyższym rozwiązaniem polega na tym, że nie odfiltrowuje ono znaków takich jak ç, ë, ï itp. Zaadaptowałem funkcję w następujący sposób (użyłem tylko ciągu 80 varchar do oszczędzania pamięci):

create FUNCTION dbo.udf_Cleanchars (@InputString varchar(80)) 
RETURNS varchar(80) 
AS 

BEGIN 
declare @return varchar(80) , @length int , @counter int , @cur_char char(1) 
SET @return = '' 
SET @length = 0 
SET @counter = 1 
SET @length = LEN(@InputString) 
IF @length > 0 
BEGIN WHILE @counter <= @length 

BEGIN SET @cur_char = SUBSTRING(@InputString, @counter, 1) IF ((ascii(@cur_char) in (32,44,46)) or (ascii(@cur_char) between 48 and 57) or (ascii(@cur_char) between 65 and 90) or (ascii(@cur_char) between 97 and 122))
BEGIN SET @return = @return + @cur_char END 
SET @counter = @counter + 1 
END END 

RETURN @return END

Dziękuję za to, Eric. Jak mówisz, post oznaczony jako odpowiedź jest bardzo dobry, ale nie usuwa głupich „numerycznych” znaków, takich jak ½.
troy

-3

Właśnie znalazłem to wbudowane w Oracle 10g, jeśli tego właśnie używasz. Musiałem usunąć wszystkie znaki specjalne, aby porównać numery telefonów.

regexp_replace(c.phone, '[^0-9]', '')

5
„SQL Server” odnosi się konkretnie do produktu firmy Microsoft.
nikt
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.