Wie entferne ich alle nicht alphabetischen Zeichen von der Zeichenfolge in SQL Server?

172

Wie können Sie alle nicht alphabetischen Zeichen aus einer Zeichenfolge entfernen?

Was ist mit nicht alphanumerischen?

Muss dies eine benutzerdefinierte Funktion sein oder gibt es auch allgemeinere Lösungen?

Sogar Mien
quelle

Antworten:

362

Versuchen Sie diese Funktion:

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

Nennen Sie es so:

Select dbo.RemoveNonAlphaCharacters('abc1234def5678ghi90jkl')

Sobald Sie den Code verstanden haben, sollten Sie feststellen, dass es relativ einfach ist, ihn zu ändern, um auch andere Zeichen zu entfernen. Sie können diese sogar dynamisch genug gestalten, um Ihr Suchmuster weiterzugeben.

Ich hoffe es hilft.

George Mastros
quelle
9
Dieser Code entfernt Nicht-Alpha-Zeichen (daher werden auch Zahlen entfernt). Wenn Sie die Zahlen belassen möchten (nicht alphanumerische Zeichen entfernen), ... ersetzen Sie ^ az durch ^ az ^ 0-9. Diese Suchzeichenfolge wird im Code an zwei verschiedenen Stellen angezeigt. Stellen Sie sicher, dass Sie beide ersetzen.
George Mastros
26
Aus Jeffs Kommentar: Ich denke, wenn Sie alle Nicht-Buchstaben und Nicht-Zahlen entfernen möchten, möchten Sie '^ a-z0-9' (gegenüber '^ az ^ 0-9', was ^ in der Zeichenfolge belassen würde). .
Sogar Mien
1
+1 George. Dies ist einer der Orte, an denen "Set-basierter" Code und die Verwendung von Inline-Skalarfunktionen große Schwierigkeiten haben, Zeile für Zeile zu schlagen. Schön gemacht. Ich verwende seit einigen Jahren auch Ihre Funktion "Initial Caps", die dieselbe Grundform hat.
Jeff Moden
6
@Lynchie Ändern Sie '% [^ az]%' in '% [^ az]%'. Fügen Sie einfach ein Leerzeichen nach dem z ein.
George Mastros
8
Der Variablenname KeepValues ​​ist eigentlich das Gegenteil von dem, was er tun soll. KeepValues ​​listet die Zeichen auf, die ausgeschlossen werden müssen ..
nee21
167

Parametrisierte Version von G Mastros ' großartiger Antwort :

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

Nur alphabetisch:

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

Nur numerisch:

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

Nur alphanumerisch:

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

Nicht alphanumerisch:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', 'a-z0-9')
Sogar Mien
quelle
3
Ich bevorzuge diese Version und habe meine Anpassung der Antwort von G Mastros erstellt, bevor ich nach unten scrolle, um sie abzustimmen!
Earnshavian
Das Regex-Muster scheint nicht mit allen Leerzeichen zu funktionieren. Wenn ich alle Sonderzeichen außer alphanumerischen Zeichen und Leerzeichen entfernen möchte, würde ich erwarten, dass SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9\s')noch Leerzeichen entfernt werden. Ich habe auch versucht zu verwenden, [[:blank:]]aber das bricht die Funktion und nichts wird aus der Zeichenfolge entfernt. Das nächste, was ich bekommen habe, ist die Verwendung von: SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9 ')(hartes Codieren eines Leerzeichens im Regex-Muster). Zeilenumbrüche werden dadurch jedoch nicht entfernt.
Billy McKee
2
@BillyMcKee Fügen Sie das Leerzeichen am Anfang hinzu, anstatt es am Ende des regulären Ausdrucks hinzuzufügen. SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^ a-z0-9')
Mike
8

Ob Sie es glauben oder nicht, in meinem System ist diese hässliche Funktion besser als die elegante von 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
J Brune
quelle
Was ist mit gebräuchlichen Kommas, Punkten, Leerzeichen usw.?
Sojim
Wie viel Unterschied, wenn Sie hier keine ASCIIGanzzahl verwenden und die Ausgabe von direkt SUBSTRINGmit einigen Zeichen vergleichen, z. B.: SET @ch=SUBSTRING(@s, @p, 1)undIF @ch BETWEEN '0' AND '9' OR @ch BETWEEN 'a' AND 'z' OR @ch BETWEEN 'A' AND 'Z' ...
S.Serpooshan
Fügen Sie WITH SCHEMABINDING zu seiner Funktion hinzu, wie es Ihre Funktion getan hat. Sie verwenden VARCHAR, seine Funktion ist NVARCHAR. Wenn die Parameter, die Sie an seine Funktion übergeben, VARCHAR sind, sollten Sie VARCHAR anstelle von NVARCHAR in seiner Funktion verwenden. Andernfalls muss Ihr System die Zeichenfolgenwerte von VARCHAR in NVARCHAR umwandeln, bevor es die teurere Funktion ausführen kann. Selbst mit diesen Änderungen ist Ihre Funktion möglicherweise noch schneller, aber dies sind einige Beispiele, bei denen ich sehen kann, wo seine Funktion in Ihrer Situation für Sie möglicherweise langsamer ausgeführt wird.
EricI
1
Seine Funktion verwendet auch NVARCHAR (MAX) und Ihre Funktion verwendet VARCHAR (256). Wenn Sie nur 256 benötigen, ändern Sie seine Funktion, um auch VARCHAR (256) zu verwenden, und seine Funktion arbeitet schneller für Sie.
EricI
5

Ich wusste, dass SQL schlecht in der Manipulation von Zeichenfolgen ist, aber ich dachte nicht, dass es so schwierig sein würde. Hier ist eine einfache Funktion, um alle Zahlen aus einer Zeichenfolge zu entfernen. Es gibt bessere Möglichkeiten, dies zu tun, aber dies ist ein Anfang.

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

Ausgabe

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

Runde 2 - Datengesteuerte Blacklist

-- ============================================
-- 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

Ausgabe

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

Meine Herausforderung an die Leser: Können Sie dies effizienter gestalten? Was ist mit Rekursion?

Rob Garrison
quelle
Mit sommarskog.se/arrays-in-sql-2005.html#tblnum könnten Sie wahrscheinlich eine bessere dbo.StripBlacklistCharacters () ohne Schleife schreiben , aber ich bin heute zu faul, um es zu versuchen ich selbst ....
KM.
4

Wenn Sie wie ich sind und nicht nur Funktionen zu Ihren Produktionsdaten hinzufügen können, aber dennoch diese Art der Filterung durchführen möchten, finden Sie hier eine reine SQL-Lösung, die eine PIVOT-Tabelle verwendet, um die gefilterten Teile wieder zusammenzusetzen.

NB Ich habe die Tabelle mit bis zu 40 Zeichen fest codiert. Sie müssen mehr hinzufügen, wenn Sie längere Zeichenfolgen zum Filtern haben.

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
Kyle Hale
quelle
Diese Lösung war für mich 2,3-mal schneller als die Verwendung einer Funktion für 235K-Zeilen. Ich musste auch 2x ersetzen und verwendete insgesamt vier CTEs. Arbeitete wie ein Champion.
JJS
4

Nachdem ich mir alle gegebenen Lösungen angesehen hatte, dachte ich, dass es eine reine SQL-Methode geben muss, die keine Funktion oder eine CTE / XML-Abfrage erfordert und keine schwierig zu verwaltenden verschachtelten REPLACE-Anweisungen beinhaltet. Hier ist meine Lösung:

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

Der Vorteil dieser Vorgehensweise besteht darin, dass die gültigen Zeichen in der einen Zeichenfolge in der Unterabfrage enthalten sind, sodass die Konfiguration für einen anderen Zeichensatz einfach ist.

Der Nachteil ist, dass Sie für jedes Zeichen bis zur Größe Ihrer Spalte eine SQL-Zeile hinzufügen müssen. Um diese Aufgabe zu vereinfachen, habe ich nur das folgende Powershell-Skript verwendet, dieses Beispiel für ein VARCHAR (64):

1..64 | % {
  "    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, {0}, 1) + '%' THEN '' ELSE SUBSTRING(x, {0}, 1) END" -f $_
} | clip.exe
Dave Sexton
quelle
3
Im allgemeinen Fall umständlich, aber einfach und nützlich für eine einmalige Abfrage mit einer schmalen Spalte.
Eric J.
3

Hier ist eine andere Möglichkeit, nicht alphabetische Zeichen mit einem zu entfernen iTVF. Zunächst benötigen Sie einen musterbasierten String-Splitter. Hier ist einer aus dem Artikel von Dwain Camp :

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

Nachdem Sie einen musterbasierten Splitter haben, müssen Sie die Zeichenfolgen teilen, die dem Muster entsprechen:

[a-z]

und verketten Sie sie dann wieder, um das gewünschte Ergebnis zu erzielen:

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)

STICHPROBE

Ergebnis:

| 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 |
Felix Pamittan
quelle
Gibt es einen Vorteil gegenüber anderen Antworten?
Serpooshan
2

Diese Lösung, die von Mr. Allens Lösung inspiriert ist, erfordert eine NumbersTabelle mit ganzen Zahlen (die Sie zur Hand haben sollten, wenn Sie ernsthafte Abfragevorgänge mit guter Leistung ausführen möchten). Es ist kein CTE erforderlich. Sie können den NOT IN (...)Ausdruck ändern , um bestimmte Zeichen auszuschließen, oder ihn in einen IN (...)ODER- LIKEAusdruck ändern , um nur bestimmte Zeichen beizubehalten.

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 ...
Jay Bienvenu
quelle
Interessante Lösung für ein nicht verwandtes Problem.
TaterJuice
2

Hier ist eine Lösung, bei der keine Funktion erstellt oder alle zu ersetzenden Zeicheninstanzen aufgelistet werden müssen. Es verwendet eine rekursive WITH-Anweisung in Kombination mit einem PATINDEX, um unerwünschte Zeichen zu finden. Es ersetzt alle unerwünschten Zeichen in einer Spalte - bis zu 100 eindeutige fehlerhafte Zeichen in einer bestimmten Zeichenfolge. (EG "ABC123DEF234" würde 4 fehlerhafte Zeichen 1, 2, 3 und 4 enthalten.) Das Limit von 100 ist die maximale Anzahl von Rekursionen, die in einer WITH-Anweisung zulässig sind. Dies legt jedoch kein Limit für die Anzahl der zu verarbeitenden Zeilen fest ist nur durch den verfügbaren Speicher begrenzt.
Wenn Sie keine UNTERSCHIEDLICHEN Ergebnisse wünschen, können Sie die beiden Optionen aus dem Code entfernen.

-- 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;
Graeme Job
quelle
1

Ich habe dies an beiden Stellen platziert, an denen PatIndex aufgerufen wird.

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

für die benutzerdefinierte Funktion über RemoveNonAlphaCharacters und benenne sie in RemoveNonAlphaNumericCharacters um

user381460
quelle
1

- Erstellen Sie zuerst eine Funktion

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

Rufen Sie diese Funktion nun wie auf

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

Sein Ergebnis gefällt

1223
Abhishek Jaiswal
quelle
1

Aus Sicht der Leistung würde ich die Inline-Funktion verwenden:

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
   */
hkravitz
quelle
Ich weiß, dass dieser Thread alt ist, aber eine Inline-Tabellenwertfunktion ist der richtige Weg. Das Problem mit Ihrer Lösung ist, dass, da Sie nur Zahlen zurückgeben, dieser Code :), TYPE) .value ('.', 'NVARCHAR (MAX)') nicht benötigt wird und die Funktion um ~ 50% verlangsamt
Alan Burstein
1

Hier ist eine weitere rekursive CTE-Lösung, die auf der Antwort von @Gerhard Weiss hier basiert . Sie sollten in der Lage sein, den gesamten Codeblock zu kopieren und in SSMS einzufügen und dort damit zu spielen. Die Ergebnisse enthalten einige zusätzliche Spalten, damit wir besser verstehen, was los ist. Es dauerte eine Weile, bis ich alles verstand, was sowohl mit PATINDEX (RegEx) als auch mit dem rekursiven CTE vor sich geht.

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
Baodad
quelle
0

Verwenden Sie eine von CTE generierte Zahlentabelle, um jedes Zeichen zu untersuchen, und dann FOR XML, um sich auf eine Zeichenfolge von beibehaltenen Werten zu konzentrieren, die Sie ...

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
Dennis Allen
quelle
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
Mohannad Qattash
quelle
0

Dieser Weg funktionierte nicht für mich, da ich versuchte, die arabischen Buchstaben beizubehalten. Ich versuchte, den regulären Ausdruck zu ersetzen, aber es funktionierte auch nicht. Ich habe eine andere Methode geschrieben, um auf ASCII-Ebene zu arbeiten, da dies meine einzige Wahl war und funktioniert hat.

 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

GEHEN

AGOMAA
quelle
-1

Obwohl der Beitrag etwas alt ist, möchte ich Folgendes sagen. Das Problem, das ich mit der obigen Lösung hatte, ist, dass Zeichen wie ç, ë, ï usw. nicht herausgefiltert werden. Ich habe eine Funktion wie folgt angepasst (ich habe nur eine 80-varchar-Zeichenfolge verwendet, um Speicher zu sparen):

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
Eric
quelle
Danke dafür, Eric. Wie Sie sagen, ist die mit einem Posting markierte Antwort sehr gut, aber sie entfernt keine albernen "numerischen" Zeichen wie ½.
Troy
-3

Ich habe gerade festgestellt, dass dies in Oracle 10g integriert ist, wenn Sie es verwenden. Ich musste alle Sonderzeichen entfernen, um die Telefonnummer vergleichen zu können.

regexp_replace(c.phone, '[^0-9]', '')
Lu_Bu
quelle
5
"SQL Server" bezieht sich speziell auf das Produkt von Microsoft.
niemand